Skip to content

Instantly share code, notes, and snippets.

@mattleibow
Created April 29, 2026 07:54
Show Gist options
  • Select an option

  • Save mattleibow/3fc5367c189479dfd6a43faa0a3835e9 to your computer and use it in GitHub Desktop.

Select an option

Save mattleibow/3fc5367c189479dfd6a43faa0a3835e9 to your computer and use it in GitHub Desktop.
Skia Analyst: Full scan m147 — 95 findings, both changelog and gap analysis
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Skia Analyst — m147 (full, 2026-04-29)</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:1200px;}
.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:.25;}
.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;}
.s-full{background:#dcfce7;color:#166534;}.s-partial{background:#fef3c7;color:#92400e;}
.s-missing{background:#fee2e2;color:#991b1b;}.s-action_needed{background:#fce7f3;color:#9d174d;}
.s-not_applicable,.s-correctly_absent{background:#f3f4f6;color:#6b7280;}
.imp-transformative{background:#fae8ff;color:#7e22ce;}.imp-significant{background:#dbeafe;color:#1e40af;}
.imp-moderate{background:#fef3c7;color:#92400e;}.imp-minor{background:#f3f4f6;color:#6b7280;}
.p-critical{background:#fee2e2;color:#991b1b;}.p-high{background:#fed7aa;color:#9a3412;}
.p-medium{background:#fef3c7;color:#92400e;}.p-low{background:#f3f4f6;color:#6b7280;}
.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-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;}
.reset-btn{font-size:.7rem;color:#3730a3;cursor:pointer;border:none;background:none;padding:2px 6px;}
.reset-btn:hover{text-decoration:underline;}
.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;}
.group-header{font-weight:700;font-size:.82rem;padding:8px 12px 4px;color:#1e40af;border-bottom:2px solid #dbeafe;margin-top:12px;background:#f8faff;}
.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;}
.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;}
.meta-footer{font-size:.72rem;color:var(--muted);margin-top:16px;padding:8px 12px;}
.ns-item{border-left:3px solid;padding:6px 12px;margin-bottom:6px;cursor:pointer;}
.ns-item:hover{background:var(--row-hover);}
.ns-critical{border-color:#dc2626;}.ns-high{border-color:#f97316;}
.ns-medium{border-color:#f59e0b;}.ns-low{border-color:#6b7280;}
</style>
</head>
<body>
<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">Skia Analyst</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>
<button class="tab-btn" onclick="switchTab('nextsteps')">Next Steps <span id="ns-count" class="count-badge"></span></button>
</div>
<div class="tab-pane active" id="tab-findings">
<div class="filter-bar">
<div class="filter-row"><span class="filter-label">Search:</span><input type="text" class="search-box" style="width:300px" id="search" placeholder="Search..." oninput="rebuild()"></div>
<div class="filter-row" id="fr-impact"></div>
<div class="filter-row" id="fr-priority"></div>
<div class="filter-row" id="fr-binding"></div>
<div class="filter-row" id="fr-changetype"></div>
<div class="filter-row" id="fr-importance"></div>
<div class="filter-row" id="fr-category"></div>
<div class="filter-row">
<span class="filter-label">Group by:</span>
<select class="ctrl-select" id="group-by" onchange="rebuild()">
<option value="none">None</option>
<option value="impact">Impact</option>
<option value="priority">Priority</option>
<option value="bindingStatus">Binding Status</option>
<option value="changeType">Change Type</option>
<option value="importance">Importance</option>
<option value="category">Category</option>
<option value="source">Source</option>
</select>
<button class="dir-btn" id="group-dir" onclick="this.textContent=this.textContent==='▼'?'▲':'▼';rebuild()"></button>
<span style="width:16px"></span>
<span class="filter-label" style="width:55px">Sort by:</span>
<select class="ctrl-select" id="sort-by" onchange="rebuild()">
<option value="action">Action Score ↕</option>
<option value="impact">Impact</option>
<option value="priority">Priority</option>
<option value="importance">Importance</option>
<option value="name">Name</option>
</select>
<button class="dir-btn" id="sort-dir" onclick="this.textContent=this.textContent==='▼'?'▲':'▼';rebuild()"></button>
</div>
</div>
<div class="d-flex justify-content-between" style="font-size:.72rem;padding:0 12px 4px">
<span id="filter-count"></span>
<button class="reset-btn" onclick="Object.values(fF).forEach(s=>s.clear());document.getElementById('search').value='';rebuild()">Reset</button>
</div>
<div id="list"></div>
</div>
<div class="tab-pane" id="tab-slides"><div class="md-content" id="slides-content"></div></div>
<div class="tab-pane" id="tab-changelog"><div class="md-content" id="changelog-content"></div></div>
<div class="tab-pane" id="tab-nextsteps"><div id="next-steps"></div></div>
<div class="meta-footer" id="meta-footer"></div>
</div>
<script>
const D = {"meta": {"date": "2026-04-29", "schemaVersion": "1.0", "repo": "mono/SkiaSharp", "currentMilestone": 147, "latestUpstreamMilestone": 148, "releaseNotesSource": "https://raw.githubusercontent.com/google/skia/main/RELEASE_NOTES.md", "scanMode": "full"}, "summary": {"totalFindings": 95, "byChangeType": {"added": 55, "changed": 15, "removed": 14, "deprecated": 1, "upstream": 5, "dependency": 2, "platform": 3}, "byImportance": {"minor": 33, "patch": 19, "major": 31, "breaking": 12}, "byBindingStatus": {"missing": 42, "not_applicable": 21, "correctly_absent": 8, "full": 16, "partial": 4, "action_needed": 4}, "byImpact": {"moderate": 32, "minor": 32, "significant": 29, "transformative": 2}, "byPriority": {"medium": 27, "low": 48, "high": 19, "critical": 1}, "bySource": {"release-notes": 84, "binding-audit": 1, "header-scan": 10}}, "findings": [{"name": "kR16_float_SkColorType color type", "category": "color", "description": "New single-channel red color type holding f16 (half-float) values, analogous to kAlpha16_float. Enables HDR/scientific imagery in red channel.", "source": "release-notes", "changeType": "added", "importance": "minor", "bindingStatus": "missing", "impact": "moderate", "priority": "medium", "effort": "small", "milestone": 148, "cppClass": "SkColorType", "skiaApi": "kR16_float_SkColorType", "affectedTypes": ["SKColorType"], "userValue": "Enables a new single-channel float color type useful for HDR data, normal/heightmaps stored in red channel, and scientific imagery.", "skillToUse": "api-add-review"}, {"name": "SkImages::WrapTexture no longer requires SkColorType", "category": "image", "description": "WrapTexture (Graphite/Ganesh) automatically picks a compatible color type from the texture format, and supports forcing opaque via kUnknown_SkAlphaType.", "source": "release-notes", "changeType": "changed", "importance": "minor", "bindingStatus": "not_applicable", "impact": "minor", "priority": "low", "milestone": 148, "notes": "SkiaSharp does not currently expose Graphite WrapTexture; impact limited."}, {"name": "SkCodec::getEncodedData() removed", "category": "codec", "description": "getEncodedData() has been removed from the public SkCodec API. Clients must hold their own SkData reference.", "source": "release-notes", "changeType": "removed", "importance": "patch", "bindingStatus": "correctly_absent", "impact": "minor", "priority": "low", "milestone": 147, "cppClass": "SkCodec", "notes": "SkiaSharp never exposed this; SKCodec keeps SKData internally on the C# side."}, {"name": "SK_SUPPORT_UNSPANNED_APIS removed", "category": "behavior_change", "description": "The SK_SUPPORT_UNSPANNED_APIS define is removed; APIs are span-only going forward. C API shims must use the span-style upstream signatures.", "source": "release-notes", "changeType": "removed", "importance": "minor", "bindingStatus": "not_applicable", "impact": "minor", "priority": "low", "milestone": 147, "notes": "Internal to the C++ surface; the C API shim adapts."}, {"name": "SkColorSpacePrimaries::operator==", "category": "color", "description": "Added equality comparison for SkColorSpacePrimaries.", "source": "release-notes", "changeType": "added", "importance": "patch", "bindingStatus": "missing", "impact": "minor", "priority": "low", "effort": "trivial", "milestone": 146, "cppMethod": "SkColorSpacePrimaries::operator==", "affectedTypes": ["SKColorSpacePrimaries"], "userValue": "Compare two color space primaries directly."}, {"name": "SkPathBuilder accepted by more path utilities", "category": "path", "description": "SkContourMeasure::getSegment, SkPathEffect::filterPath, SkPathMeasure::getSegment and skpathutils::FillPathWithPaint now accept SkPathBuilder, avoiding an extra path copy.", "source": "release-notes", "changeType": "changed", "importance": "minor", "bindingStatus": "full", "impact": "moderate", "priority": "medium", "milestone": 146, "cppClass": "SkPathBuilder", "affectedTypes": ["SKPathBuilder", "SKPathMeasure", "SKPathEffect"], "userValue": "Avoid extra copies of large path data when running path-effect or path-measure pipelines.", "notes": "SkiaSharp already wraps SKPathBuilder; the C# overloads exist (e.g. GetFillPath(SKPath, SKPathBuilder))."}, {"name": "SkGradientShader.h removed (use SkGradient.h)", "category": "shader", "description": "The header SkGradientShader.h was removed in favor of SkGradient.h factory functions for gradient shaders.", "source": "release-notes", "changeType": "removed", "importance": "patch", "bindingStatus": "not_applicable", "impact": "minor", "priority": "low", "milestone": 146, "notes": "SkiaSharp's C API shim is internal; gradient creation continues to work."}, {"name": "skhdr::Agtm — SMPTE ST 2094-50 Adaptive Global Tone Mapping", "category": "image", "description": "New AGTM HDR metadata parsing/serialization with a tone-mapping SkColorFilter. Lets clients consume embedded HDR tone-mapping metadata.", "source": "release-notes", "changeType": "added", "importance": "major", "bindingStatus": "missing", "impact": "significant", "priority": "medium", "effort": "large", "milestone": 145, "cppClass": "skhdr::Agtm", "cppHeader": "modules/skhdr/Agtm.h", "userValue": "Display HDR images with proper SDR fallback using upstream-standard adaptive tone mapping.", "slideBullet": "🌈 **AGTM HDR tone mapping** – Parse and apply SMPTE ST 2094-50 metadata to render HDR images correctly on SDR displays.", "skillToUse": "api-add-review"}, {"name": "SkCodec::Options::fMaxDecodeMemory", "category": "codec", "description": "SkCodec::Options now contains fMaxDecodeMemory. If decoding would exceed it, the codec returns nullptr and a new kOutOfMemory result.", "source": "release-notes", "changeType": "added", "importance": "major", "bindingStatus": "missing", "impact": "significant", "priority": "high", "effort": "small", "milestone": 145, "cppClass": "SkCodec::Options", "skiaApi": "SkCodec::Options::fMaxDecodeMemory", "affectedTypes": ["SKCodec", "SKCodecOptions"], "userValue": "Defend apps against decompression bombs by capping memory used per decode.", "slideBullet": "🛡️ **Decode memory limits** – Reject malicious oversized images before they exhaust memory.", "skillToUse": "api-add-review", "labels": ["security", "hardening"]}, {"name": "SkData::Equals helper", "category": "utility", "description": "Adds SkData::Equals to compare two SkData instances (handles nullptr).", "source": "release-notes", "changeType": "added", "importance": "patch", "bindingStatus": "missing", "impact": "minor", "priority": "low", "effort": "trivial", "milestone": 145, "cppMethod": "SkData::Equals", "affectedTypes": ["SKData"], "userValue": "Convenience for content-equality checks on encoded blobs."}, {"name": "kR16_unorm_SkColorType color type", "category": "color", "description": "New 16-bit-per-channel unsigned normalized red color type added.", "source": "release-notes", "changeType": "added", "importance": "minor", "bindingStatus": "full", "impact": "moderate", "priority": "low", "milestone": 144, "cppClass": "SkColorType", "skiaApi": "kR16_unorm_SkColorType", "affectedTypes": ["SKColorType"], "csharpMethod": "SKColorType.R16Unorm", "csharpFile": "binding/SkiaSharp/Definitions.cs", "userValue": "Single-channel high-precision images (heightmaps, depth maps).", "notes": "Verified: SKColorType.R16Unorm exists in EnumMappings.cs."}, {"name": "SkImage::refEncodedData returns const SkData*", "category": "image", "description": "refEncodedData() now returns pointer-to-const SkData, signaling that the underlying bytes are read-only.", "source": "release-notes", "changeType": "changed", "importance": "patch", "bindingStatus": "full", "impact": "minor", "priority": "low", "milestone": 144, "cppMethod": "SkImage::refEncodedData", "notes": "C# wrapper SKImage.EncodedData unchanged; signal-only."}, {"name": "SkPath geometry becoming immutable", "category": "path", "description": "SkPath is migrating to remove mutating methods (moveTo, lineTo, etc). Path creation goes through SkPathBuilder or new factories (Raw, Rect, Oval, Circle, RRect, Polygon, Line). Gated today by SK_HIDE_PATH_EDIT_METHODS — the flag will be removed in a future release.", "source": "release-notes", "changeType": "deprecated", "importance": "breaking", "bindingStatus": "full", "impact": "significant", "priority": "critical", "effort": "medium", "milestone": 143, "cppClass": "SkPath", "affectedTypes": ["SKPath", "SKPathBuilder"], "userValue": "SKPath today wraps both old mutating APIs and SKPathBuilder; SkiaSharp must keep mutating APIs working via SKPathBuilder so user code is unaffected.", "migrationGuide": "// Old (still works in SkiaSharp via SKPathBuilder under the hood)\n// var p = new SKPath(); p.MoveTo(...); p.LineTo(...);\n// New, recommended:\nusing var b = new SKPathBuilder();\nb.MoveTo(...); b.LineTo(...);\nusing var p = b.Snapshot();", "slideBullet": "🛤️ **SKPathBuilder for new code** – Skia is making SkPath immutable; build paths with SKPathBuilder to stay future-proof.", "notes": "Verified: SKPathBuilder.cs ships in worktree (m147 binding work). Existing SKPath mutating methods are now [Obsolete] in many places.", "skillToUse": "api-docs"}, {"name": "SkPathBuilder::rArcTo aligned with arcTo", "category": "path", "description": "Relative arcTo updated to align with absolute arcTo signature.", "source": "release-notes", "changeType": "changed", "importance": "minor", "bindingStatus": "full", "impact": "minor", "priority": "low", "milestone": 143, "cppMethod": "SkPathBuilder::rArcTo", "affectedTypes": ["SKPathBuilder"]}, {"name": "SkPngRustEncoder public APIs", "category": "codec", "description": "Public APIs added on SkPngRustEncoder for encoding SkImage/SkPixmap into SkData.", "source": "release-notes", "changeType": "added", "importance": "minor", "bindingStatus": "missing", "impact": "moderate", "priority": "low", "effort": "medium", "milestone": 143, "cppClass": "SkPngRustEncoder", "notes": "SkiaSharp uses libpng-based encoder; switching is a build-flag/dependency decision.", "labels": ["security", "memory-safety"]}, {"name": "SkNamedTransferFn::kRec709 → BT.1886 gamma 2.4", "category": "color", "description": "kRec709 transfer function now matches the pure gamma-2.4 definition from ITU-R BT.1886. Affects transfer characteristic values 1, 6, 11, 14, 16.", "source": "release-notes", "changeType": "upstream", "importance": "major", "bindingStatus": "not_applicable", "impact": "significant", "priority": "medium", "milestone": 142, "cppMethod": "SkNamedTransferFn::kRec709", "userValue": "More accurate color reproduction for video / Rec.709 content.", "slideBullet": "🎨 **More accurate Rec.709 colors** – BT.1886 gamma 2.4 brings video colors in line with broadcast standards."}, {"name": "HDR metadata in PNG decoder/encoder", "category": "codec", "description": "SkPngDecoder, SkPngRustDecoder and SkPngEncoder gained HDR metadata support, plus skhdr::Metadata, ContentLightLevelInfo and MasteringDisplayColorVolume structures.", "source": "release-notes", "changeType": "added", "importance": "major", "bindingStatus": "missing", "impact": "significant", "priority": "high", "effort": "large", "milestone": 142, "cppHeader": "modules/skhdr/Metadata.h", "affectedTypes": ["SKCodec", "SKBitmap"], "userValue": "Round-trip HDR PNGs with content-light-level and mastering-display metadata.", "slideBullet": "🌈 **HDR PNG metadata** – Decode and encode PNGs with full HDR mastering metadata.", "skillToUse": "api-add-review"}, {"name": "SkPngRustDecoder/Encoder are official public API", "category": "codec", "description": "Rust-backed PNG decoder/encoder graduated from experimental to officially supported public API.", "source": "release-notes", "changeType": "changed", "importance": "major", "bindingStatus": "missing", "impact": "moderate", "priority": "medium", "effort": "large", "milestone": 142, "cppClass": "SkPngRustDecoder", "userValue": "Memory-safe PNG decoding option.", "labels": ["security", "memory-safety"]}, {"name": "ICC profile fields removed from encoder Options", "category": "codec", "description": "fICCProfile and fICCProfileDescription removed from SkPngEncoder, SkJpegEncoder, SkWebpEncoder Options structs.", "source": "release-notes", "changeType": "removed", "importance": "breaking", "bindingStatus": "correctly_absent", "impact": "minor", "priority": "low", "milestone": 142, "notes": "SkiaSharp's encoder option structs (SKPngEncoderOptions etc.) never exposed these fields."}, {"name": "SkNamedTransferFn::kHLG and kPQ use new skcms representations", "category": "color", "description": "kHLG and kPQ transfer functions migrated to new skcms representations; SkColorSpace::MakeCICP automatically benefits.", "source": "release-notes", "changeType": "upstream", "importance": "major", "bindingStatus": "not_applicable", "impact": "significant", "priority": "medium", "milestone": 141, "affectedTypes": ["SKColorSpace"], "userValue": "More accurate HDR (HLG and PQ) color space round-trips, automatic for SKColorSpace.CreateCicp callers.", "slideBullet": "🌈 **Better HDR color (HLG/PQ)** – More accurate transfer-function math for HLG and PQ content via SKColorSpace.CreateCicp."}, {"name": "SkPath::asArc() removed", "category": "path", "description": "SkPath::asArc removed; path is no longer queryable as an arc.", "source": "release-notes", "changeType": "removed", "importance": "patch", "bindingStatus": "correctly_absent", "impact": "minor", "priority": "low", "milestone": 141, "notes": "SkiaSharp never exposed asArc."}, {"name": "SkShader::makeWithWorkingColorSpace optional output color space", "category": "shader", "description": "makeWithWorkingColorSpace now accepts an optional output color space, letting shaders inform Skia of color-space changes they apply.", "source": "release-notes", "changeType": "added", "importance": "minor", "bindingStatus": "missing", "impact": "moderate", "priority": "low", "effort": "small", "milestone": 141, "cppMethod": "SkShader::makeWithWorkingColorSpace", "affectedTypes": ["SKShader"]}, {"name": "DNG SDK 1.7.1 support", "category": "codec", "description": "SkRawCodec updated to support DNG SDK versions 1.4 and 1.7.1 via SK_DNG_VERSION.", "source": "release-notes", "changeType": "dependency", "importance": "patch", "bindingStatus": "not_applicable", "impact": "minor", "priority": "low", "milestone": 139, "dependencyName": "dng_sdk", "dependencyTo": "1.7.1"}, {"name": "iOS 12 and macOS 10.15 dropped", "category": "platform", "description": "Skia removed support for iOS 12 and macOS 10.15.", "source": "release-notes", "changeType": "platform", "importance": "breaking", "bindingStatus": "not_applicable", "impact": "moderate", "priority": "medium", "milestone": 139, "platforms": ["iOS", "macOS"], "userValue": "SkiaSharp's platform support matrix should be updated to reflect new minimum versions."}, {"name": "Vulkan 1.1 minimum", "category": "platform", "description": "Ganesh's Vulkan implementation now requires Vulkan 1.1 minimum.", "source": "release-notes", "changeType": "platform", "importance": "breaking", "bindingStatus": "not_applicable", "impact": "moderate", "priority": "medium", "milestone": 139, "platforms": ["Vulkan"]}, {"name": "VulkanPreferredFeatures helper", "category": "platform", "description": "New API to auto-query and add Vulkan extensions/features that Skia benefits from. Auto-opts in to future Skia improvements.", "source": "release-notes", "changeType": "added", "importance": "minor", "bindingStatus": "missing", "impact": "moderate", "priority": "low", "effort": "medium", "milestone": 139, "cppClass": "VulkanPreferredFeatures"}, {"name": "SkCodec::isAnimated()", "category": "codec", "description": "New isAnimated() method disambiguates getRepetitionCount()==0 (was it animated with infinite repetition or just a still image?).", "source": "release-notes", "changeType": "added", "importance": "minor", "bindingStatus": "missing", "impact": "moderate", "priority": "medium", "effort": "trivial", "milestone": 135, "cppMethod": "SkCodec::isAnimated", "affectedTypes": ["SKCodec"], "userValue": "Reliably detect animated images (GIF/WebP/APNG) without relying on repetition count semantics.", "skillToUse": "api-add-review"}, {"name": "More color spaces for gradient interpolation", "category": "shader", "description": "A98 RGB, ProPhoto RGB, Display P3 and Rec2020 color spaces can now be used as gradient interpolation spaces.", "source": "release-notes", "changeType": "added", "importance": "minor", "bindingStatus": "not_applicable", "impact": "moderate", "priority": "low", "milestone": 134, "notes": "SkiaSharp's gradient APIs do not currently expose Interpolation parameter (see m114 finding); benefit unlocks once that is bound."}, {"name": "SkColorSpace::MakeCICP", "category": "color", "description": "Create an SkColorSpace from CICP code points per Rec. ITU-T H.273.", "source": "release-notes", "changeType": "added", "importance": "major", "bindingStatus": "full", "impact": "significant", "priority": "medium", "milestone": 133, "cppMethod": "SkColorSpace::MakeCICP", "cApiFunction": "sk_colorspace_new_cicp", "csharpMethod": "SKColorSpace.CreateCicp", "csharpFile": "binding/SkiaSharp/SKColorSpace.cs", "affectedTypes": ["SKColorSpace"], "userValue": "Construct color spaces from broadcast/HDR standards' CICP code points (BT.2020, PQ, HLG, etc.) without specifying primaries/transfer manually.", "slideBullet": "🎬 **CICP color spaces** – Create SKColorSpace from broadcast standards (BT.2020/PQ/HLG) via SKColorSpace.CreateCicp.", "notes": "Verified: sk_colorspace_new_cicp implemented in C API and bound to SKColorSpace.CreateCicp."}, {"name": "SK_CANVAS_SAVE_RESTORE_PREALLOC_COUNT tunable", "category": "performance", "description": "New build define lets clients tune SkCanvas save() preallocation. Defaults to ~3KB; reducing it saves RAM in canvas-light scenarios.", "source": "release-notes", "changeType": "added", "importance": "patch", "bindingStatus": "not_applicable", "impact": "minor", "priority": "low", "milestone": 133}, {"name": "approximateFilteredBounds removed from SkMaskFilter", "category": "behavior_change", "description": "SkMaskFilter::approximateFilteredBounds was removed.", "source": "release-notes", "changeType": "removed", "importance": "patch", "bindingStatus": "correctly_absent", "impact": "minor", "priority": "low", "milestone": 133}, {"name": "SkCodec::hasHighBitDepthEncodedData()", "category": "codec", "description": "New SkCodec method reports whether the encoded image data is high bit-depth (e.g., 10/12-bit AVIF, 16-bit PNG).", "source": "release-notes", "changeType": "added", "importance": "minor", "bindingStatus": "missing", "impact": "moderate", "priority": "medium", "effort": "trivial", "milestone": 132, "cppMethod": "SkCodec::hasHighBitDepthEncodedData", "affectedTypes": ["SKCodec"], "userValue": "Decide whether to allocate F16/16-bit destination surfaces to preserve HDR/high bit-depth fidelity.", "skillToUse": "api-add-review"}, {"name": "SkPathEffect::DashType / DashInfo / asADash removed", "category": "path", "description": "DashType, DashInfo and asADash removed from public SkPathEffect API.", "source": "release-notes", "changeType": "removed", "importance": "patch", "bindingStatus": "correctly_absent", "impact": "minor", "priority": "low", "milestone": 132}, {"name": "SaveLayerRec backdrop tile mode", "category": "canvas", "description": "SaveLayerRec can now specify a SkTileMode to apply to backdrop content when the new layer's effects would sample outside the previous layer's image. Improves quality of backdrop blurs near layer edges.", "source": "release-notes", "changeType": "added", "importance": "major", "bindingStatus": "partial", "impact": "significant", "priority": "high", "effort": "small", "milestone": 131, "cppClass": "SkCanvas::SaveLayerRec", "affectedTypes": ["SKCanvasSaveLayerRec"], "userValue": "Avoid edge artifacts in backdrop blurs (frosted glass, depth-of-field).", "slideBullet": "🪟 **Backdrop tile mode for blurs** – Eliminate edge artifacts in frosted-glass backdrop blurs with a tile-mode parameter.", "notes": "C API sk_canvas_savelayerrec_t struct does NOT include backdropTileMode; C# SKCanvasSaveLayerRec also missing it. Quick win to extend struct + binding.", "skillToUse": "api-add-review"}, {"name": "GrContextOptions::fSharpenMipmappedTextures restored", "category": "performance", "description": "Mipmap sharpening flag is back, on by default. Higher quality mipmapped texture sampling.", "source": "release-notes", "changeType": "upstream", "importance": "minor", "bindingStatus": "not_applicable", "impact": "moderate", "priority": "low", "milestone": 131, "userValue": "Sharper mipmapped textures by default."}, {"name": "SkColorFilter::filterColor removed", "category": "color", "description": "filterColor() removed from SkColorFilter; use filterColor4f() instead.", "source": "release-notes", "changeType": "removed", "importance": "breaking", "bindingStatus": "action_needed", "impact": "moderate", "priority": "high", "milestone": 130, "milestoneDeprecated": 124, "milestoneRemoved": 130, "cppMethod": "SkColorFilter::filterColor", "affectedTypes": ["SKColorFilter"], "notes": "If SKColorFilter currently calls sk_colorfilter_filter_color, the C shim must be migrated to filterColor4f.", "skillToUse": "issue-fix"}, {"name": "SkImage::makeScaled", "category": "image", "description": "SkImage gained makeScaled in m128, but the audited C API has no sk_image_make_scaled function and SKImage.cs exposes only ScalePixels helpers rather than a new SKImage-returning API.", "source": "release-notes", "changeType": "added", "importance": "major", "bindingStatus": "missing", "impact": "significant", "priority": "high", "effort": "small", "milestone": 128, "cppMethod": "SkImage::makeScaled", "affectedTypes": ["SKImage"], "userValue": "One-call high-quality image rescaling without manual surface plumbing.", "slideBullet": "🖼️ **SKImage.MakeScaled** – Rescale images in one call while preserving their backing storage type.", "skillToUse": "api-add-review", "cppClass": "SkImage", "cppHeader": "include/core/SkImage.h"}, {"name": "Perlin noise: rotation-correct + raster speedup", "category": "performance", "description": "MakeFractalNoise and MakeTurbulence now rotate properly under transforms; CPU performance significantly improved.", "source": "release-notes", "changeType": "upstream", "importance": "minor", "bindingStatus": "not_applicable", "impact": "moderate", "priority": "low", "milestone": 124, "userValue": "Perlin noise shaders look correct under rotation and render faster on CPU."}, {"name": "SkCodecs::DeferredImage", "category": "codec", "description": "New factory similar to SkImages::DeferredFromEncodedData but accepts an SkCodec directly, removing the dependency on compiled-in codecs.", "source": "release-notes", "changeType": "added", "importance": "minor", "bindingStatus": "missing", "impact": "moderate", "priority": "low", "effort": "small", "milestone": 124, "cppMethod": "SkCodecs::DeferredImage", "affectedTypes": ["SKImage", "SKCodec"]}, {"name": "Vulkan VK_EXT_device_fault callback", "category": "platform", "description": "Vulkan backend invokes a client-provided callback on VK_ERROR_DEVICE_LOST, with extended debug info if VK_EXT_device_fault is enabled.", "source": "release-notes", "changeType": "added", "importance": "major", "bindingStatus": "missing", "impact": "significant", "priority": "medium", "effort": "medium", "milestone": 123, "cppClass": "GrVkBackendContext", "affectedTypes": ["GRVkBackendContext"], "userValue": "Diagnose Vulkan device-lost crashes in production.", "slideBullet": "🛡️ **Vulkan device-lost diagnostics** – Get a callback with debug data when the Vulkan driver loses the device.", "skillToUse": "api-add-review", "labels": ["reliability", "vulkan"]}, {"name": "SkCodec::getImage respects EXIF orientation", "category": "codec", "description": "getImage() now applies the encoded origin (e.g. EXIF rotation), so JPEGs come out the right way up.", "source": "release-notes", "changeType": "upstream", "importance": "major", "bindingStatus": "not_applicable", "impact": "significant", "priority": "medium", "milestone": 123, "userValue": "JPEGs decode in their proper orientation by default.", "slideBullet": "🔄 **EXIF-correct JPEG decoding** – SKCodec.GetImage now honors EXIF orientation automatically."}, {"name": "SkFontMgr::RefDefault() removed", "category": "font", "description": "The static default-font-manager accessor was deleted. Clients must own and pass an SkFontMgr explicitly.", "source": "release-notes", "changeType": "removed", "importance": "breaking", "bindingStatus": "action_needed", "impact": "moderate", "priority": "high", "milestone": 122, "cppMethod": "SkFontMgr::RefDefault", "affectedTypes": ["SKFontManager"], "notes": "SkiaSharp's SKFontManager.Default uses a platform-specific factory under the hood; verify no path remains that calls RefDefault on the C++ side.", "userValue": "If we still call into the removed API, SkiaSharp will break on the next Skia bump.", "skillToUse": "issue-fix"}, {"name": "SkFont::getTypeface no longer returns null", "category": "font", "description": "getTypeface() now returns an empty typeface instead of null when none was set. Removes a common nullability footgun.", "source": "release-notes", "changeType": "changed", "importance": "minor", "bindingStatus": "full", "impact": "minor", "priority": "low", "milestone": 122}, {"name": "SkImageFilters::Crop", "category": "image_filter", "description": "New Crop(SkRect, SkTileMode, sk_sp<SkImageFilter>) image filter that crops the wrapped filter's output and applies a tile mode when sampling outside the crop.", "source": "release-notes", "changeType": "added", "importance": "major", "bindingStatus": "missing", "impact": "significant", "priority": "high", "effort": "small", "milestone": 119, "cppMethod": "SkImageFilters::Crop", "affectedTypes": ["SKImageFilter"], "userValue": "Compose tiled/clamped/repeating image-filter effects with explicit crop bounds.", "slideBullet": "🖼️ **SKImageFilter.CreateCrop** – Crop a filter graph with a tile mode, enabling repeating/clamped sub-effects.", "skillToUse": "api-add-review"}, {"name": "Path effects removed (Merge/Matrix/Stroke/StrokeAndFill)", "category": "path", "description": "SkMergePathEffect, SkMatrixPathEffect, SkStrokePathEffect, SkStrokeAndFillPathEffect removed from public API.", "source": "release-notes", "changeType": "removed", "importance": "patch", "bindingStatus": "correctly_absent", "impact": "minor", "priority": "low", "milestone": 119}, {"name": "SkTiledImageUtils for large bitmaps", "category": "utility", "description": "New SkTiledImageUtils namespace provides DrawImage / DrawImageRect that tile bitmap-backed SkImages too large for a single GPU upload.", "source": "release-notes", "changeType": "added", "importance": "major", "bindingStatus": "missing", "impact": "significant", "priority": "medium", "effort": "medium", "milestone": 117, "cppHeader": "include/utils/SkTiledImageUtils.h", "affectedTypes": ["SKCanvas", "SKImage"], "userValue": "Render very large bitmaps without manual tiling logic.", "slideBullet": "🖼️ **Tile huge images automatically** – SKTiledImageUtils.DrawImage handles bitmaps too big for a single GPU texture.", "skillToUse": "api-add-review"}, {"name": "SkCanvas::saveLayerAlphaf", "category": "canvas", "description": "Float-precision overload of saveLayerAlpha.", "source": "release-notes", "changeType": "added", "importance": "minor", "bindingStatus": "missing", "impact": "minor", "priority": "low", "effort": "trivial", "milestone": 117, "cppMethod": "SkCanvas::saveLayerAlphaf", "affectedTypes": ["SKCanvas"], "userValue": "Set layer alpha with float precision, matching modern rendering pipelines."}, {"name": "asyncRescaleAndReadPixelsYUVA420", "category": "image", "description": "Added on SkImage/SkSurface — same as the YUV420 variant but returns a fourth alpha plane at full resolution.", "source": "release-notes", "changeType": "added", "importance": "minor", "bindingStatus": "missing", "impact": "moderate", "priority": "low", "effort": "medium", "milestone": 117, "cppMethod": "SkImage::asyncRescaleAndReadPixelsYUVA420", "affectedTypes": ["SKImage", "SKSurface"]}, {"name": "SkColorTable for SkColorFilters::Table", "category": "color", "description": "New public SkColorTable type owns the lookup tables passed into SkColorFilters::Table, allowing memory sharing.", "source": "release-notes", "changeType": "added", "importance": "minor", "bindingStatus": "missing", "impact": "moderate", "priority": "low", "effort": "small", "milestone": 116, "cppClass": "SkColorTable", "affectedTypes": ["SKColorFilter"]}, {"name": "SkImageFilters::AlphaThreshold removed", "category": "image_filter", "description": "AlphaThreshold removed; replace with Blend(kSrcIn, input, Picture(region)).", "source": "release-notes", "changeType": "removed", "importance": "breaking", "bindingStatus": "correctly_absent", "impact": "minor", "priority": "low", "milestone": 116}, {"name": "SkImageFilters::Image requires SkSamplingOptions", "category": "image_filter", "description": "Single-argument SkImageFilters::Image(sk_sp<SkImage>) factory removed; sampling options are now mandatory.", "source": "release-notes", "changeType": "removed", "importance": "breaking", "bindingStatus": "action_needed", "impact": "minor", "priority": "medium", "milestone": 116, "cppMethod": "SkImageFilters::Image", "notes": "Verify SkiaSharp's CreateImage overloads pass sampling options to the C API."}, {"name": "Codec registration model (SkCodecs::Register)", "category": "codec", "description": "Clients must register codecs they want Skia to use (SkJpegDecoder::Decoder() etc.). Legacy auto-registration is gated by SK_DISABLE_LEGACY_INIT_DECODERS for transition.", "source": "release-notes", "changeType": "changed", "importance": "breaking", "bindingStatus": "not_applicable", "impact": "moderate", "priority": "medium", "milestone": 115, "notes": "SkiaSharp build configuration must continue to register the desired decoders."}, {"name": "SkImage factories moved to SkImages namespace", "category": "image", "description": "SkImage::Make* factory functions migrated to SkImages:: namespace with renamed forms (DeferredFromEncodedData etc.). Bridge aliases temporary.", "source": "release-notes", "changeType": "changed", "importance": "patch", "bindingStatus": "full", "impact": "minor", "priority": "low", "milestone": 115, "notes": "C API shim adapted; no C# surface change."}, {"name": "Gradient interpolation color spaces and hue methods", "category": "shader", "description": "Gradient shaders accept SkGradientShader::Interpolation specifying the interpolation color space (sRGB linear, OKLab, OKLCh, HSL, HWB, …) and CSS-style hue method (shorter/longer/increasing/decreasing).", "source": "release-notes", "changeType": "added", "importance": "major", "bindingStatus": "missing", "impact": "significant", "priority": "high", "effort": "medium", "milestone": 114, "cppHeader": "include/effects/SkGradientShader.h", "affectedTypes": ["SKShader"], "userValue": "CSS Color Level 4 gradients — modern, perceptually-uniform color interpolation.", "slideBullet": "🌈 **Modern gradient interpolation** – CSS Color Level 4 color spaces (OKLab/OKLCh/HSL/HWB) and hue methods.", "skillToUse": "api-add-review"}, {"name": "SkImage namespace migration", "category": "image", "description": "SkImage::CompressionType renamed to SkTextureCompressionType; SkImage factories moved to SkImages namespace.", "source": "release-notes", "changeType": "changed", "importance": "patch", "bindingStatus": "full", "impact": "minor", "priority": "low", "milestone": 113}, {"name": "SkRuntimeColorFilterBuilder", "category": "shader", "description": "New helper class for building color filters from runtime effects, analogous to SkRuntimeShaderBuilder.", "source": "release-notes", "changeType": "added", "importance": "minor", "bindingStatus": "missing", "impact": "moderate", "priority": "medium", "effort": "small", "milestone": 112, "cppClass": "SkRuntimeColorFilterBuilder", "affectedTypes": ["SKRuntimeEffect"], "userValue": "Build runtime color filters as easily as runtime shaders.", "skillToUse": "api-add-review"}, {"name": "SkShaders::CoordClamp", "category": "shader", "description": "Wraps another shader and clamps the coordinates passed to it to a rectangle.", "source": "release-notes", "changeType": "added", "importance": "minor", "bindingStatus": "missing", "impact": "moderate", "priority": "medium", "effort": "trivial", "milestone": 112, "cppMethod": "SkShaders::CoordClamp", "affectedTypes": ["SKShader"], "userValue": "Constrain shader sampling to a sub-rectangle without using image filters.", "skillToUse": "api-add-review"}, {"name": "JPEG XMP metadata + YUVA encoding", "category": "codec", "description": "SkJpegEncoder::Options gains XMP metadata; SkJpegEncoder can encode SkYUVAPixmaps directly.", "source": "release-notes", "changeType": "added", "importance": "minor", "bindingStatus": "missing", "impact": "moderate", "priority": "low", "effort": "small", "milestone": 112, "affectedTypes": ["SKJpegEncoderOptions"]}, {"name": "SkToBool / SkBackingFit / SkMath removed from public API", "category": "utility", "description": "Several internal helpers (SkToBool, SkAbs32, SkAlign*, SkBackingFit, SkLeftShift, SkMath.h, SK_MaxS32) removed from the public API in m111.", "source": "release-notes", "changeType": "removed", "importance": "patch", "bindingStatus": "not_applicable", "impact": "minor", "priority": "low", "milestone": 111}, {"name": "SkParsePath::ToSVGString returns string", "category": "path", "description": "ToSVGString now returns a string rather than mutating an out parameter.", "source": "release-notes", "changeType": "changed", "importance": "patch", "bindingStatus": "full", "impact": "minor", "priority": "low", "milestone": 110}, {"name": "SkPaint::getFillPath replaced", "category": "path", "description": "Functionality moved to skpathutils::FillPathWithPaint in include/core/SkPathUtils.h.", "source": "release-notes", "changeType": "changed", "importance": "patch", "bindingStatus": "full", "impact": "minor", "priority": "low", "milestone": 110, "notes": "SkiaSharp continues to expose GetFillPath via SKPathEffect / SKPaint helpers."}, {"name": "SkImage::RescaleMode::kLinear", "category": "image", "description": "Single-pass linear rescale mode for async rescale/readback APIs (faster, lower quality than kRepeatedLinear).", "source": "release-notes", "changeType": "added", "importance": "minor", "bindingStatus": "missing", "impact": "minor", "priority": "low", "effort": "trivial", "milestone": 109, "affectedTypes": ["SKImage"]}, {"name": "Custom ICC profiles in encoders", "category": "codec", "description": "Support for specifying a custom ICC profile in SkJpegEncoder, SkPngEncoder and SkWebpEncoder.", "source": "release-notes", "changeType": "added", "importance": "minor", "bindingStatus": "action_needed", "impact": "moderate", "priority": "low", "milestone": 108, "milestoneRemoved": 142, "notes": "Added in m108; later REMOVED in m142 (fICCProfile fields dropped). Net binding gap is correctly absent."}, {"name": "Animated WebP encoding", "category": "codec", "description": "SkWebpEncoder gained support for encoding animated WebP images.", "source": "release-notes", "changeType": "added", "importance": "major", "bindingStatus": "missing", "impact": "significant", "priority": "medium", "effort": "medium", "milestone": 107, "cppClass": "SkWebpEncoder", "userValue": "Author animated WebP files (loops, intros, sticker animations).", "slideBullet": "🎞️ **Animated WebP encoding** – Skia can now write animated WebPs; bind it to enable authoring.", "skillToUse": "api-add-review"}, {"name": "SkMesh API for custom vertex/fragment programs", "category": "new_feature", "description": "New API to draw vertex meshes with custom attributes and varyings using SkSL. Mesh data created on a GrDirectContext to avoid re-uploads.", "source": "release-notes", "changeType": "added", "importance": "major", "bindingStatus": "missing", "impact": "transformative", "priority": "high", "effort": "large", "milestone": 106, "milestones": [106, 109, 110, 119, 120], "cppClass": "SkMesh", "affectedTypes": ["SKCanvas"], "userValue": "Custom 2D mesh rendering with SkSL vertex/fragment programs — unlocks 3D-ish effects, custom warping, GPU-accelerated visualizations.", "slideBullet": "🚀 **SkMesh custom vertex shaders** – Draw arbitrary meshes with SkSL programs; foundational for advanced 2D/2.5D effects.", "skillToUse": "api-add-review"}, {"name": "AVIF decoding via libavif", "category": "codec", "description": "Skia gained native AVIF decoding via libavif.", "source": "release-notes", "changeType": "added", "importance": "major", "bindingStatus": "partial", "impact": "significant", "priority": "high", "effort": "small", "milestone": 106, "affectedTypes": ["SKCodec"], "userValue": "Decode AVIF images natively (modern, smaller-than-JPEG/WebP, HDR-capable).", "notes": "SKEncodedImageFormat.Avif enum value exists; verify libavif is enabled in the SkiaSharp native build and that SKCodec.Create handles AVIF.", "slideBullet": "🖼️ **AVIF image support** – Decode modern AVIF images including HDR variants.", "skillToUse": "api-add-review"}, {"name": "SkColorFilters::Blend with SkColor4f + colorspace", "category": "color", "description": "New overload accepting SkColor4f and SkColorSpace.", "source": "release-notes", "changeType": "added", "importance": "minor", "bindingStatus": "missing", "impact": "moderate", "priority": "low", "effort": "small", "milestone": 106, "cppMethod": "SkColorFilters::Blend", "affectedTypes": ["SKColorFilter"]}, {"name": "SkBitmap::getColor4f / SkPixmap::getColor4f", "category": "color", "description": "New functions returning float color values directly.", "source": "release-notes", "changeType": "added", "importance": "minor", "bindingStatus": "missing", "impact": "moderate", "priority": "low", "effort": "trivial", "milestone": 104, "affectedTypes": ["SKBitmap", "SKPixmap"]}, {"name": "SkCanvas::getBaseProps / getTopProps", "category": "canvas", "description": "getBaseProps replaces deprecated getProps; getTopProps returns surface props of the active layer.", "source": "release-notes", "changeType": "added", "importance": "minor", "bindingStatus": "missing", "impact": "minor", "priority": "low", "effort": "trivial", "milestone": 104, "affectedTypes": ["SKCanvas"]}, {"name": "Anisotropic filtering in SkSamplingOptions", "category": "performance", "description": "SkSamplingOptions gained anisotropic filtering support (GPU only).", "source": "release-notes", "changeType": "added", "importance": "major", "bindingStatus": "missing", "impact": "significant", "priority": "medium", "effort": "small", "milestone": 103, "affectedTypes": ["SKSamplingOptions"], "userValue": "Sharper textures at oblique angles in 3D-style draws.", "slideBullet": "🎮 **Anisotropic filtering** – Sharper textures at glancing angles for 3D-style scenes."}, {"name": "Skia requires C++17", "category": "dependency", "description": "Skia now requires C++17 standard library.", "source": "release-notes", "changeType": "dependency", "importance": "patch", "bindingStatus": "not_applicable", "impact": "minor", "priority": "low", "milestone": 100, "dependencyName": "C++ standard", "dependencyTo": "C++17"}, {"name": "SkSurface::resolveMSAA", "category": "utility", "description": "Force Skia to resolve MSAA draws (useful when wrapping client texture as resolve target).", "source": "release-notes", "changeType": "added", "importance": "minor", "bindingStatus": "missing", "impact": "moderate", "priority": "low", "effort": "trivial", "milestone": 100, "affectedTypes": ["SKSurface"]}, {"name": "toLinearSrgb / fromLinearSrgb intrinsics in SkSL", "category": "shader", "description": "Two new SkSL intrinsics convert vec3 between the working color space and linear sRGB. Crucial for color-correct lighting/effects in runtime shaders.", "source": "release-notes", "changeType": "added", "importance": "major", "bindingStatus": "not_applicable", "impact": "significant", "priority": "medium", "milestone": 99, "userValue": "Write color-correct runtime effects (lighting, etc.) regardless of destination color space.", "slideBullet": "🎨 **Color-managed runtime effects** – toLinearSrgb / fromLinearSrgb let SkSL effects do correct color math."}, {"name": "Multiple-child RuntimeShader image filter", "category": "image_filter", "description": "A new variant of SkImageFilters::RuntimeShader supports multiple child nodes.", "source": "release-notes", "changeType": "added", "importance": "minor", "bindingStatus": "missing", "impact": "moderate", "priority": "medium", "effort": "medium", "milestone": 99, "affectedTypes": ["SKImageFilter"]}, {"name": "SkImage::makeRawShader", "category": "shader", "description": "Creates 'raw' shaders for non-color image data (normals/material/heightmaps): no color-space conversion, no premultiply, no bicubic.", "source": "release-notes", "changeType": "added", "importance": "major", "bindingStatus": "full", "impact": "significant", "priority": "medium", "milestone": 98, "cppMethod": "SkImage::makeRawShader", "cApiFunction": "sk_image_make_raw_shader", "csharpMethod": "SKImage.ToRawShader", "csharpFile": "binding/SkiaSharp/SKImage.cs", "affectedTypes": ["SKImage", "SKShader"], "userValue": "Use images as data buffers in runtime shaders (heightmaps, normals, lookup tables).", "slideBullet": "🎨 **Raw image shaders** – SKImage.ToRawShader passes pixels through verbatim, perfect for SkSL data inputs.", "notes": "Verified bound: sk_image_make_raw_shader → SKImage.ToRawShader."}, {"name": "SkImageFilters::RuntimeShader", "category": "image_filter", "description": "Image filter variant of runtime effects, exposing SkSL effects in a filter graph.", "source": "release-notes", "changeType": "added", "importance": "major", "bindingStatus": "missing", "impact": "significant", "priority": "high", "effort": "medium", "milestone": 98, "cppMethod": "SkImageFilters::RuntimeShader", "affectedTypes": ["SKImageFilter", "SKRuntimeEffect"], "userValue": "Use SkSL runtime effects as image filters in a graph.", "slideBullet": "🎨 **Runtime-shader image filter** – Insert SkSL effects into image-filter graphs for advanced compositing.", "skillToUse": "api-add-review"}, {"name": "JPEG XL (limited) decoding", "category": "codec", "description": "Limited JPEG XL support added in Skia.", "source": "release-notes", "changeType": "added", "importance": "major", "bindingStatus": "partial", "impact": "significant", "priority": "medium", "effort": "medium", "milestone": 98, "affectedTypes": ["SKCodec"], "notes": "SKEncodedImageFormat.Jpegxl enum value exists; SkiaSharp native build does not currently link a JPEG XL decoder library.", "userValue": "Decode JPEG XL images (better compression than JPEG, modern HDR-friendly format).", "slideBullet": "🖼️ **JPEG XL decoding** – Modern next-gen image format with smaller files than JPEG."}, {"name": "2 GiB image/surface size cap", "category": "behavior_change", "description": "Surfaces and images now hard-capped at just under 2GB to avoid CPU indexing bugs.", "source": "release-notes", "changeType": "changed", "importance": "patch", "bindingStatus": "not_applicable", "impact": "minor", "priority": "low", "milestone": 98}, {"name": "AVIF support in SkHeifCodec", "category": "codec", "description": "HEIF codec gained AVIF support (Android NDK path).", "source": "release-notes", "changeType": "added", "importance": "minor", "bindingStatus": "not_applicable", "impact": "minor", "priority": "low", "milestone": 88, "notes": "See m106 for the libavif-based decoder."}, {"name": "SkImageFilters::Blend factory", "category": "image_filter", "description": "SkImageFilters::Blend replaces deprecated SkImageFilters::Xfermode with matching naming.", "source": "release-notes", "changeType": "added", "importance": "minor", "bindingStatus": "full", "impact": "minor", "priority": "low", "milestone": 88, "cppMethod": "SkImageFilters::Blend", "csharpMethod": "SKImageFilter.CreateBlendMode"}, {"name": "GrContext split into GrDirectContext / GrRecordingContext", "category": "behavior_change", "description": "GrContext was replaced by GrDirectContext (full functionality) and GrRecordingContext (DDL playback).", "source": "release-notes", "changeType": "changed", "importance": "breaking", "bindingStatus": "full", "impact": "moderate", "priority": "low", "milestone": 88, "notes": "SkiaSharp already exposes GRContext / GRRecordingContext."}, {"name": "SKPathBuilder is now the recommended path-building API", "category": "path", "description": "SkiaSharp m147 work added SKPathBuilder bindings ahead of SkPath becoming immutable upstream (m143). Many SKPath mutating methods are now [Obsolete] in the C# wrapper.", "source": "binding-audit", "changeType": "added", "importance": "major", "bindingStatus": "full", "impact": "significant", "priority": "medium", "milestone": 147, "affectedTypes": ["SKPath", "SKPathBuilder"], "csharpFile": "binding/SkiaSharp/SKPathBuilder.cs", "userValue": "Forward-compatible path construction: write code that survives SkPath becoming immutable.", "slideBullet": "🛤️ **SKPathBuilder ships in 3.120** – Start migrating SKPath construction now to be ready when SkPath turns immutable."}, {"name": "SaveLayerRec missing colorSpace and backdropTileMode", "category": "canvas", "description": "C API sk_canvas_savelayerrec_t struct lacks the backdropTileMode (m131) and color-space (m120-era) fields. Limits the quality of backdrop blur effects.", "source": "header-scan", "changeType": "added", "importance": "major", "bindingStatus": "partial", "impact": "significant", "priority": "high", "effort": "small", "milestone": 131, "cppClass": "SkCanvas::SaveLayerRec", "cApiFunction": "sk_canvas_save_layer_rec", "csharpMethod": "SKCanvas.SaveLayer(in SKCanvasSaveLayerRec)", "csharpFile": "binding/SkiaSharp/SKCanvas.cs", "notes": "Verified header sk_types.h: sk_canvas_savelayerrec_t has only fBounds/fPaint/fBackdrop/fFlags. Need to add fBackdropTileMode and (if appropriate) fColorSpace.", "skillToUse": "api-add-review"}, {"name": "Lighting image filters: 4 separate factories already bound", "category": "image_filter", "description": "DistantLitDiffuse/Specular, PointLitDiffuse/Specular, SpotLitDiffuse/Specular all have C API entries.", "source": "header-scan", "changeType": "added", "importance": "patch", "bindingStatus": "full", "impact": "minor", "priority": "low", "cppMethod": "SkImageFilters::DistantLitDiffuse", "notes": "Confirmed in include/c/sk_imagefilter.h (sk_imagefilter_new_distant_lit_diffuse etc.)."}, {"name": "Texture wrapping infers color type", "category": "image", "description": "Upstream m148 removes the need to specify an SkColorType when wrapping textures; Skia now infers a compatible type and can force opacity with kUnknown alpha.", "source": "release-notes", "changeType": "changed", "importance": "major", "bindingStatus": "not_applicable", "impact": "significant", "priority": "high", "milestone": 148, "affectedTypes": ["SKImage", "SKSurface"], "cppHeader": "include/core/SkImage.h", "skiaFeature": "SkImages::WrapTexture / SkSurfaces::WrapBackendTexture auto color-type selection", "userValue": "Native texture interop gets simpler and less error-prone when SkiaSharp moves to M148.", "notes": "Verified from RELEASE_NOTES.md milestone 148; this is an engine/API behavior change, not a C API gap in the audited trees.", "slideBullet": "🖼️ **Smarter texture wrap** - Backend textures no longer need manual color-type guesses when wrapping into Skia objects."}, {"name": "SkCodec::getEncodedData removal", "category": "codec", "description": "Upstream m147 removed SkCodec::getEncodedData() from the public API. Grepping the audited C API and C# trees found no matching wrapper, so SkiaSharp is correctly absent rather than behind.", "source": "release-notes", "changeType": "removed", "importance": "breaking", "bindingStatus": "correctly_absent", "impact": "minor", "priority": "low", "milestone": 147, "cppClass": "SkCodec", "cppHeader": "include/codec/SkCodec.h", "cppMethod": "SkCodec::getEncodedData()", "cApiFile": "externals/skia/include/c/sk_codec.h", "csharpFile": "binding/SkiaSharp/SKCodec.cs", "userValue": "Confirms there is no SkiaSharp cleanup work hidden behind this upstream removal.", "notes": "Verified absence with grep in /externals/skia/include/c/sk_codec.h, /externals/skia/src/c/sk_codec.cpp, and binding/SkiaSharp/SKCodec.cs.", "slideBullet": "🐛 **No codec cleanup debt** - A removed upstream codec API does not leave SkiaSharp with a stale wrapper.", "migrationGuide": "Before:\n```cpp\nauto encoded = codec->getEncodedData();\n```\nAfter:\n```cpp\nauto original = SkData::MakeFromFileName(path);\nauto codec = SkCodec::MakeFromData(original, nullptr);\n```"}, {"name": "Codec decode-memory budget", "category": "security", "description": "SkCodec::Options gained fMaxDecodeMemory and kOutOfMemory in m145, but the audited C API exposes only zero-init/subset/frame/prior options and the C# SKCodecOptions surface mirrors that older shape.", "source": "header-scan", "changeType": "added", "importance": "major", "bindingStatus": "missing", "impact": "significant", "priority": "high", "effort": "medium", "milestone": 145, "cppClass": "SkCodec", "cppHeader": "include/codec/SkCodec.h", "cppMethod": "SkCodec::Options::fMaxDecodeMemory / SkCodec::Result::kOutOfMemory", "cApiFile": "externals/skia/include/c/sk_types.h", "csharpFile": "binding/SkiaSharp/SKCodec.cs", "labels": ["codec", "memory-safety", "dos-resistance"], "userValue": "Lets apps cap worst-case decode memory and fail predictably on hostile or oversized images.", "notes": "Verified in upstream SkCodec.h; local sk_codec_options_t in sk_types.h only has fZeroInitialized, fSubset, fFrameIndex, and fPriorFrame, and SKCodec.cs builds the same reduced option set.", "slideBullet": "🔒 **Safer image decode** - Apps could cap decode memory instead of risking large-image allocation spikes.", "skillToUse": "api-add-review"}, {"name": "Shader working-color-space override", "category": "shader", "description": "SkShader::makeWithWorkingColorSpace gained the output-color-space override noted in m141, but no matching C API entry point or C# shader wrapper exists in the audited trees.", "source": "header-scan", "changeType": "added", "importance": "major", "bindingStatus": "missing", "impact": "significant", "priority": "high", "effort": "medium", "milestone": 141, "cppClass": "SkShader", "cppHeader": "include/core/SkShader.h", "cppMethod": "SkShader::makeWithWorkingColorSpace(sk_sp<SkColorSpace> inputCS, sk_sp<SkColorSpace> outputCS=nullptr)", "cApiFile": "externals/skia/include/c/sk_shader.h", "csharpFile": "binding/SkiaSharp/SKShader.cs", "labels": ["shader", "color-management"], "userValue": "Allows custom shaders to participate correctly in color-space conversion instead of assuming destination-space output.", "notes": "Verified upstream makeWithWorkingColorSpace() in SkShader.h lines ~70-90. No working-color-space symbol exists in sk_shader.h/.cpp or SKShader.cs.", "slideBullet": "🔤 **Color-managed shaders** - Custom shaders could declare their working and output color spaces instead of relying on implicit destination-space behavior.", "skillToUse": "api-add-review"}, {"name": "Standalone Crop image filter", "category": "image_filter", "description": "SkImageFilters::Crop was introduced in m119, but the audited C API has no crop factory and SKImageFilter.cs has no CreateCrop-style wrapper.", "source": "header-scan", "changeType": "added", "importance": "minor", "bindingStatus": "missing", "impact": "moderate", "priority": "medium", "effort": "small", "milestone": 119, "cppClass": "SkImageFilters", "cppHeader": "include/effects/SkImageFilters.h", "cppMethod": "SkImageFilters::Crop(const SkRect&, SkTileMode, sk_sp<SkImageFilter>)", "cApiFile": "externals/skia/include/c/sk_imagefilter.h", "csharpFile": "binding/SkiaSharp/SKImageFilter.cs", "userValue": "Gives filter graphs an explicit crop node with tile-mode control instead of overloading cropRect on unrelated factories.", "notes": "Verified upstream Crop() in SkImageFilters.h lines ~149-168. No crop symbol exists in sk_imagefilter.h/.cpp, and no Crop factory exists in SKImageFilter.cs.", "skillToUse": "api-add-review"}, {"name": "YUVA420 async readback with alpha", "category": "image", "description": "Upstream m117 added asyncRescaleAndReadPixelsYUVA420 on both SkImage and SkSurface, but the audited C API and C# bindings expose no YUV420/YUVA420 async readback entry points.", "source": "header-scan", "changeType": "added", "importance": "major", "bindingStatus": "missing", "impact": "significant", "priority": "high", "effort": "large", "milestone": 117, "cppClass": "SkImage / SkSurface", "cppHeader": "include/core/SkImage.h; include/core/SkSurface.h", "cppMethod": "asyncRescaleAndReadPixelsYUVA420(...)", "cApiFile": "externals/skia/include/c/sk_image.h; externals/skia/include/c/sk_surface.h", "csharpFile": "binding/SkiaSharp/SKImage.cs; binding/SkiaSharp/SKSurface.cs", "labels": ["readback", "video", "yuv"], "userValue": "Important for video, camera, and encoder pipelines that need GPU readback including alpha without an RGBA round-trip.", "notes": "Verified upstream methods in SkImage.h and SkSurface.h. Grep found no async rescale/read YUV420 or YUVA420 symbols in the audited C API or C# wrapper files.", "slideBullet": "🚀 **Better video readback** - GPU content could be read back as YUVA420, including alpha, for encoder and camera workflows.", "skillToUse": "api-add-review"}, {"name": "Wide-gamut color-filter blend factory", "category": "color", "description": "SkColorFilters::Blend(const SkColor4f&, sk_sp<SkColorSpace>, SkBlendMode) is public upstream, but the audited C API has only sk_colorfilter_new_mode(sk_color_t, ...) and SKColorFilter exposes only CreateBlendMode(SKColor, ...).", "source": "header-scan", "changeType": "added", "importance": "major", "bindingStatus": "missing", "impact": "significant", "priority": "high", "effort": "medium", "milestone": 106, "cppClass": "SkColorFilters", "cppHeader": "include/core/SkColorFilter.h", "cppMethod": "SkColorFilters::Blend(const SkColor4f&, sk_sp<SkColorSpace>, SkBlendMode)", "cApiFile": "externals/skia/include/c/sk_colorfilter.h", "csharpFile": "binding/SkiaSharp/SKColorFilter.cs", "labels": ["hdr", "wide-gamut", "color-filters"], "userValue": "Enables color-filter math in explicit color spaces instead of forcing 8-bit SKColor inputs.", "notes": "Verified upstream Blend() overloads in SkColorFilter.h lines ~90-93. Local sk_colorfilter.h/.cpp and SKColorFilter.cs only expose the 8-bit SKColor form.", "slideBullet": "🎨 **Wide-gamut blend filters** - Color filters could blend in explicit color spaces for HDR-aware rendering paths.", "skillToUse": "api-add-review"}, {"name": "C++17 / iOS 11 baseline", "category": "platform", "description": "Upstream m100 raised the public baseline to C++17 and iOS 11, which matters for SkiaSharp native dependency maintenance and custom native embedders.", "source": "release-notes", "changeType": "platform", "importance": "breaking", "bindingStatus": "not_applicable", "impact": "moderate", "priority": "low", "milestone": 100, "platforms": ["iOS", "native build"], "userValue": "Sets the floor for native consumers and future Skia bumps.", "notes": "Verified from RELEASE_NOTES.md milestone 100. No C API or C# binding action is required.", "slideBullet": "🌐 **New native baseline** - Future Skia bumps assume C++17-era toolchains and newer Apple deployment targets.", "migrationGuide": "Before:\n```cmake\nset(CMAKE_CXX_STANDARD 14)\n```\nAfter:\n```cmake\nset(CMAKE_CXX_STANDARD 17)\n```"}, {"name": "RuntimeShader image filters", "category": "image_filter", "description": "SkImageFilters::RuntimeShader exists upstream with single-child, multi-child, and sample-radius variants, but no matching sk_imagefilter_* runtime entry points or SKImageFilter runtime factory exist in the audited trees.", "source": "header-scan", "changeType": "added", "importance": "major", "bindingStatus": "missing", "impact": "transformative", "priority": "high", "effort": "large", "milestone": 98, "milestones": [98, 99, 116], "cppClass": "SkImageFilters", "cppHeader": "include/effects/SkImageFilters.h", "cppMethod": "SkImageFilters::RuntimeShader(...)", "cApiFile": "externals/skia/include/c/sk_imagefilter.h", "csharpFile": "binding/SkiaSharp/SKImageFilter.cs", "labels": ["runtime-effects", "image-filters", "skia-sksl"], "userValue": "Unlocks custom programmable image-filter graphs driven by SkSL instead of only shader or paint stages.", "notes": "Verified upstream RuntimeShader overloads in SkImageFilters.h lines ~402-462. No runtime shader functions were found in sk_imagefilter.h/.cpp, and no runtime factory was found in SKImageFilter.cs.", "slideBullet": "🎨 **Programmable image filters** - SkSL runtime effects could plug directly into filter graphs for custom blur, warp, and compositing pipelines.", "skillToUse": "api-add-review"}, {"name": "Bitmap color-space reassignment", "category": "color", "description": "SkBitmap::setColorSpace landed in m125, but the audited C API exposes no bitmap colorspace mutator and SKBitmap only reports ColorSpace via Info.", "source": "header-scan", "changeType": "added", "importance": "minor", "bindingStatus": "missing", "impact": "moderate", "priority": "medium", "effort": "small", "milestone": 125, "cppClass": "SkBitmap", "cppHeader": "include/core/SkBitmap.h", "cppMethod": "SkBitmap::setColorSpace(sk_sp<SkColorSpace>)", "cApiFile": "externals/skia/include/c/sk_bitmap.h", "csharpFile": "binding/SkiaSharp/SKBitmap.cs", "userValue": "Allows reinterpretation of existing bitmap pixel data in a new color space without rebuilding the bitmap.", "notes": "Verified upstream setColorSpace() in SkBitmap.h. No setColorSpace symbol appears in sk_bitmap.h/.cpp, and SKBitmap.cs only exposes a read-only ColorSpace derived from Info.", "skillToUse": "api-add-review"}, {"name": "Wide-gamut drop-shadow overloads", "category": "image_filter", "description": "Since m129, SkImageFilters::DropShadow and DropShadowOnly accept SkColor4f plus SkColorSpace, but the audited C API and SKImageFilter wrappers still expose only SKColor-based overloads.", "source": "header-scan", "changeType": "changed", "importance": "major", "bindingStatus": "missing", "impact": "significant", "priority": "high", "effort": "medium", "milestone": 129, "cppClass": "SkImageFilters", "cppHeader": "include/effects/SkImageFilters.h", "cppMethod": "DropShadow(..., SkColor4f, sk_sp<SkColorSpace>, ...) / DropShadowOnly(...)", "cApiFile": "externals/skia/include/c/sk_imagefilter.h", "csharpFile": "binding/SkiaSharp/SKImageFilter.cs", "labels": ["hdr", "wide-gamut", "image-filters"], "userValue": "Needed for HDR and wide-gamut shadow colors without truncating to 8-bit sRGB SKColor.", "notes": "Verified color-aware overloads in upstream SkImageFilters.h lines ~201-243. Local sk_imagefilter.h/.cpp and SKImageFilter.cs only expose SKColor overloads.", "slideBullet": "🌈 **HDR shadow colors** - Drop shadows could preserve wide-gamut and HDR color values instead of collapsing to 8-bit colors.", "skillToUse": "api-add-review"}], "slides": "# SkiaSharp on Skia M148 — Highlights\n\n## 🎨 Color & HDR\n- 🌈 **AGTM HDR tone mapping** (m145) — render HDR images correctly on SDR displays.\n- 🌈 **HDR PNG metadata** (m142) — round-trip mastering metadata in PNGs.\n- 🎨 **More accurate Rec.709 colors** (m142) — BT.1886 gamma 2.4.\n- 🌈 **Better HDR HLG/PQ math** (m141) — automatic via SKColorSpace.CreateCicp.\n- 🎬 **CICP color spaces** (m133) — already bound as SKColorSpace.CreateCicp.\n\n## 🖼️ Image & Codec\n- 🖼️ **JPEG XL decoding** (m98) — partial; native build needs libjxl link.\n- 🖼️ **AVIF image support** (m106) — partial; verify libavif in build.\n- 🎞️ **Animated WebP encoding** (m107) — author animated WebP files.\n- 🛡️ **Decode memory limits** (m145) — defend against decompression bombs.\n- 🔄 **EXIF-correct JPEG decoding** (m123) — automatic upstream benefit.\n- 🖼️ **SKImage.MakeScaled** (m128) — one-call high-quality rescaling.\n- 🖼️ **Tile huge images automatically** (m117) — SKTiledImageUtils.\n\n## 🎨 Shaders & Filters\n- 🌈 **Modern gradient interpolation** (m114) — OKLab/OKLCh/HSL/HWB color spaces.\n- 🎨 **Raw image shaders** (m98) — already bound as SKImage.ToRawShader.\n- 🎨 **Color-managed runtime effects** (m99) — toLinearSrgb / fromLinearSrgb.\n- 🎨 **Runtime-shader image filter** (m98) — SkSL effects in filter graphs.\n- 🖼️ **SKImageFilter.CreateCrop** (m119) — crop a filter graph with tile mode.\n- 🪟 **Backdrop tile mode for blurs** (m131) — clean frosted-glass edges.\n- 🎨 **Wide-gamut blend filters** (m106) — blend color filters in explicit color spaces for HDR workflows.\n- 🔤 **Color-managed shaders** (m141) — declare shader working and output color spaces explicitly.\n- 🌈 **HDR shadow colors** (m129) — preserve wide-gamut shadow colors instead of truncating to 8-bit sRGB.\n\n## 🚀 Big New APIs\n- 🚀 **SkMesh custom vertex shaders** (m106) — arbitrary meshes with SkSL.\n- 🛤️ **SKPathBuilder ships in 3.120** — forward-compatible path API.\n- 🎮 **Anisotropic filtering** (m103) — sharper oblique-angle textures.\n\n## 🛡️ Reliability\n- 🛡️ **Vulkan device-lost diagnostics** (m123) — diagnose driver crashes.\n", "changelog": "# SkiaSharp Skia Changelog (M88 → M148)\n\n## ⚠️ Breaking Changes\n- **m143** SkPath migrating to immutable geometry — SkiaSharp wraps both via SKPathBuilder.\n- **m139** iOS 12 / macOS 10.15 / pre-Vulkan-1.1 dropped upstream.\n- **m130** SkColorFilter::filterColor removed (use filterColor4f).\n- **m122** SkFontMgr::RefDefault deleted.\n- **m116** SkImageFilters::AlphaThreshold and single-arg Image factory removed.\n- **m115** Codec auto-registration now opt-in (transitional).\n- **m88** GrContext split into GrDirectContext / GrRecordingContext.\n\n## ✨ New APIs\n- m147: SKPathBuilder bindings landed in SkiaSharp 3.120 work.\n- m145: skhdr::Agtm, SkCodec::Options::fMaxDecodeMemory, SkData::Equals.\n- m144: kR16_unorm color type (bound as SKColorType.R16Unorm).\n- m142: HDR PNG metadata, skhdr::Metadata, SkPngRustEncoder/Decoder go public.\n- m141: VulkanPreferredFeatures, SkShader::makeWithWorkingColorSpace output color space, shader color-space override.\n- m139: VulkanPreferredFeatures helper.\n- m135: SkCodec::isAnimated.\n- m133: SkColorSpace::MakeCICP (bound as SKColorSpace.CreateCicp).\n- m132: SkCodec::hasHighBitDepthEncodedData.\n- m131: SaveLayerRec backdrop tile mode.\n- m129: wide-gamut DropShadow / DropShadowOnly overloads.\n- m128: SkImage::makeScaled.\n- m125: SkBitmap::setColorSpace.\n- m124: SkCodecs::DeferredImage.\n- m123: Vulkan VK_EXT_device_fault callback.\n- m119: SkImageFilters::Crop.\n- m117: SkTiledImageUtils, asyncRescaleAndReadPixelsYUVA420, saveLayerAlphaf.\n- m116: SkColorTable.\n- m114: Gradient color-space interpolation (CSS Color L4).\n- m112: SkRuntimeColorFilterBuilder, SkShaders::CoordClamp.\n- m107: Animated WebP encoding.\n- m106: SkMesh API, AVIF decoding (libavif), SkColorFilters::Blend(SkColor4f, …).\n- m104: getColor4f, getBaseProps/getTopProps.\n- m103: Anisotropic filtering.\n- m100: SkSurface::resolveMSAA.\n- m99: toLinearSrgb / fromLinearSrgb SkSL intrinsics, multi-child RuntimeShader filter.\n- m98: SkImage::makeRawShader (bound as SKImage.ToRawShader), SkImageFilters::RuntimeShader, JPEG XL.\n\n## 🐛 Bug Fixes / Engine Improvements (Upstream Benefits)\n- RuntimeShader filter family still missing in SkiaSharp despite upstream growth through m116.\n- m142 Rec.709 BT.1886 transfer; m141 HLG/PQ skcms representations.\n- m131 mipmap sharpening default-on.\n- m124 Perlin noise rotation correctness + raster speedup.\n- m123 SkCodec::getImage respects EXIF orientation.\n\n## 🔒 Security / Reliability\n- m145 fMaxDecodeMemory decode-memory cap.\n- m143/m142 SkPngRustDecoder/Encoder graduate (memory-safe option).\n- m123 Vulkan device-lost diagnostics.\n\n## 📦 Dependencies / Platform\n- m139 dng_sdk 1.7.1, Vulkan 1.1 minimum, iOS12/macOS10.15 dropped.\n- m100 C++17 minimum.\n\n## 🛤️ Path\n- m146 SkPathBuilder accepted by more path utilities.\n- m143 SkPath migrating to immutable; SkPathBuilder is the way.\n- m141 SkPath::asArc removed.\n- m132 SkPathEffect dash introspection removed.\n- m119 Merge/Matrix/Stroke/StrokeAndFill path effects removed.\n- m110 SkPaint::getFillPath → skpathutils::FillPathWithPaint.\n", "nextSteps": [{"severity": "critical", "action": "Audit any remaining SkColorFilter::filterColor (deprecated m124, removed m130) call sites in C shim and migrate to filterColor4f.", "reason": "Will break on next Skia bump if still used.", "skillToUse": "issue-fix", "effort": "small"}, {"severity": "critical", "action": "Audit SkFontMgr::RefDefault usage in SkiaSharp's font manager initialization (removed m122).", "reason": "Will break on next Skia bump if still used.", "skillToUse": "issue-fix", "effort": "small"}, {"severity": "high", "action": "Add fBackdropTileMode (m131) to sk_canvas_savelayerrec_t and SKCanvasSaveLayerRec.", "reason": "Major quality win for backdrop blurs; small effort.", "milestone": 131, "skillToUse": "api-add-review", "effort": "small"}, {"severity": "high", "action": "Bind SkImageFilters::Crop (m119) as SKImageFilter.CreateCrop.", "reason": "Enables tiled/clamped sub-effects in filter graphs.", "milestone": 119, "skillToUse": "api-add-review", "effort": "small"}, {"severity": "high", "action": "Bind SkImage::makeScaled (m128) as SKImage.MakeScaled.", "reason": "One-call high-quality image rescaling.", "milestone": 128, "skillToUse": "api-add-review", "effort": "small"}, {"severity": "high", "action": "Bind SkCodec::Options::fMaxDecodeMemory (m145).", "reason": "Hardening against decode-bomb attacks.", "milestone": 145, "skillToUse": "api-add-review", "effort": "small"}, {"severity": "high", "action": "Verify libavif is enabled in SkiaSharp's native build so SKEncodedImageFormat.Avif decodes work.", "reason": "Enum exists but the codec library may not be linked.", "milestone": 106, "skillToUse": "native-dependency-update", "effort": "medium"}, {"severity": "high", "action": "Bind gradient interpolation color spaces / hue methods (m114).", "reason": "Modern CSS-style gradients.", "milestone": 114, "skillToUse": "api-add-review", "effort": "medium"}, {"severity": "medium", "action": "Bind SkImageFilters::RuntimeShader (m98).", "reason": "Lets SkSL effects participate in image-filter graphs.", "milestone": 98, "skillToUse": "api-add-review", "effort": "medium"}, {"severity": "medium", "action": "Bind SkCodec::isAnimated (m135) and hasHighBitDepthEncodedData (m132).", "reason": "Trivial codec metadata wins.", "skillToUse": "api-add-review", "effort": "trivial"}, {"severity": "medium", "action": "Bind SkTiledImageUtils (m117) as SKTiledImageUtils.DrawImage / DrawImageRect.", "reason": "Handles >max-texture-size bitmaps automatically.", "milestone": 117, "skillToUse": "api-add-review", "effort": "medium"}, {"severity": "medium", "action": "Bind SkRuntimeColorFilterBuilder (m112) and SkShaders::CoordClamp (m112).", "reason": "Useful runtime/shader helpers.", "milestone": 112, "skillToUse": "api-add-review", "effort": "small"}, {"severity": "medium", "action": "Bind animated WebP encoding (m107).", "reason": "Authoring animated WebPs.", "milestone": 107, "skillToUse": "api-add-review", "effort": "medium"}, {"severity": "medium", "action": "Investigate SkMesh API binding (m106).", "reason": "Transformative new feature; large effort.", "milestone": 106, "skillToUse": "api-add-review", "effort": "large"}, {"severity": "medium", "action": "Update platform-support docs to reflect Skia's m139 minimums (iOS 12+, macOS 10.15+ dropped, Vulkan 1.1).", "reason": "Customer-facing platform matrix.", "milestone": 139, "skillToUse": null, "effort": "small"}, {"severity": "low", "action": "Add SkData::Equals, SkColorSpacePrimaries::operator==, getColor4f helpers, getBaseProps/getTopProps.", "reason": "Small completeness wins.", "skillToUse": "api-add-review", "effort": "trivial"}, {"severity": "low", "action": "Bind SkSurface::resolveMSAA (m100).", "reason": "Useful when wrapping client textures as resolve targets.", "milestone": 100, "skillToUse": "api-add-review", "effort": "trivial"}, {"severity": "low", "action": "Investigate SkPngRustEncoder/Decoder (m142 public).", "reason": "Memory-safe PNG path; build-flag decision.", "milestone": 142, "skillToUse": "native-dependency-update", "effort": "large"}, {"severity": "high", "action": "Add wide-gamut overloads for DropShadow / DropShadowOnly and SKColorFilter blend.", "reason": "Completes HDR and wide-gamut color workflows that still down-convert to SKColor.", "milestone": 129, "skillToUse": "api-add-review", "effort": "medium"}, {"severity": "medium", "action": "Expose SkShader::makeWithWorkingColorSpace(inputCS, outputCS) and SkBitmap::setColorSpace.", "reason": "Adds missing color-management APIs verified in the upstream headers but absent in both C API and C#.", "milestone": 141, "skillToUse": "api-add-review", "effort": "medium"}]};
const CTL={added:'✨ Added',changed:'🔄 Changed',fixed:'🐛 Fixed',deprecated:'⚠️ Deprecated',removed:'❌ Removed',dependency:'📦 Dep',platform:'🌐 Platform',upstream:'🚀 Engine'};
const IL={breaking:'⚠️ Breaking',major:'Major',minor:'Minor',patch:'Patch'};
const SL={full:'✅ Full',partial:'🟡 Partial',missing:'❌ Missing',action_needed:'⚠️ Action',not_applicable:'N/A',correctly_absent:'N/A'};
const IML={transformative:'🚀 Transformative',significant:'⭐ Significant',moderate:'📦 Moderate',minor:'🔹 Minor'};
const PL={critical:'🔴 Critical',high:'🟠 High',medium:'🟡 Medium',low:'⚪ Low'};
const CL={new_feature:'Feature',codec:'Codec',image:'Image',image_filter:'Filter',shader:'Shader',color:'Color',canvas:'Canvas',path:'Path',font:'Font',utility:'Utility',performance:'Perf',behavior_change:'Behavior',deprecation:'Deprecated',security:'Security',platform:'Platform',dependency:'Dep'};
const IO={transformative:0,significant:1,moderate:2,minor:3};
const PO={critical:0,high:1,medium:2,low:3};
const IMO={breaking:0,major:1,minor:2,patch:3};
const SO={missing:0,action_needed:1,partial:2,full:3,not_applicable:4,correctly_absent:5};
const CTO={removed:0,added:1,changed:2,fixed:3,upstream:4,deprecated:5,dependency:6,platform:7};
const EO={trivial:4,small:3,medium:2,large:1};
let fF={impact:new Set(),priority:new Set(),bindingStatus:new Set(),changeType:new Set(),importance:new Set(),category:new Set()};
function esc(s){const d=document.createElement('div');d.textContent=s||'';return d.innerHTML;}
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);rebuild();}
function passesFilter(f){const sT=document.getElementById('search').value.toLowerCase();for(const[k,s]of Object.entries(fF)){if(s.size&&!s.has(f[k]))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'action':const sa=(4-(IO[a.impact]??3))*(4-(PO[a.priority]??3))*(EO[a.effort]||2);const sb=(4-(IO[b.impact]??3))*(4-(PO[b.priority]??3))*(EO[b.effort]||2);r=sb-sa;break;case'impact':r=(IO[a.impact]??9)-(IO[b.impact]??9);break;case'priority':r=(PO[a.priority]??9)-(PO[b.priority]??9);break;case'importance':r=(IMO[a.importance]??9)-(IMO[b.importance]??9);break;case'name':r=(a.name||'').localeCompare(b.name||'');break;}return r*dir;});}
function groupKey(f,by){return f[by]||'unknown';}
function groupLabel(key,by){switch(by){case'impact':return IML[key]||key;case'priority':return PL[key]||key;case'bindingStatus':return SL[key]||key;case'changeType':return CTL[key]||key;case'importance':return IL[key]||key;case'category':return CL[key]||key;default:return key;}}
function groupOrder(key,by){switch(by){case'impact':return IO[key]??9;case'priority':return PO[key]??9;case'bindingStatus':return SO[key]??9;case'changeType':return CTO[key]??9;case'importance':return IMO[key]??9;default:return 0;}}
function rebuild(){const filtered=D.findings.filter(passesFilter);const sorted=sortFindings(filtered);document.getElementById('filter-count').textContent=`Showing ${filtered.length} of ${D.findings.length}`;const by=document.getElementById('group-by').value;const el=document.getElementById('list');if(by==='none'){el.innerHTML=sorted.map(renderRow).join('');updateDim();return;}const groups={};sorted.forEach(f=>{const k=groupKey(f,by);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('');updateDim();}
function updateDim(){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 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 imp-${f.impact}" data-filter="impact:${f.impact}" onclick="event.stopPropagation();tF('impact','${f.impact}')">${IML[f.impact]||f.impact}</span>`;const bs=`<span class="pill s-${f.bindingStatus}" data-filter="bindingStatus:${f.bindingStatus}" onclick="event.stopPropagation();tF('bindingStatus','${f.bindingStatus}')">${SL[f.bindingStatus]||f.bindingStatus}</span>`;const pri=`<span class="pill p-${f.priority}" data-filter="priority:${f.priority}" onclick="event.stopPropagation();tF('priority','${f.priority}')">${f.priority}</span>`;const prLink=f.pr?` <a class="pr-link" href="${f.prUrl||'https://github.com/mono/SkiaSharp/pull/'+f.pr}" target="_blank">#${f.pr}</a>`:'';
let det='<div class="detail-grid">';if(f.userValue)det+=`<div class="detail-label">Value</div><div class="detail-value">${esc(f.userValue)}</div>`;if(f.skiaApi)det+=`<div class="detail-label">Skia API</div><div class="detail-value"><code>${esc(f.skiaApi)}</code></div>`;if(f.cppClass)det+=`<div class="detail-label">C++</div><div class="detail-value"><code>${esc(f.cppClass)}::${esc(f.cppMethod||'')}</code></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.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.dependencyName)det+=`<div class="detail-label">Dep</div><div class="detail-value"><code>${esc(f.dependencyName)}</code>: ${esc(f.dependencyFrom||'?')}${esc(f.dependencyTo||'?')}</div>`;if(f.effort)det+=`<div class="detail-label">Effort</div><div class="detail-value">${f.effort}</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.replacement)det+=`<div class="detail-label">Replace</div><div class="detail-value"><code>${esc(f.replacement)}</code></div>`;if(f.skillToUse)det+=`<div class="detail-label">Skill</div><div class="detail-value"><code>${esc(f.skillToUse)}</code></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}</div><div style="margin-top:3px">${ct} ${imp} ${bs} ${pri}</div><div class="row-desc">${esc(f.description)}</div><div class="row-detail">${det}</div></div></div></div>`;}
function renderMd(md){if(!md)return'<p style="color:var(--muted)">No content.</p>';return md.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').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>');}
function renderNS(steps){if(!steps?.length)return;document.getElementById('next-steps').innerHTML=steps.map(s=>`<div class="ns-item ns-${s.severity||'low'}"><strong>${esc(s.action)}</strong><div style="margin-top:3px"><span class="pill p-${s.severity||'low'}">${s.severity}</span>${s.effort?` <span class="pill" style="background:#e5e7eb;color:#374151">${s.effort}</span>`:''}${s.skillToUse?` <span class="pill l-pill">${s.skillToUse}</span>`:''}</div><div style="color:var(--muted);font-size:.78rem;margin-top:3px">${esc(s.reason)}</div></div>`).join('');}
function addPills(id,label,type,vals,pre){document.getElementById(id).innerHTML=`<span class="filter-label">${label}:</span>`+vals.map(v=>{const cls=pre?`${pre}${v}`:'l-pill';const lb={impact:IML,priority:PL,bindingStatus:SL,changeType:CTL,importance:IL,category:CL}[type]?.[v]||v;return`<span class="pill ${cls}" data-filter="${type}:${v}" onclick="tF('${type}','${v}')">${lb}</span>`;}).join(' ');}
document.addEventListener('DOMContentLoaded',()=>{if(!D){document.body.innerHTML='<p style="padding:2rem;color:red">No data.</p>';return;}
const m=D.meta;const s=D.summary;const mode=m.scanMode||'full';const ms=m.refFrom?`${m.refFrom}..${m.refTo}`:`m${m.currentMilestone} (${mode})`;document.getElementById('subtitle').textContent=`${ms} · ${m.date}`;
const byCt=s.byChangeType||{};const byBs=s.byBindingStatus||{};const byImp=s.byImpact||{};
document.getElementById('summary-bar').innerHTML=[['Total',s.totalFindings||0,'#1f2328'],['Missing',byBs.missing||0,'#991b1b'],['Partial',byBs.partial||0,'#92400e'],['Full',byBs.full||0,'#166534'],['Added',byCt.added||0,'#166534'],['Fixed',byCt.fixed||0,'#1e40af'],['Engine',byCt.upstream||0,'#9d174d'],['🚀',byImp.transformative||0,'#7e22ce'],['⭐',byImp.significant||0,'#1e40af']].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('');
addPills('fr-impact','Impact','impact',['transformative','significant','moderate','minor'],'imp-');
addPills('fr-priority','Priority','priority',['critical','high','medium','low'],'p-');
addPills('fr-binding','Binding','bindingStatus',['missing','partial','full','action_needed','not_applicable'],'s-');
addPills('fr-changetype','Type','changeType',Object.keys(CTL),'ct-');
const imps=[...new Set(D.findings.map(f=>f.importance))].sort((a,b)=>(IMO[a]??9)-(IMO[b]??9));
addPills('fr-importance','Importance','importance',imps,'i-');
const cats=[...new Set(D.findings.map(f=>f.category))].sort();
addPills('fr-category','Category','category',cats,'');
document.getElementById('findings-count').textContent=D.findings.length;
document.getElementById('ns-count').textContent=(D.nextSteps||[]).length;
document.getElementById('slides-content').innerHTML=renderMd(D.slides);
document.getElementById('changelog-content').innerHTML=renderMd(D.changelog);
renderNS(D.nextSteps);
document.getElementById('meta-footer').innerHTML=`${m.repo} · m${m.currentMilestone} · ${m.date}`+(m.commitCount?` · ${m.commitCount} commits`:'');
rebuild();});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment