Created
April 29, 2026 07:54
-
-
Save mattleibow/3fc5367c189479dfd6a43faa0a3835e9 to your computer and use it in GitHub Desktop.
Skia Analyst: Full scan m147 — 95 findings, both changelog and gap analysis
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>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,'&').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>');} | |
| 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