Created
April 27, 2026 15:42
-
-
Save mattleibow/74ffb511e7910257609a07eebf50168e to your computer and use it in GitHub Desktop.
SkiaSharp Release Notes: v3.119.2 → 4.0 main (37 findings with upstream benefits)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| <title>Release Notes — v3.119.2..origin/main (2026-04-27)</title> | |
| <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"> | |
| <style> | |
| :root{--bg:#f6f8fa;--border:#d8dee4;--row-hover:#f0f4f8;--muted:#656d76;} | |
| body{background:var(--bg);color:#1f2328;font-size:14px;} | |
| .shell{max-width:1100px;} | |
| .summary-bar{background:#fff;border:1px solid var(--border);border-radius:8px;padding:12px 16px;margin-bottom:12px;} | |
| .summary-num{font-size:1.5rem;font-weight:700;line-height:1;} | |
| .summary-label{font-size:.65rem;color:var(--muted);text-transform:uppercase;letter-spacing:.5px;} | |
| .pill{display:inline-block;font-size:.68rem;font-weight:600;padding:1px 7px;border-radius:10px;cursor:pointer;user-select:none;white-space:nowrap;vertical-align:middle;} | |
| .pill:hover{opacity:.8;}.pill.dim{opacity:.3;} | |
| .i-breaking{background:#fee2e2;color:#991b1b;}.i-major{background:#dbeafe;color:#1e40af;} | |
| .i-minor{background:#fef3c7;color:#92400e;}.i-patch{background:#f3f4f6;color:#6b7280;} | |
| .ct-added{background:#dcfce7;color:#166534;}.ct-changed{background:#fef3c7;color:#92400e;} | |
| .ct-fixed{background:#dbeafe;color:#1e40af;}.ct-deprecated{background:#fed7aa;color:#9a3412;} | |
| .ct-removed{background:#fee2e2;color:#991b1b;}.ct-dependency{background:#ede9fe;color:#5b21b6;} | |
| .ct-platform{background:#ccfbf1;color:#115e59;}.ct-upstream{background:#fce7f3;color:#9d174d;} | |
| .l-pill{background:#e0e7ff;color:#3730a3;} | |
| .row-item{border-bottom:1px solid #eaeef2;padding:7px 12px;cursor:pointer;} | |
| .row-item:hover{background:var(--row-hover);} | |
| .row-item.expanded{background:#fafbfc;} | |
| .row-name{font-weight:600;font-size:.88rem;}.row-chevron{color:#bbb;font-size:.7rem;margin-right:6px;transition:transform .15s;} | |
| .row-item.expanded .row-chevron{transform:rotate(90deg);color:#666;} | |
| .row-desc{color:var(--muted);font-size:.78rem;margin-top:2px;} | |
| .row-pills{margin-top:3px;} | |
| .row-detail{display:none;padding:8px 0 4px 0;font-size:.82rem;} | |
| .row-item.expanded .row-detail{display:block;} | |
| .detail-grid{display:grid;grid-template-columns:110px 1fr;gap:2px 12px;} | |
| .detail-label{color:var(--muted);font-weight:600;font-size:.72rem;text-transform:uppercase;} | |
| .detail-value{font-size:.82rem;} | |
| .detail-value code{font-size:.78rem;background:#f0f4f8;padding:1px 4px;border-radius:3px;} | |
| .filter-bar{display:flex;flex-direction:column;gap:4px;padding:10px 12px;background:#fff;border:1px solid var(--border);border-radius:8px;margin-bottom:8px;} | |
| .filter-row{display:flex;gap:4px;align-items:center;flex-wrap:wrap;} | |
| .filter-label{font-size:.7rem;color:var(--muted);font-weight:600;width:80px;flex-shrink:0;text-align:right;padding-right:6px;} | |
| .search-box{font-size:.8rem;padding:3px 10px;border:1px solid var(--border);border-radius:6px;} | |
| .ctrl-select{font-size:.78rem;padding:2px 8px;border:1px solid var(--border);border-radius:6px;background:#fff;color:#1f2328;} | |
| .reset-btn{font-size:.7rem;color:#3730a3;cursor:pointer;border:none;background:none;padding:2px 6px;} | |
| .reset-btn:hover{text-decoration:underline;} | |
| .count-badge{font-size:.6rem;background:#e5e7eb;color:#374151;padding:0 5px;border-radius:8px;margin-left:2px;vertical-align:middle;} | |
| .tab-btn{font-size:.85rem;font-weight:600;padding:6px 16px;border:none;background:none;color:var(--muted);cursor:pointer;border-bottom:2px solid transparent;} | |
| .tab-btn.active{color:#1e40af;border-bottom-color:#1e40af;}.tab-btn:hover{color:#1e40af;} | |
| .tab-pane{display:none;}.tab-pane.active{display:block;} | |
| .group-header{font-weight:700;font-size:.82rem;padding:8px 12px 4px;color:#1e40af;border-bottom:2px solid #dbeafe;margin-top:12px;background:#f8faff;} | |
| .meta-footer{font-size:.72rem;color:var(--muted);margin-top:16px;padding:8px 12px;} | |
| .meta-footer a{color:#3730a3;} | |
| .dir-btn{font-size:.8rem;width:26px;height:26px;border:1px solid var(--border);border-radius:4px;background:#fff;cursor:pointer;color:#1e40af;font-weight:700;padding:0;line-height:26px;text-align:center;} | |
| .dir-btn:hover{background:#f0f4f8;} | |
| .md-content{background:#fff;border:1px solid var(--border);border-radius:8px;padding:20px 24px;font-size:.9rem;line-height:1.6;} | |
| .md-content h2{font-size:1.1rem;margin-top:20px;padding-bottom:4px;border-bottom:1px solid var(--border);} | |
| .md-content h3{font-size:1rem;margin-top:16px;} | |
| .md-content code{font-size:.85em;background:#f0f4f8;padding:1px 4px;border-radius:3px;} | |
| .md-content pre{background:#f6f8fa;padding:12px;border-radius:6px;overflow-x:auto;font-size:.82rem;} | |
| .md-content ul{padding-left:20px;} | |
| .md-content li{margin-bottom:4px;} | |
| .pr-link{font-size:.72rem;color:#3730a3;text-decoration:none;} | |
| .pr-link:hover{text-decoration:underline;} | |
| </style> | |
| </head> | |
| <body> | |
| <script>const DATA = {"meta": {"date": "2026-04-27", "schemaVersion": "1.0", "repo": "mono/SkiaSharp", "refFrom": "v3.119.2", "refTo": "origin/main", "shaFrom": "2888c737ad016d584c74525e2d35db5097ea8576", "shaTo": "3dd450cd11da39456c4870df3c3a5e7a8d1552f4", "dateFrom": "2026-02-06", "dateTo": "2026-04-27", "commitCount": 175, "prCount": 120, "skiaMilestoneBumped": true, "skiaMilestoneFrom": 119, "skiaMilestoneTo": 133}, "summary": {"totalFindings": 37, "byChangeType": {"added": 9, "changed": 3, "fixed": 9, "deprecated": 0, "removed": 0, "dependency": 7, "platform": 4, "upstream": 5}, "byImportance": {"breaking": 2, "major": 6, "minor": 19, "patch": 10}, "byLabel": {"security": 5, "skia-upstream": 5, "breaking-change": 2, "new-platform": 3, "performance": 2, "community": 4}}, "findings": [{"name": "Major version upgrade from 3.x to 4.x", "changeType": "changed", "importance": "breaking", "description": "SkiaSharp package version is upgraded from 3.x to 4.x, signaling breaking API changes. Consumers must update their package references and may need code changes for removed or changed APIs.", "labels": ["breaking-change"], "pr": 3640, "prUrl": "https://github.com/mono/SkiaSharp/pull/3640", "slideBullet": "🚀 **SkiaSharp 4.0** — Major version upgrade with new font, palette, and platform APIs", "migrationGuide": "Update all SkiaSharp NuGet package references from `3.x` to `4.x`.\n\n### Before\n```xml\n<PackageReference Include=\"SkiaSharp\" Version=\"3.119.2\" />\n```\n\n### After\n```xml\n<PackageReference Include=\"SkiaSharp\" Version=\"4.0.0\" />\n```\n\nReview the breaking changes below for any required code changes."}, {"name": "Default typeface resolution moved to managed layer", "changeType": "changed", "importance": "breaking", "description": "SKTypeface.Default and SKTypeface.CreateDefault() now resolve the platform default typeface via SKFontManager.Default.MatchFamily instead of the native sk_typeface_ref_default(). SKPaint() constructor uses a managed-layer default font. On Android, this fixes incorrect fallback when the NDK font manager doesn't find 'sans-serif'. Behavior may differ subtly on platforms where the native and managed font manager defaults diverge.", "labels": ["breaking-change"], "pr": 3730, "prUrl": "https://github.com/mono/SkiaSharp/pull/3730", "issue": 3693, "issueUrl": "https://github.com/mono/SkiaSharp/issues/3693", "affectedTypes": ["SKTypeface", "SKPaint", "SKFont"], "affectedMethods": ["SKTypeface.Default", "SKTypeface.CreateDefault", "SKTypeface.FromFamilyName", "SKPaint.SKPaint", "SKPaint.Reset"], "migrationGuide": "SKTypeface.FromFamilyName now delegates to SKFontManager.Default.MatchFamily and returns `SKTypeface.Default` instead of `null` when no match is found. If your code checks for `null` returns from FromFamilyName, it will now receive the default typeface instead.\n\n### Before\n```csharp\nvar tf = SKTypeface.FromFamilyName(\"NonExistent\");\nif (tf == null)\n tf = SKTypeface.Default; // manual fallback\n```\n\n### After\n```csharp\nvar tf = SKTypeface.FromFamilyName(\"NonExistent\");\n// Always returns a valid typeface (Default if not found)\n```\n\nNew properties: `SKTypeface.Empty` and `SKTypeface.IsEmpty` are available for checking empty typefaces."}, {"name": "Variable font support in HarfBuzzSharp", "changeType": "added", "importance": "major", "description": "Full OpenType variable font support added to HarfBuzzSharp. Face exposes variation axis enumeration (HasVariationData, VariationAxisCount, VariationAxisInfos), named instance discovery (NamedInstanceCount, GetNamedInstanceDesignCoords), and axis lookup (TryFindVariationAxis). Font gains SetVariations, SetVariationCoordsDesign, SetVariationCoordsNormalized, and SetVariationNamedInstance for applying variation settings.", "labels": ["community"], "pr": 3703, "prUrl": "https://github.com/mono/SkiaSharp/pull/3703", "affectedTypes": ["HarfBuzzSharp.Face", "HarfBuzzSharp.Font"], "affectedMethods": ["Face.HasVariationData", "Face.VariationAxisCount", "Face.VariationAxisInfos", "Face.TryFindVariationAxis", "Face.NamedInstanceCount", "Font.SetVariations", "Font.SetVariationCoordsDesign"], "slideBullet": "🎨 **Variable Font Support** — Full OpenType variable font axis control with named instance discovery"}, {"name": "Color font palette support", "changeType": "added", "importance": "major", "description": "Color font palette APIs added to HarfBuzzSharp and SkiaSharp. Face exposes palette enumeration (HasPalettes, PaletteCount, GetPaletteColors, GetPaletteFlags), color layer and glyph detection (HasColorLayers, HasColorPng, HasColorSvg). New SKFontArguments ref struct supports PaletteIndex and PaletteOverrides for selecting and customizing color palettes. New SKFontPaletteOverride struct for per-color overrides.", "labels": [], "pr": 3742, "prUrl": "https://github.com/mono/SkiaSharp/pull/3742", "affectedTypes": ["HarfBuzzSharp.Face", "SKFontArguments", "SKFontPaletteOverride"], "affectedMethods": ["Face.HasPalettes", "Face.PaletteCount", "Face.GetPaletteColors", "Face.HasColorLayers", "Face.HasColorPng"], "slideBullet": "🎨 **Color Font Palettes** — Query and customize color palettes in emoji and color fonts"}, {"name": "GTK 4 views package (SkiaSharp.Views.Gtk4)", "changeType": "added", "importance": "major", "description": "New SkiaSharp.Views.Gtk4 package using GirCore.Gtk-4.0 bindings. Provides SKDrawingArea for hardware-accelerated SkiaSharp rendering in GTK 4 applications on Linux.", "labels": ["new-platform"], "pr": 3527, "prUrl": "https://github.com/mono/SkiaSharp/pull/3527", "affectedTypes": ["SKDrawingArea"], "platforms": ["Linux"], "slideBullet": "🌐 **GTK 4 Support** — Native SkiaSharp views for modern GTK 4 Linux applications"}, {"name": "Linux Bionic native assets support", "changeType": "added", "importance": "major", "description": "Add Android Bionic Library (non-Android OS) build support for SkiaSharp native libraries. Enables running SkiaSharp on Linux distributions using the Bionic C library instead of glibc, with fontconfig disabled.", "labels": ["new-platform", "community"], "pr": 3217, "prUrl": "https://github.com/mono/SkiaSharp/pull/3217", "platforms": ["Linux-Bionic"], "slideBullet": "🌐 **Linux Bionic Support** — Run SkiaSharp on Bionic-based Linux distributions"}, {"name": "SKSamplingOptions support on SKSurface.Draw and SKCanvas.DrawSurface", "changeType": "added", "importance": "minor", "description": "New overloads for SKSurface.Draw(canvas, x, y, sampling, paint) and SKCanvas.DrawSurface(surface, x, y, sampling, paint) that accept SKSamplingOptions for controlling texture filtering when drawing surfaces.", "labels": [], "pr": 3491, "prUrl": "https://github.com/mono/SkiaSharp/pull/3491", "affectedTypes": ["SKSurface", "SKCanvas"], "affectedMethods": ["SKSurface.Draw", "SKCanvas.DrawSurface"], "slideBullet": "🔧 **Surface Sampling Options** — Control texture filtering when drawing surfaces with SKSamplingOptions"}, {"name": "SKColorSpace.CreateCicp for ITU-T H.273 color spaces", "changeType": "added", "importance": "minor", "description": "New static method SKColorSpace.CreateCicp(colorPrimaries, transferCharacteristics) creates an SKColorSpace from CICP (Coding-Independent Code Points) specified by ITU-T H.273. Useful for video content and HDR workflows.", "labels": [], "affectedTypes": ["SKColorSpace"], "affectedMethods": ["SKColorSpace.CreateCicp"], "skiaMilestone": 133, "skiaFeature": "SkColorSpace::MakeCICP", "pr": 3660, "prUrl": "https://github.com/mono/SkiaSharp/pull/3660"}, {"name": "SKFourByteTag struct for OpenType tag handling", "changeType": "added", "importance": "minor", "description": "New SKFourByteTag struct with constructors from uint and char quadruplet, Parse method, implicit conversions, and equality operators. Provides type-safe OpenType font tag handling.", "labels": [], "affectedTypes": ["SKFourByteTag"], "pr": 3703, "prUrl": "https://github.com/mono/SkiaSharp/pull/3703"}, {"name": "SKFontArguments ref struct with palette support", "changeType": "added", "importance": "minor", "description": "New SKFontArguments ref struct with VariationDesignPosition, CollectionIndex, PaletteIndex, and PaletteOverrides properties. Enables passing structured font arguments including palette customization.", "labels": [], "pr": 3742, "prUrl": "https://github.com/mono/SkiaSharp/pull/3742", "affectedTypes": ["SKFontArguments", "SKFontPaletteOverride"]}, {"name": "New color types: Bgra10101010XR and RgbF16F16F16x", "changeType": "added", "importance": "minor", "description": "Two new SKColorType enum values added: Bgra10101010XR (10-bit per channel with XR extension) and RgbF16F16F16x (half-float RGB with padding). These support wider color gamut and HDR rendering scenarios.", "labels": [], "affectedTypes": ["SKColorType"], "pr": 3560, "prUrl": "https://github.com/mono/SkiaSharp/pull/3560"}, {"name": "Fix SKGLView not rendering after tab switch in MAUI TabBar", "changeType": "fixed", "importance": "minor", "description": "Fixed SKGLView on Android not being rendered when navigating back to a tab in a MAUI TabBar. The GL surface was not being recreated after the view was detached and reattached.", "labels": ["community"], "pr": 3076, "prUrl": "https://github.com/mono/SkiaSharp/pull/3076", "affectedTypes": ["SKGLView"], "platforms": ["Android"]}, {"name": "Fix AoT crash when IlcDisableReflection=true", "changeType": "fixed", "importance": "minor", "description": "Fixed crash in SKBlender when running under NativeAOT with reflection disabled. The static constructor now explicitly lists all SKBlendMode enum values instead of using Enum.GetValues, which requires reflection.", "labels": [], "pr": 3485, "prUrl": "https://github.com/mono/SkiaSharp/pull/3485", "affectedTypes": ["SKBlender"]}, {"name": "Fix Cairo assertion crash in GTK4 SKDrawingArea", "changeType": "fixed", "importance": "minor", "description": "Fixed a Cairo assertion crash that occurred in the GTK4 SKDrawingArea widget during rendering.", "labels": [], "pr": 3562, "prUrl": "https://github.com/mono/SkiaSharp/pull/3562", "platforms": ["Linux"]}, {"name": "Delay-load D3D12 DLLs to prevent crashes without DX12", "changeType": "fixed", "importance": "minor", "description": "D3D12, DXGI, and D3DCompiler DLLs are now delay-loaded on Windows, preventing crashes on systems that don't have DirectX 12 installed. The native library now gracefully degrades instead of failing to load.", "labels": ["security"], "pr": 3633, "prUrl": "https://github.com/mono/SkiaSharp/pull/3633", "platforms": ["Windows"]}, {"name": "Fix tvOS NativeReference targets for device/simulator split", "changeType": "fixed", "importance": "patch", "description": "Fixed tvOS native builds to split device and simulator frameworks, matching the iOS pattern. Resolves build failures when targeting tvOS simulator.", "labels": [], "pr": 3561, "prUrl": "https://github.com/mono/SkiaSharp/pull/3561", "platforms": ["tvOS"]}, {"name": "Fix Linux ARM64 fontconfig crash", "changeType": "fixed", "importance": "patch", "description": "Added fontconfig runtime library to the cross-compile sysroot, fixing a crash on Linux ARM64 when the font configuration library was missing.", "labels": [], "pr": 3494, "prUrl": "https://github.com/mono/SkiaSharp/pull/3494", "platforms": ["Linux"]}, {"name": "Refactor SKManagedStream to use SKData snapshot", "changeType": "fixed", "importance": "patch", "description": "Replaced the fragile parent/child chain in SKManagedStream with an SKData snapshot approach for duplicate/fork operations. Improves reliability and simplifies the managed stream lifecycle.", "labels": [], "pr": 3589, "prUrl": "https://github.com/mono/SkiaSharp/pull/3589", "affectedTypes": ["SKManagedStream"]}, {"name": "Fix x86 .NET Framework threading test OOM failures", "changeType": "fixed", "importance": "patch", "description": "Fixed out-of-memory failures in threading tests on x86 .NET Framework by reducing memory pressure in the test harness.", "labels": [], "pr": 3608, "prUrl": "https://github.com/mono/SkiaSharp/pull/3608"}, {"name": "Fix LoongArch64 Linux build mapping", "changeType": "fixed", "importance": "patch", "description": "Fixed the architecture mapping for LoongArch64: Skia's GN files use 'loong64' while the build and packaging pipeline uses 'loongarch64'. Added proper mapping function.", "labels": [], "pr": 3648, "prUrl": "https://github.com/mono/SkiaSharp/pull/3648", "platforms": ["Linux"]}, {"name": "Spectre mitigation for Windows native DLLs", "changeType": "platform", "importance": "minor", "description": "Spectre mitigation compiler flags enabled for libSkiaSharp.dll on Windows, hardening against speculative execution side-channel attacks.", "labels": ["security"], "pr": 3496, "prUrl": "https://github.com/mono/SkiaSharp/pull/3496", "platforms": ["Windows"], "slideBullet": "🔒 **Spectre Mitigation** — Native DLLs hardened against speculative execution attacks on Windows and Linux"}, {"name": "Spectre mitigation for Linux native libraries", "changeType": "platform", "importance": "minor", "description": "Spectre mitigation compiler flags enabled for libSkiaSharp.so on Linux, matching the Windows hardening applied in #3496.", "labels": ["security"], "pr": 3502, "prUrl": "https://github.com/mono/SkiaSharp/pull/3502", "platforms": ["Linux"]}, {"name": "Tizen x64 and arm64 native build support", "changeType": "platform", "importance": "minor", "description": "Added x64 and arm64 native build targets for Tizen, expanding platform coverage beyond the previous ARM-only builds.", "labels": ["new-platform"], "pr": 3620, "prUrl": "https://github.com/mono/SkiaSharp/pull/3620", "platforms": ["Tizen"]}, {"name": "Enforce --no-undefined linker flag for Linux and Android", "changeType": "platform", "importance": "patch", "description": "Added -Wl,--no-undefined linker flag for Linux and Android native builds, ensuring all symbols are resolved at link time rather than failing at runtime.", "labels": [], "pr": 3629, "prUrl": "https://github.com/mono/SkiaSharp/pull/3629", "platforms": ["Linux", "Android"]}, {"name": "Bump Skia to milestone 132", "changeType": "dependency", "importance": "major", "description": "Upgraded the Skia graphics engine from milestone 119 to 132. This brings 13 milestones of upstream rendering improvements, codec enhancements, and performance optimizations.", "labels": ["community"], "pr": 3560, "prUrl": "https://github.com/mono/SkiaSharp/pull/3560", "dependencyName": "Skia", "dependencyFrom": "m119", "dependencyTo": "m132"}, {"name": "Bump Skia to milestone 133", "changeType": "dependency", "importance": "major", "description": "Final Skia engine upgrade from m132 to m133, bringing canvas preallocation tuning, CICP color spaces, and GPU time reporting capabilities.", "labels": [], "pr": 3660, "prUrl": "https://github.com/mono/SkiaSharp/pull/3660", "dependencyName": "Skia", "dependencyFrom": "m132", "dependencyTo": "m133"}, {"name": "Update libpng to 1.6.58", "changeType": "dependency", "importance": "patch", "description": "Bumped libpng from 1.6.47 to 1.6.58 (via intermediate 1.6.54 in #3452). Includes security fixes and bug fixes for PNG encoding/decoding.", "labels": ["security"], "pr": 3718, "prUrl": "https://github.com/mono/SkiaSharp/pull/3718", "dependencyName": "libpng", "dependencyFrom": "1.6.47", "dependencyTo": "1.6.58"}, {"name": "Update libexpat to 2.7.5", "changeType": "dependency", "importance": "patch", "description": "Bumped libexpat from 2.6.4 to 2.7.5 (via intermediate 2.7.3 in #3458). Addresses multiple CVE fixes in the XML parser used by SVG and font subsystems.", "labels": ["security"], "pr": 3717, "prUrl": "https://github.com/mono/SkiaSharp/pull/3717", "dependencyName": "libexpat", "dependencyFrom": "2.6.4", "dependencyTo": "2.7.5"}, {"name": "Update libwebp to 1.6.0", "changeType": "dependency", "importance": "patch", "description": "Bumped libwebp to 1.6.0 with encoding/decoding improvements.", "labels": [], "pr": 3478, "prUrl": "https://github.com/mono/SkiaSharp/pull/3478", "dependencyName": "libwebp", "dependencyFrom": "1.4.0", "dependencyTo": "1.6.0"}, {"name": "Update brotli to 1.2.0", "changeType": "dependency", "importance": "patch", "description": "Bumped brotli compression library to 1.2.0.", "labels": [], "pr": 3469, "prUrl": "https://github.com/mono/SkiaSharp/pull/3469", "dependencyName": "brotli", "dependencyFrom": "1.1.0", "dependencyTo": "1.2.0"}, {"name": "Update to .NET 10 SDK", "changeType": "dependency", "importance": "minor", "description": "Updated build tooling to .NET 10 SDK with workload version set pinning and latestFeature rollForward policy.", "labels": [], "pr": 3514, "prUrl": "https://github.com/mono/SkiaSharp/pull/3514", "dependencyName": ".NET SDK", "dependencyFrom": "8.x", "dependencyTo": "10.0.100"}, {"name": "Perlin noise rendering fixed and significantly faster", "changeType": "upstream", "importance": "minor", "description": "Perlin noise shaders (MakeFractalNoise and MakeTurbulence) now properly rotate when transformed. Raster surface performance for noise shaders is significantly improved. Apps using noise-based effects will see both visual correctness improvements and faster rendering. No code changes needed.", "labels": ["skia-upstream", "performance"], "skiaMilestone": 124, "skiaFeature": "Perlin noise rotation fix and raster performance optimization", "slideBullet": "⚡ **Faster Noise Shaders** — Perlin noise renders correctly under rotation and runs significantly faster on CPU", "pr": 3560, "prUrl": "https://github.com/mono/SkiaSharp/pull/3560"}, {"name": "Vulkan device-lost error callback support", "changeType": "upstream", "importance": "minor", "description": "The Vulkan backend now invokes an optional client-provided callback when VK_ERROR_DEVICE_LOST occurs, with additional debugging information from VK_EXT_device_fault if available. Apps using Vulkan can now diagnose GPU crashes more effectively.", "labels": ["skia-upstream"], "skiaMilestone": 123, "skiaFeature": "Vulkan device-lost callback with VK_EXT_device_fault", "pr": 3560, "prUrl": "https://github.com/mono/SkiaSharp/pull/3560"}, {"name": "Exif orientation respected in image decoding", "changeType": "upstream", "importance": "minor", "description": "SkCodec.getImage() now respects Exif orientation metadata, automatically rotating images to their correct orientation. Photos from cameras and phones that embed rotation metadata will display correctly without manual rotation. No code changes needed.", "labels": ["skia-upstream"], "skiaMilestone": 123, "skiaFeature": "SkCodec::getImage Exif orientation", "slideBullet": "🖼️ **Auto Image Orientation** — Photos now automatically respect Exif rotation metadata from cameras", "pr": 3560, "prUrl": "https://github.com/mono/SkiaSharp/pull/3560"}, {"name": "SkSL reserved keyword validation strengthened", "changeType": "upstream", "importance": "minor", "description": "SkSL now rejects GLSL reserved keywords from modern GLSL versions (e.g., dmat3x3, atomic_uint, isampler2D) that were previously silently accepted but caused rendering failures on OpenGL. Shader code using these names will now get clear compile-time errors instead of mysterious runtime failures.", "labels": ["skia-upstream"], "skiaMilestone": 128, "skiaFeature": "SkSL reserved keyword rejection for modern GLSL", "pr": 3560, "prUrl": "https://github.com/mono/SkiaSharp/pull/3560"}, {"name": "Mipmap sharpening enabled by default", "changeType": "upstream", "importance": "minor", "description": "GrContextOptions.fSharpenMipmappedTextures is now enabled by default, producing sharper mipmapped textures. Downscaled images and textures will appear crisper without any code changes.", "labels": ["skia-upstream", "performance"], "skiaMilestone": 131, "skiaFeature": "fSharpenMipmappedTextures default enabled", "slideBullet": "⚡ **Sharper Mipmaps** — Downscaled images render crisper with improved default mipmap sharpening", "pr": 3560, "prUrl": "https://github.com/mono/SkiaSharp/pull/3560"}, {"name": "Split NuGet mega-packages to stay under 500MB feed limit", "changeType": "changed", "importance": "minor", "description": "Native asset NuGet packages were split into smaller per-architecture packages to stay within the 500MB NuGet feed size limit. Package structure changed but consumption remains the same through meta-packages.", "labels": [], "pr": 3584, "prUrl": "https://github.com/mono/SkiaSharp/pull/3584"}], "slides": "## What's New in SkiaSharp 4.0\n\n🚀 **SkiaSharp 4.0** — Major version upgrade powered by Skia m133 engine (14 milestones of improvements)\n\n### 🎨 Rendering & Typography\n- 🎨 **Variable Font Support** — Full OpenType variable font axis control with named instance discovery\n- 🎨 **Color Font Palettes** — Query and customize color palettes in emoji and color fonts\n- 🔧 **Surface Sampling Options** — Control texture filtering when drawing surfaces with SKSamplingOptions\n\n### 🌐 Platform Support\n- 🌐 **GTK 4 Support** — Native SkiaSharp views for modern GTK 4 Linux applications\n- 🌐 **Linux Bionic Support** — Run SkiaSharp on Bionic-based Linux distributions\n- 🔒 **Spectre Mitigation** — Native DLLs hardened against speculative execution attacks on Windows and Linux\n\n### 🚀 Engine Improvements (Skia m120→m133)\n- ⚡ **Faster Noise Shaders** — Perlin noise renders correctly under rotation and runs significantly faster\n- ⚡ **Sharper Mipmaps** — Downscaled images render crisper with improved default mipmap sharpening\n- 🖼️ **Auto Image Orientation** — Photos now automatically respect Exif rotation metadata from cameras\n\n### 🐛 Key Fixes\n- Fixed SKGLView not rendering after Android TabBar navigation\n- Fixed NativeAOT crash when reflection is disabled\n- D3D12 delay-loaded to prevent crashes on systems without DirectX 12\n\n### 📦 Dependencies\n- Skia upgraded from m119 → m133 (14 milestone jump)\n- libpng 1.6.58, libexpat 2.7.5, libwebp 1.6.0, brotli 1.2.0\n- Built with .NET 10 SDK", "changelog": "# Changelog: SkiaSharp v3.119.2 → 4.0 (origin/main)\n\n## ⚠️ Breaking Changes\n\n- **Major version upgrade from 3.x to 4.x** (#3640)\n Package version changes from 3.x to 4.x. Update all NuGet package references.\n\n- **Default typeface resolution moved to managed layer** (#3730, fixes #3693)\n `SKTypeface.Default`, `CreateDefault()`, and `FromFamilyName()` now use `SKFontManager.Default.MatchFamily` instead of native resolution. `SKPaint()` constructor uses a managed-layer default font. `FromFamilyName` returns `Default` instead of `null` when no match is found.\n - New: `SKTypeface.Empty`, `SKTypeface.IsEmpty`\n - Affected: `SKTypeface`, `SKPaint`, `SKFont`\n\n## ✨ New APIs\n\n- **Variable font support** (#3703) — `HarfBuzzSharp.Face`: `HasVariationData`, `VariationAxisCount`, `VariationAxisInfos`, `TryFindVariationAxis`, `NamedInstanceCount`, `GetNamedInstanceDesignCoords`, `GetNamedInstanceSubfamilyNameId`. `HarfBuzzSharp.Font`: `SetVariations`, `SetVariationCoordsDesign`, `SetVariationCoordsNormalized`, `SetVariationNamedInstance`, `VariationCoordsNormalized`.\n\n- **Color font palette support** (#3742) — `HarfBuzzSharp.Face`: `HasPalettes`, `PaletteCount`, `GetPaletteColors`, `GetPaletteFlags`, `GetPaletteNameId`, `GetPaletteColorNameId`, `HasColorLayers`, `HasColorPng`, `HasColorSvg`. New types: `SKFontArguments`, `SKFontPaletteOverride`.\n\n- **SkiaSharp.Views.Gtk4** (#3527) — New `SKDrawingArea` for GTK 4 applications.\n\n- **Linux Bionic native assets** (#3217) — `SkiaSharp.NativeAssets.Linux.Bionic` support.\n\n- **SKSamplingOptions on surface draw** (#3491) — `SKSurface.Draw(canvas, x, y, sampling, paint)` and `SKCanvas.DrawSurface(surface, x, y, sampling, paint)`.\n\n- **SKColorSpace.CreateCicp** — Create color spaces from ITU-T H.273 CICP code points.\n\n- **SKFourByteTag** — Type-safe OpenType tag struct with parse, equality, and implicit conversions.\n\n- **New color types** — `SKColorType.Bgra10101010XR` and `SKColorType.RgbF16F16F16x`.\n\n## 🐛 Bug Fixes\n\n- Fix SKGLView not rendering after tab switch in MAUI TabBar on Android (#3076)\n- Fix AoT crash when `IlcDisableReflection=true` — SKBlender avoids reflection (#3485)\n- Fix Cairo assertion crash in GTK4 SKDrawingArea (#3562)\n- Delay-load D3D12 DLLs to prevent crashes on systems without DX12 (#3633)\n- Fix tvOS NativeReference targets: split device/simulator frameworks (#3561)\n- Fix Linux ARM64 crash: add fontconfig runtime to cross-compile sysroot (#3494)\n- Refactor SKManagedStream: replace parent/child chain with SKData snapshot (#3589)\n- Fix x86 .NET Framework threading test OOM failures (#3608)\n- Fix LoongArch64 Linux build mapping (#3648)\n\n## 🔒 Security\n\n- Spectre mitigation enabled for `libSkiaSharp.dll` on Windows (#3496)\n- Spectre mitigation enabled for `libSkiaSharp.so` on Linux (#3502)\n- D3D12 DLLs delay-loaded to prevent DLL hijacking surface (#3633)\n\n## 🚀 Engine Improvements (Skia m119 → m133)\n\nBy upgrading from Skia m119 to m133, these improvements are automatic — no code changes needed:\n\n- **Perlin noise fixed and faster** (m124) — Noise shaders now rotate correctly under transforms; raster performance significantly improved.\n- **Vulkan device-lost callback** (m123) — Optional callback when VK_ERROR_DEVICE_LOST occurs, with VK_EXT_device_fault diagnostics.\n- **Exif orientation respected** (m123) — `SkCodec.getImage()` auto-rotates based on Exif metadata.\n- **SkSL keyword validation** (m128) — Reserved GLSL keywords from modern versions now rejected at compile time instead of silently failing.\n- **Mipmap sharpening default** (m131) — Mipmapped textures render sharper by default.\n\n## 🌐 Platform Changes\n\n- Tizen x64 and arm64 native build support (#3620)\n- Enforce `--no-undefined` linker flag for Linux and Android (#3629)\n- Split NuGet mega-packages to stay under 500MB feed limit (#3584)\n\n## 📦 Dependencies\n\n| Dependency | From | To | PR |\n|---|---|---|---|\n| Skia | m119 | m133 | #3560, #3660 |\n| libpng | 1.6.47 | 1.6.58 | #3452, #3718 |\n| libexpat | 2.6.4 | 2.7.5 | #3458, #3717 |\n| libwebp | 1.4.0 | 1.6.0 | #3478 |\n| brotli | 1.1.0 | 1.2.0 | #3469 |\n| .NET SDK | 8.x | 10.0.100 | #3514 |"};</script> | |
| <div class="shell mx-auto px-3 py-3"> | |
| <div class="d-flex align-items-center justify-content-between mb-2"> | |
| <div><strong style="font-size:1.15rem">SkiaSharp Release Notes</strong> | |
| <span class="text-muted" style="font-size:.8rem;margin-left:8px" id="subtitle"></span></div> | |
| </div> | |
| <div class="summary-bar d-flex gap-4 align-items-center flex-wrap" id="summary-bar"></div> | |
| <div style="border-bottom:1px solid var(--border);margin-bottom:8px"> | |
| <button class="tab-btn active" onclick="switchTab('findings')">Findings <span id="findings-count" class="count-badge"></span></button> | |
| <button class="tab-btn" onclick="switchTab('slides')">Slides</button> | |
| <button class="tab-btn" onclick="switchTab('changelog')">Changelog</button> | |
| </div> | |
| <!-- Findings Tab --> | |
| <div class="tab-pane active" id="tab-findings"> | |
| <div class="filter-bar" id="findings-filters"> | |
| <div class="filter-row"> | |
| <span class="filter-label">Search:</span> | |
| <input type="text" class="search-box" style="width:300px" id="search" placeholder="Search findings..."> | |
| </div> | |
| <div class="filter-row" id="fr-importance"></div> | |
| <div class="filter-row" id="fr-changetype"></div> | |
| <div class="filter-row" id="fr-label"></div> | |
| <div class="filter-row"> | |
| <span class="filter-label">Group by:</span> | |
| <select class="ctrl-select" id="group-by" onchange="rebuildList()"> | |
| <option value="none">None</option> | |
| <option value="changeType" selected>Change Type</option> | |
| <option value="importance">Importance</option> | |
| <option value="label">Label</option> | |
| </select> | |
| <button class="dir-btn" id="group-dir" onclick="toggleDir('group-dir');rebuildList()" title="Toggle group order">▼</button> | |
| <span style="width:16px"></span> | |
| <span class="filter-label" style="width:55px">Sort by:</span> | |
| <select class="ctrl-select" id="sort-by" onchange="rebuildList()"> | |
| <option value="importance">Importance</option> | |
| <option value="changeType">Change Type</option> | |
| <option value="name">Name</option> | |
| </select> | |
| <button class="dir-btn" id="sort-dir" onclick="toggleDir('sort-dir');rebuildList()" title="Toggle sort order">▼</button> | |
| </div> | |
| </div> | |
| <div class="text-muted d-flex justify-content-between" style="font-size:.72rem;padding:0 12px 4px"> | |
| <span id="filter-count"></span> | |
| <button class="reset-btn" onclick="resetFilters()">Reset filters</button> | |
| </div> | |
| <div id="list"></div> | |
| </div> | |
| <!-- Slides Tab --> | |
| <div class="tab-pane" id="tab-slides"> | |
| <div class="md-content" id="slides-content"></div> | |
| </div> | |
| <!-- Changelog Tab --> | |
| <div class="tab-pane" id="tab-changelog"> | |
| <div class="md-content" id="changelog-content"></div> | |
| </div> | |
| <div class="meta-footer" id="meta-footer"></div> | |
| </div> | |
| <script> | |
| const CTL={added:'✨ Added',changed:'🔄 Changed',fixed:'🐛 Fixed',deprecated:'⚠️ Deprecated',removed:'❌ Removed',dependency:'📦 Dependency',platform:'🌐 Platform',upstream:'🚀 Engine'}; | |
| const IL={breaking:'⚠️ Breaking',major:'Major',minor:'Minor',patch:'Patch'}; | |
| const IO={breaking:0,major:1,minor:2,patch:3}; | |
| const CTO={removed:0,added:1,changed:2,fixed:3,upstream:4,deprecated:5,dependency:6,platform:7}; | |
| let fF={importance:new Set(),changeType:new Set(),label:new Set()}; | |
| let sT=''; | |
| function esc(s){const d=document.createElement('div');d.textContent=s||'';return d.innerHTML;} | |
| function toggleDir(id){const b=document.getElementById(id);b.textContent=b.textContent==='▼'?'▲':'▼';} | |
| function getDir(id){return document.getElementById(id).textContent==='▲'?-1:1;} | |
| function switchTab(t){document.querySelectorAll('.tab-pane').forEach(p=>p.classList.remove('active'));document.querySelectorAll('.tab-btn').forEach(b=>b.classList.remove('active'));document.getElementById('tab-'+t).classList.add('active');document.querySelector(`[onclick="switchTab('${t}')"]`).classList.add('active');} | |
| function tF(type,val){fF[type].has(val)?fF[type].delete(val):fF[type].add(val);applyAndRebuild();} | |
| function resetFilters(){Object.values(fF).forEach(s=>s.clear());sT='';document.getElementById('search').value='';applyAndRebuild();} | |
| function passesFilter(f){if(fF.importance.size&&!fF.importance.has(f.importance))return false;if(fF.changeType.size&&!fF.changeType.has(f.changeType))return false;if(fF.label.size&&!(f.labels||[]).some(l=>fF.label.has(l)))return false;if(sT&&!JSON.stringify(f).toLowerCase().includes(sT))return false;return true;} | |
| function sortFindings(arr){const s=document.getElementById('sort-by').value;const dir=getDir('sort-dir');return[...arr].sort((a,b)=>{let r=0;switch(s){case'importance':r=(IO[a.importance]??4)-(IO[b.importance]??4);break;case'changeType':r=(CTO[a.changeType]??9)-(CTO[b.changeType]??9);break;case'name':r=(a.name||'').localeCompare(b.name||'');break;}return r*dir;});} | |
| function groupKey(f){const g=document.getElementById('group-by').value;switch(g){case'changeType':return f.changeType;case'importance':return f.importance;case'label':return(f.labels||[])[0]||'other';default:return null;}} | |
| function groupLabel(key,by){switch(by){case'changeType':return CTL[key]||key;case'importance':return IL[key]||key;default:return key;}} | |
| function groupOrder(key,by){switch(by){case'changeType':return CTO[key]??9;case'importance':return IO[key]??9;default:return 0;}} | |
| function applyAndRebuild(){rebuildList();document.querySelectorAll('#tab-findings .pill[data-filter]').forEach(p=>{const[t,v]=p.dataset.filter.split(':');p.classList.toggle('dim',fF[t]?.size>0&&!fF[t].has(v));});} | |
| function rebuildList(){const filtered=DATA.findings.filter(passesFilter);const sorted=sortFindings(filtered);document.getElementById('filter-count').textContent=`Showing ${filtered.length} of ${DATA.findings.length}`;const by=document.getElementById('group-by').value;const el=document.getElementById('list');if(by==='none'){el.innerHTML=sorted.map(renderRow).join('');return;} | |
| const groups={};sorted.forEach(f=>{const k=groupKey(f);if(!groups[k])groups[k]=[];groups[k].push(f);});const gOrder=Object.keys(groups);const gDir=getDir('group-dir');gOrder.sort((a,b)=>(groupOrder(a,by)-groupOrder(b,by))*gDir||(a.localeCompare(b)*gDir)); | |
| el.innerHTML=gOrder.map(k=>`<div class="group-header">${groupLabel(k,by)} <span class="count-badge">${groups[k].length}</span></div>`+groups[k].map(renderRow).join('')).join('');} | |
| function renderRow(f){const ct=`<span class="pill ct-${f.changeType}" data-filter="changeType:${f.changeType}" onclick="event.stopPropagation();tF('changeType','${f.changeType}')">${CTL[f.changeType]||f.changeType}</span>`;const imp=`<span class="pill i-${f.importance}" data-filter="importance:${f.importance}" onclick="event.stopPropagation();tF('importance','${f.importance}')">${f.importance}</span>`;const labels=(f.labels||[]).map(l=>`<span class="pill l-pill" data-filter="label:${l}" onclick="event.stopPropagation();tF('label','${l}')">${l}</span>`).join(' ');const prLink=f.pr?`<a class="pr-link" href="${f.prUrl||'https://github.com/mono/SkiaSharp/pull/'+f.pr}" target="_blank">#${f.pr}</a>`:'';const commitLink=f.commit&&!f.pr?`<a class="pr-link" href="${f.commitUrl||'https://github.com/mono/SkiaSharp/commit/'+f.commit}" target="_blank">${f.commit.slice(0,7)}</a>`:''; | |
| let det='<div class="detail-grid">';if(f.affectedTypes?.length)det+=`<div class="detail-label">Types</div><div class="detail-value">${f.affectedTypes.map(t=>'<code>'+esc(t)+'</code>').join(', ')}</div>`;if(f.affectedMethods?.length)det+=`<div class="detail-label">Methods</div><div class="detail-value">${f.affectedMethods.map(m=>'<code>'+esc(m)+'</code>').join(', ')}</div>`;if(f.cApiFunction)det+=`<div class="detail-label">C API</div><div class="detail-value"><code>${esc(f.cApiFunction)}</code></div>`;if(f.csharpMethod)det+=`<div class="detail-label">C#</div><div class="detail-value"><code>${esc(f.csharpMethod)}</code>${f.csharpFile?' ('+esc(f.csharpFile)+')':''}</div>`;if(f.skiaFeature)det+=`<div class="detail-label">Skia</div><div class="detail-value">${esc(f.skiaFeature)}${f.skiaMilestone?' (m'+f.skiaMilestone+')':''}</div>`;if(f.dependencyName)det+=`<div class="detail-label">Dependency</div><div class="detail-value"><code>${esc(f.dependencyName)}</code>: ${esc(f.dependencyFrom||'?')} → ${esc(f.dependencyTo||'?')}</div>`;if(f.platforms?.length)det+=`<div class="detail-label">Platforms</div><div class="detail-value">${f.platforms.join(', ')}</div>`;if(f.author)det+=`<div class="detail-label">Author</div><div class="detail-value">${esc(f.author)}</div>`;if(f.migrationGuide)det+=`<div class="detail-label">Migration</div><div class="detail-value" style="white-space:pre-wrap">${esc(f.migrationGuide)}</div>`;if(f.notes)det+=`<div class="detail-label">Notes</div><div class="detail-value">${esc(f.notes)}</div>`;det+='</div>'; | |
| return`<div class="row-item" onclick="this.classList.toggle('expanded')"><div class="d-flex align-items-start"><span class="row-chevron">▶</span><div style="flex:1"><div class="row-name">${esc(f.name)} ${prLink}${commitLink}</div><div class="row-pills">${ct} ${imp} ${labels}</div><div class="row-desc">${esc(f.description)}</div><div class="row-detail">${det}</div></div></div></div>`;} | |
| function renderSummary(s){const byCt=s.byChangeType||{};const byImp=s.byImportance||{};document.getElementById('summary-bar').innerHTML=[['Total',s.totalFindings||0,'#1f2328'],['Added',byCt.added||0,'#166534'],['Fixed',byCt.fixed||0,'#1e40af'],['Engine',byCt.upstream||0,'#9d174d'],['Deps',byCt.dependency||0,'#5b21b6'],['Platform',byCt.platform||0,'#115e59'],['Breaking',byImp.breaking||0,'#991b1b']].map(([l,n,c])=>`<div class="text-center"><div class="summary-num" style="color:${c}">${n}</div><div class="summary-label">${l}</div></div>`).join('');} | |
| function addPillRow(rowId,label,type,vals,pre){const row=document.getElementById(rowId);row.innerHTML=`<span class="filter-label">${label}:</span>`+vals.map(v=>{const cls=pre?`${pre}${v}`:'l-pill';const lb=type==='changeType'?(CTL[v]||v):type==='importance'?(IL[v]||v):v;return`<span class="pill ${cls}" data-filter="${type}:${v}" onclick="tF('${type}','${v}')">${lb}</span>`;}).join(' ');} | |
| function renderMarkdown(md){if(!md)return'<p style="color:var(--muted)">No content generated.</p>';return md.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/^### (.+)$/gm,'<h3>$1</h3>').replace(/^## (.+)$/gm,'<h2>$1</h2>').replace(/^# (.+)$/gm,'<h1>$1</h1>').replace(/\*\*(.+?)\*\*/g,'<strong>$1</strong>').replace(/`([^`]+)`/g,'<code>$1</code>').replace(/^- (.+)$/gm,'<li>$1</li>').replace(/(<li>.*<\/li>\n?)+/g,'<ul>$&</ul>').replace(/\n\n/g,'<br><br>').replace(/\n(?!<)/g,'\n');} | |
| document.addEventListener('DOMContentLoaded',()=>{if(typeof DATA==='undefined'){document.body.innerHTML='<p style="padding:2rem;color:red">No DATA found.</p>';return;}const m=DATA.meta;document.getElementById('subtitle').textContent=`${m.refFrom}..${m.refTo} · ${m.date} · ${m.commitCount} commits`;renderSummary(DATA.summary); | |
| addPillRow('fr-importance','Importance','importance',['breaking','major','minor','patch'],'i-'); | |
| addPillRow('fr-changetype','Type','changeType',['added','changed','fixed','deprecated','removed','dependency','platform','upstream'],'ct-'); | |
| const lbls=[...new Set(DATA.findings.flatMap(f=>f.labels||[]))].sort();if(lbls.length)addPillRow('fr-label','Label','label',lbls,''); | |
| document.getElementById('findings-count').textContent=DATA.findings.length; | |
| document.getElementById('slides-content').innerHTML=renderMarkdown(DATA.slides); | |
| document.getElementById('changelog-content').innerHTML=renderMarkdown(DATA.changelog); | |
| document.getElementById('meta-footer').innerHTML=`${m.repo} · ${m.refFrom}..${m.refTo} · ${m.date} · ${m.commitCount} commits`+(m.prCount?` · ${m.prCount} PRs`:'')+(m.skiaMilestoneBumped?` · Skia m${m.skiaMilestoneFrom}→m${m.skiaMilestoneTo}`:''); | |
| document.getElementById('search').addEventListener('input',e=>{sT=e.target.value.toLowerCase();applyAndRebuild();});rebuildList();}); | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment