Skip to content

Instantly share code, notes, and snippets.

@mattleibow
Created April 27, 2026 01:30
Show Gist options
  • Select an option

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

Select an option

Save mattleibow/f7a542001d2c1ee3fc9ae6f4b27dbc30 to your computer and use it in GitHub Desktop.
Skia Feature Scout: Full scan m133 (112 findings with impact scoring)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Skia Feature Scout — m133 (2026-04-27)</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
:root{--bg:#f6f8fa;--border:#d8dee4;--row-hover:#f0f4f8;--muted:#656d76;}
body{background:var(--bg);color:#1f2328;font-size:14px;}
.shell{max-width:1100px;}
.summary-bar{background:#fff;border:1px solid var(--border);border-radius:8px;padding:12px 16px;margin-bottom:12px;}
.summary-num{font-size:1.5rem;font-weight:700;line-height:1;}
.summary-label{font-size:.65rem;color:var(--muted);text-transform:uppercase;letter-spacing:.5px;}
.pill{display:inline-block;font-size:.68rem;font-weight:600;padding:1px 7px;border-radius:10px;cursor:pointer;user-select:none;white-space:nowrap;vertical-align:middle;}
.pill:hover{opacity:.8;}.pill.dim{opacity:.3;}
.p-critical{background:#fee2e2;color:#991b1b;}.p-high{background:#fed7aa;color:#9a3412;}
.p-medium{background:#fef3c7;color:#92400e;}.p-low{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;}
.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;}
.c-new_feature{background:#dbeafe;color:#1e40af;}.c-codec{background:#fce7f3;color:#9d174d;}
.c-image{background:#ede9fe;color:#5b21b6;}.c-image_filter{background:#e0e7ff;color:#3730a3;}
.c-shader{background:#ccfbf1;color:#115e59;}.c-color{background:#fef3c7;color:#92400e;}
.c-canvas{background:#dbeafe;color:#1e40af;}.c-path{background:#fed7aa;color:#9a3412;}
.c-font{background:#e0e7ff;color:#3730a3;}.c-utility{background:#f3f4f6;color:#374151;}
.c-performance{background:#d1fae5;color:#065f46;}.c-behavior_change{background:#fef3c7;color:#92400e;}
.c-deprecation{background:#fee2e2;color:#991b1b;}
.l-pill{background:#e0e7ff;color:#3730a3;}
.ms{font-size:.68rem;font-weight:600;color:#3730a3;background:#e0e7ff;padding:1px 6px;border-radius:8px;}
.row-item{border-bottom:1px solid #eaeef2;padding:7px 12px;cursor:pointer;}
.row-item:hover{background:var(--row-hover);}
.row-item.expanded{background:#fafbfc;}
.row-name{font-weight:600;font-size:.88rem;}.row-chevron{color:#bbb;font-size:.7rem;margin-right:6px;transition:transform .15s;}
.row-item.expanded .row-chevron{transform:rotate(90deg);color:#666;}
.row-desc{color:var(--muted);font-size:.78rem;margin-top:2px;}
.row-pills{margin-top:3px;}
.row-detail{display:none;padding:8px 0 4px 0;font-size:.82rem;}
.row-item.expanded .row-detail{display:block;}
.detail-grid{display:grid;grid-template-columns:100px 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:70px;flex-shrink:0;text-align:right;padding-right:6px;}
.search-box{font-size:.8rem;padding:3px 10px;border:1px solid var(--border);border-radius:6px;}
.ctrl-select{font-size:.78rem;padding:2px 8px;border:1px solid var(--border);border-radius:6px;background:#fff;color:#1f2328;}
.reset-btn{font-size:.7rem;color:#3730a3;cursor:pointer;border:none;background:none;padding:2px 6px;}
.reset-btn:hover{text-decoration:underline;}
.next-step{border-left:3px solid;padding:6px 12px;margin-bottom:6px;cursor:pointer;}
.next-step: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;}
.count-badge{font-size:.6rem;background:#e5e7eb;color:#374151;padding:0 5px;border-radius:8px;margin-left:2px;vertical-align:middle;}
.tab-btn{font-size:.85rem;font-weight:600;padding:6px 16px;border:none;background:none;color:var(--muted);cursor:pointer;border-bottom:2px solid transparent;}
.tab-btn.active{color:#1e40af;border-bottom-color:#1e40af;}.tab-btn:hover{color:#1e40af;}
.tab-pane{display:none;}.tab-pane.active{display:block;}
.group-header{font-weight:700;font-size:.82rem;padding:8px 12px 4px;color:#1e40af;border-bottom:2px solid #dbeafe;margin-top:12px;background:#f8faff;}
.meta-footer{font-size:.72rem;color:var(--muted);margin-top:16px;padding:8px 12px;}
.meta-footer a{color:#3730a3;}
.dir-btn{font-size:.8rem;width:26px;height:26px;border:1px solid var(--border);border-radius:4px;background:#fff;cursor:pointer;color:#1e40af;font-weight:700;padding:0;line-height:26px;text-align:center;}
.dir-btn:hover{background:#f0f4f8;}
</style>
</head>
<body>
<script>const DATA = {"meta": {"date": "2026-04-27", "schemaVersion": "3.0", "mode": "full", "currentMilestone": 133, "latestUpstreamMilestone": 148, "skiaSubmoduleCommit": "f5345469d1388a85decaed40f424c4461d3a39e5", "releaseNotesSource": "https://raw.githubusercontent.com/google/skia/main/RELEASE_NOTES.md"}, "summary": {"totalFindings": 112, "byStatus": {"missing": 55, "action_needed": 11, "partial": 8, "not_applicable": 17, "full": 10, "correctly_absent": 11}, "byPriority": {"critical": 3, "high": 18, "medium": 54, "low": 37}, "bySource": {"release-notes": 95, "header-scan": 14, "binding-audit": 3}, "byImpact": {"transformative": 4, "moderate": 56, "significant": 16, "minor": 36}, "byLabel": {"action-needed": 13, "behavior-change": 17, "quick-win": 11, "hidden-api": 23, "perf": 5}}, "findings": [{"name": "SkPath migrating to immutable (SK_HIDE_PATH_EDIT_METHODS)", "category": "deprecation", "priority": "critical", "impact": "transformative", "description": "SkPath is becoming immutable. moveTo/lineTo/quadTo/cubicTo and all edit methods will be removed. New static factories (Raw/Rect/Oval/Circle/RRect/Polygon/Line) and SkPathBuilder are the replacement. SkiaSharp's entire SKPath edit surface depends on the methods that will disappear.", "bindingStatus": "missing", "source": "release-notes", "labels": ["action-needed"], "milestone": 143, "skiaApi": "SkPath::moveTo, lineTo, quadTo, cubicTo, etc.; SkPathBuilder", "csharpMethod": "SKPath.MoveTo, LineTo, QuadTo, CubicTo, ArcTo, etc.", "csharpFile": "binding/SkiaSharp/SKPath.cs", "replacement": "SKPathBuilder (not yet bound) and SKPath.Raw/Rect/Oval/Circle/RRect/Polygon/Line factories", "obsoleteMessage": "SkPath is becoming immutable. Use SKPathBuilder (not yet bound) to construct paths.", "userValue": "Without SkPathBuilder bindings the next Skia bump will break the entire SKPath construction API.", "skillToUse": "add-api", "effort": "large", "cppHeader": "include/core/SkPathBuilder.h"}, {"name": "Encoder Options ICC profile fields removed upstream", "category": "deprecation", "priority": "critical", "impact": "moderate", "description": "fICCProfile and fICCProfileDescription members were removed from SkPngEncoder::Options, SkJpegEncoder::Options, and SkWebpEncoder::Options in M142. SkiaSharp's Definitions.cs still declares these fields in SKPngEncoderOptions / SKJpegEncoderOptions / SKWebpEncoderOptions and SkiaApi.generated.cs maps them. The next Skia bump will break the struct ABI.", "bindingStatus": "action_needed", "source": "release-notes", "labels": ["action-needed", "behavior-change"], "milestone": 108, "milestoneRemoved": 142, "skiaApi": "SkPngEncoder::Options::fICCProfile, SkJpegEncoder::Options::fICCProfile, SkWebpEncoder::Options::fICCProfile", "csharpMethod": "SKPngEncoderOptions.fICCProfile, SKJpegEncoderOptions.fICCProfile, SKWebpEncoderOptions.fICCProfile", "csharpFile": "binding/SkiaSharp/Definitions.cs, binding/SkiaSharp/SkiaApi.generated.cs", "replacement": "Set ICC profile via the image's color space / use HDR metadata struct instead", "obsoleteMessage": "ICC profile encoder options were removed in Skia M142. Set the color space on the source image instead.", "effort": "medium", "userValue": "Avoid build break during the M142+ Skia bump.", "skillToUse": "add-api"}, {"name": "SkMesh / Custom Vertex Mesh Drawing", "category": "new_feature", "priority": "high", "impact": "transformative", "description": "Draw custom vertex meshes with user-defined attributes/varyings in SkSL. Mesh data can be uploaded to GPU once and reused across draws. Extended in m109 (signature changes), m110 (error reporting), m117 (namespace move), m119/m120 (children/effects).", "bindingStatus": "missing", "source": "release-notes", "milestone": 106, "milestones": [109, 110, 117, 119, 120], "skiaApi": "SkMesh::Make, SkMesh::MakeIndexed, SkMeshSpecification, SkMeshes namespace", "userValue": "Enables particle systems, deformable meshes, custom geometry, and high-performance instanced drawing.", "skillToUse": "add-api", "effort": "large"}, {"name": "skhdr::Metadata + HDR PNG pipeline", "category": "new_feature", "priority": "high", "impact": "transformative", "description": "M142 added skhdr::Metadata containing all HDR metadata (ContentLightLevelInfo, MasteringDisplayColorVolume) and wired HDR metadata support into SkPngDecoder, SkPngRustDecoder, and SkPngEncoder. M145 added skhdr::Agtm (SMPTE ST 2094-50 Adaptive Global Tone Mapping) with parsing, serialization, and an SkColorFilter for tone mapping.", "bindingStatus": "missing", "source": "release-notes", "milestone": 142, "milestones": [145], "skiaApi": "skhdr::Metadata, skhdr::ContentLightLevelInfo, skhdr::MasteringDisplayColorVolume, skhdr::Agtm", "userValue": "End-to-end HDR image workflow (decode HDR PNGs with metadata, encode with HDR metadata, tone-map for SDR display).", "skillToUse": "add-api", "effort": "large"}, {"name": "SkPathBuilder bindings", "category": "path", "priority": "critical", "impact": "transformative", "description": "The new path construction paradigm. M143 added arcTo/rArcTo alignment fixes, M146 made several utilities take SkPathBuilder instead of SkPath.", "bindingStatus": "missing", "source": "release-notes", "milestone": 143, "milestones": [146], "skiaApi": "SkPathBuilder (include/core/SkPathBuilder.h)", "cppHeader": "include/core/SkPathBuilder.h", "userValue": "Required to construct paths once SkPath edit methods are removed. Also avoids extra path-data copies in SkContourMeasure::getSegment, SkPathEffect::filterPath, SkPathMeasure::getSegment, skpathutils::FillPathWithPaint.", "skillToUse": "add-api", "effort": "large"}, {"name": "Gradient interpolation in CSS Color 4 spaces", "category": "shader", "priority": "high", "impact": "significant", "description": "M114 added SkGradientShader::Interpolation parameter to gradient factories (color space + hue method) per CSS Color Level 4. M134 extended this to A98 RGB, ProPhoto RGB, Display P3 and Rec2020. SkiaSharp's CreateLinearGradient/CreateRadialGradient/CreateSweepGradient/CreateTwoPointConicalGradient have no Interpolation overload.", "bindingStatus": "missing", "source": "release-notes", "milestone": 114, "milestones": [134], "skiaApi": "SkGradientShader::Interpolation { ColorSpace, HueMethod, InPremul }", "csharpMethod": "SKShader.CreateLinearGradient/Radial/Sweep/TwoPointConical (no Interpolation overload)", "csharpFile": "binding/SkiaSharp/SKShader.cs", "userValue": "Dramatically better-looking gradients (CSS-spec-compliant) — visible quality improvement users notice immediately.", "skillToUse": "add-api", "effort": "medium"}, {"name": "SkImageFilters::RuntimeShader", "category": "image_filter", "priority": "high", "impact": "significant", "description": "M98 added SkImageFilters::RuntimeShader to host runtime effects in image-filter graphs. M99 added a multi-child variant. M104 took child names as string_view. M116 added a maxSampleRadius variant for boundary handling. SkiaSharp's SKRuntimeEffect supports runtime shaders, but there is no SKImageFilter.CreateRuntimeShader factory.", "bindingStatus": "missing", "source": "release-notes", "milestone": 98, "milestones": [99, 104, 116], "skiaApi": "SkImageFilters::RuntimeShader", "csharpFile": "binding/SkiaSharp/SKImageFilter.cs", "userValue": "Custom GPU shader effects can be embedded in SkImageFilter pipelines for image processing.", "skillToUse": "add-api", "effort": "medium", "cppHeader": "include/effects/SkImageFilters.h"}, {"name": "SkImageFilters::Crop with SkTileMode", "category": "image_filter", "priority": "high", "impact": "significant", "description": "M119 added SkImageFilters::Crop(SkRect, SkTileMode, sk_sp<SkImageFilter>) which crops the input filter and applies a tile mode when sampling outside the crop. SkiaSharp has no SKImageFilter.CreateCrop factory.", "bindingStatus": "missing", "source": "release-notes", "milestone": 119, "skiaApi": "SkImageFilters::Crop", "cApiFile": "externals/skia/include/c/sk_imagefilter.h", "csharpFile": "binding/SkiaSharp/SKImageFilter.cs", "userValue": "Replaces the old crop-rect parameter style with a first-class crop+tile filter — the modern way to clip image-filter output.", "skillToUse": "add-api", "effort": "small", "cppHeader": "include/effects/SkImageFilters.h", "cppMethod": "static sk_sp<SkImageFilter> Crop(const SkRect&, SkTileMode, sk_sp<SkImageFilter>)"}, {"name": "SkImageFilters::DropShadow / DropShadowOnly with SkColor4f", "category": "image_filter", "priority": "medium", "impact": "moderate", "description": "DropShadow and DropShadowOnly gained SKColor4f + SKColorSpace overloads in m129 for HDR / wide-gamut shadows. SkiaSharp's CreateDropShadow overloads only accept SKColor (sRGB).", "bindingStatus": "missing", "source": "release-notes", "milestone": 129, "skiaApi": "SkImageFilters::DropShadow(... SkColor4f, sk_sp<SkColorSpace>)", "cApiFunction": "sk_imagefilter_new_drop_shadow (SKColor only)", "csharpMethod": "SKImageFilter.CreateDropShadow / CreateDropShadowOnly", "csharpFile": "binding/SkiaSharp/SKImageFilter.cs", "userValue": "Wide-gamut HDR drop shadows.", "skillToUse": "add-api", "effort": "small", "labels": ["quick-win"]}, {"name": "SkImage::makeScaled (domain-preserving scale)", "category": "image", "priority": "high", "impact": "significant", "description": "M128 added SkImage::makeScaled which returns a scaled version of an image while retaining its domain (raster stays raster, ganesh stays ganesh, graphite stays graphite, lazy becomes raster like makeSubset). SkiaSharp has ScalePixels but no makeScaled equivalent.", "bindingStatus": "missing", "source": "release-notes", "milestone": 128, "skiaApi": "SkImage::makeScaled(const SkImageInfo&, const SkSamplingOptions&)", "cppHeader": "include/core/SkImage.h", "csharpFile": "binding/SkiaSharp/SKImage.cs", "userValue": "Convenient one-call image scaling that preserves backing type — common workflow today requires manual surface+draw.", "skillToUse": "add-api", "effort": "small", "cppClass": "SkImage", "cppMethod": "sk_sp<SkImage> makeScaled(const SkImageInfo&, const SkSamplingOptions&) const"}, {"name": "SkImage::reinterpretColorSpace", "category": "image", "priority": "medium", "impact": "moderate", "description": "Re-tag an SkImage with a new color space without converting pixels. Useful for color-managed workflows where data needs reinterpretation rather than conversion.", "bindingStatus": "missing", "source": "header-scan", "labels": ["hidden-api"], "cppClass": "SkImage", "cppHeader": "include/core/SkImage.h", "cppMethod": "reinterpretColorSpace(sk_sp<SkColorSpace>) const", "csharpFile": "binding/SkiaSharp/SKImage.cs", "skillToUse": "add-api", "effort": "small"}, {"name": "Async rescale & readback (RGBA + YUV420 + YUVA420)", "category": "performance", "priority": "high", "impact": "significant", "description": "M86 added SkImage::asyncRescaleAndReadPixels and asyncRescaleAndReadPixelsYUV420. M100 made callback timing explicit (requires submit). M109 added kLinear rescale mode. M117 added asyncRescaleAndReadPixelsYUVA420 (with full-resolution alpha plane). Not bound in SkiaSharp.", "bindingStatus": "missing", "source": "release-notes", "milestone": 86, "milestones": [100, 109, 117], "skiaApi": "SkImage/SkSurface::asyncRescaleAndReadPixels, asyncRescaleAndReadPixelsYUV420, asyncRescaleAndReadPixelsYUVA420", "userValue": "Non-blocking pixel readback at scaled size is essential for video pipelines, screenshot/streaming, and HW-accelerated downscaling.", "skillToUse": "add-api", "effort": "large"}, {"name": "AVIF decode support", "category": "codec", "priority": "high", "impact": "significant", "description": "M88 added AVIF support to SkHeifCodec, M106 added AVIF decoding via libavif. SkiaSharp's SKEncodedImageFormat enum includes Avif but the decoder must be registered (M115 codec registration model) and is not wired in by default.", "bindingStatus": "partial", "source": "release-notes", "milestone": 88, "milestones": [106, 115], "skiaApi": "SkAvifDecoder, SkCodecs::Register", "csharpMethod": "SKEncodedImageFormat.Avif (enum only)", "csharpFile": "binding/SkiaSharp/Definitions.cs", "userValue": "First-class AVIF decoding for modern image format support.", "skillToUse": "add-api", "effort": "medium"}, {"name": "JPEG XL decode support", "category": "codec", "priority": "medium", "impact": "moderate", "description": "M98 added limited JPEG XL support. M115 codec registration applies. SkiaSharp's enum has Jpegxl but the decoder is not registered.", "bindingStatus": "partial", "source": "release-notes", "milestone": 98, "milestones": [115], "skiaApi": "SkJpegxlDecoder", "csharpMethod": "SKEncodedImageFormat.Jpegxl (enum only)", "userValue": "Modern next-gen image format with very high compression and quality.", "skillToUse": "add-api", "effort": "medium"}, {"name": "Animated WebP encoding", "category": "codec", "priority": "medium", "impact": "significant", "description": "Upstream SkWebpEncoder gained animated WebP output, but SkiaSharp only exposes single-image WebP encode options. There is no fallback C API or managed surface for assembling animated WebP files.", "bindingStatus": "missing", "source": "release-notes", "milestone": 107, "skiaApi": "SkWebpEncoder::EncodeAnimated", "csharpFile": "binding/SkiaSharp/SKPixmap.cs", "userValue": "Encode animated WebPs from frames — common for stickers, web banners, lightweight animation.", "skillToUse": "add-api", "effort": "medium"}, {"name": "SkCodec::isAnimated", "category": "codec", "priority": "medium", "impact": "moderate", "description": "M135 added SkCodec::isAnimated to disambiguate getRepetitionCount() returning 0. SkiaSharp's SKCodec exposes RepetitionCount but no IsAnimated property.", "bindingStatus": "missing", "source": "release-notes", "milestone": 135, "skiaApi": "SkCodec::isAnimated", "csharpFile": "binding/SkiaSharp/SKCodec.cs", "userValue": "Reliable check for whether a decoded image is animated.", "skillToUse": "add-api", "effort": "trivial", "cppClass": "SkCodec", "cppMethod": "bool isAnimated() const", "labels": ["hidden-api"]}, {"name": "SkCodec::hasHighBitDepthEncodedData", "category": "codec", "priority": "medium", "impact": "moderate", "description": "M132 added a new SkCodec method to detect whether the encoded data is high-bit-depth (e.g. 10/12-bit PNG, HDR formats). Useful for choosing F16/1010102 decode targets.", "bindingStatus": "missing", "source": "release-notes", "milestone": 132, "skiaApi": "SkCodec::hasHighBitDepthEncodedData", "csharpFile": "binding/SkiaSharp/SKCodec.cs", "userValue": "Lets apps preserve HDR / 10-bit content (e.g. HEIC, AVIF) instead of silently downsampling to 8-bit.", "skillToUse": "add-api", "effort": "trivial", "cppClass": "SkCodec", "cppMethod": "bool hasHighBitDepthEncodedData() const", "labels": ["hidden-api"]}, {"name": "SkCodec::Options::fMaxDecodeMemory + kOutOfMemory result", "category": "codec", "priority": "medium", "impact": "moderate", "description": "M145 added fMaxDecodeMemory cap on decode memory; if estimated/used memory exceeds this, decode returns nullptr and Result::kOutOfMemory. Critical safety against OOM from malicious images.", "bindingStatus": "missing", "source": "release-notes", "milestone": 145, "skiaApi": "SkCodec::Options::fMaxDecodeMemory, SkCodec::Result::kOutOfMemory", "csharpFile": "binding/SkiaSharp/SKCodec.cs", "userValue": "Defense against decompression bombs / malicious images — security feature.", "skillToUse": "add-api", "effort": "small"}, {"name": "SkCodec::SelectionPolicy", "category": "codec", "priority": "medium", "impact": "moderate", "description": "M123 documented behavior of SelectionPolicy::kPreferStillImage (and SelectionPolicy itself) for SkWuffsCodec/SkGifDecoder. Not exposed in SkiaSharp.", "bindingStatus": "missing", "source": "release-notes", "milestone": 123, "skiaApi": "SkCodec::SelectionPolicy", "csharpFile": "binding/SkiaSharp/SKCodec.cs", "userValue": "Lets callers opt into still-image-only decoding for animated formats, avoiding stream copies.", "skillToUse": "add-api", "effort": "small"}, {"name": "SkCodec::getImage respects EXIF origin", "category": "behavior_change", "priority": "medium", "impact": "moderate", "description": "M123: SkCodec::getImage now respects origin metadata (e.g. Exif rotation). Callers passing an SkImageInfo may need to swap width/height (see SkPixmapUtils::SwapWidthHeight).", "bindingStatus": "not_applicable", "source": "release-notes", "labels": ["behavior-change"], "milestone": 123, "skiaApi": "SkCodec::getImage", "userValue": "Apps using SKCodec.GetImage may see auto-rotation with the next bump — flag for testing.", "bindingActionNeeded": false}, {"name": "SkShaders::CoordClamp", "category": "shader", "priority": "medium", "impact": "moderate", "description": "M112 added SkShaders::CoordClamp wrapper that clamps the coordinates supplied to another shader to a rectangle. Useful for tiling-style effects without altering tile mode.", "bindingStatus": "missing", "source": "release-notes", "milestone": 112, "skiaApi": "SkShaders::CoordClamp", "csharpFile": "binding/SkiaSharp/SKShader.cs", "userValue": "Clean way to bound shader sampling without the cost of a layer crop.", "skillToUse": "add-api", "effort": "small", "cppClass": "SkShaders", "cppHeader": "include/core/SkShader.h", "cppMethod": "CoordClamp(sk_sp<SkShader>, const SkRect& subset)", "labels": ["hidden-api"]}, {"name": "SkShader::makeWithWorkingColorSpace (with output CS in M141)", "category": "shader", "priority": "medium", "impact": "moderate", "description": "Shaders can be wrapped with an explicit working color space so evaluation happens in a known gamut/transfer space before converting to the destination. The fallback C API exposes no equivalent, so shader-based color workflows cannot request these conversions.", "bindingStatus": "missing", "source": "release-notes", "milestone": 141, "skiaApi": "SkShader::makeWithWorkingColorSpace", "cppClass": "SkShader", "cppHeader": "include/core/SkShader.h", "csharpFile": "binding/SkiaSharp/SKShader.cs", "skillToUse": "add-api", "effort": "small", "userValue": "Helps blend, matrix, and runtime color filters produce predictable results across destination color spaces.", "cppMethod": "makeWithWorkingColorSpace(sk_sp<SkColorSpace> inputCS, sk_sp<SkColorSpace> outputCS = nullptr) const", "labels": ["hidden-api"]}, {"name": "SkSL toLinearSrgb / fromLinearSrgb intrinsics", "category": "shader", "priority": "medium", "impact": "moderate", "description": "m99 added toLinearSrgb / fromLinearSrgb intrinsics so runtime effects can do color-space-aware lighting math. Automatically available to any SkSL author through SkiaSharp once they upgrade.", "bindingStatus": "not_applicable", "source": "release-notes", "milestone": 99, "skiaApi": "toLinearSrgb(vec3), fromLinearSrgb(vec3)", "userValue": "Available automatically inside SkSL programs — nothing to bind, but worth documenting.", "bindingActionNeeded": false, "labels": ["perf"]}, {"name": "SkBitmap::setColorSpace / getColor4f", "category": "image", "priority": "medium", "impact": "moderate", "description": "M104 added SkBitmap::getColor4f and SkPixmap::getColor4f for float color reads. M125 added SkBitmap::setColorSpace to reinterpret color space without changing pixels. SkiaSharp's SKBitmap.ColorSpace getter exists but no setter and no GetColor4f.", "bindingStatus": "missing", "source": "release-notes", "milestone": 104, "milestones": [125], "skiaApi": "SkBitmap::setColorSpace, SkBitmap::getColor4f, SkPixmap::getColor4f", "csharpFile": "binding/SkiaSharp/SKBitmap.cs, binding/SkiaSharp/SKPixmap.cs", "userValue": "Color-managed bitmap workflows and lossless float color queries.", "skillToUse": "add-api", "effort": "small", "cppHeader": "include/core/SkBitmap.h", "labels": ["quick-win"]}, {"name": "SkData::Equals", "category": "utility", "priority": "low", "impact": "minor", "description": "M145 added SkData::Equals static helper that handles nulls. SkiaSharp's SKData inherits Object.Equals but not the native equality helper.", "bindingStatus": "missing", "source": "release-notes", "labels": ["quick-win"], "milestone": 145, "skiaApi": "SkData::Equals", "cApiFile": "externals/skia/include/c/sk_data.h", "csharpFile": "binding/SkiaSharp/SKData.cs", "skillToUse": "add-api", "effort": "trivial", "userValue": "Compare two SkDatas without rolling your own loop.", "cppClass": "SkData", "cppHeader": "include/core/SkData.h", "cppMethod": "static bool Equals(const SkData*, const SkData*)"}, {"name": "SkColorSpacePrimaries::operator==", "category": "utility", "priority": "low", "impact": "minor", "description": "m146 added equality for SkColorSpacePrimaries. Trivial convenience.", "bindingStatus": "missing", "source": "release-notes", "labels": ["quick-win"], "milestone": 146, "skiaApi": "SkColorSpacePrimaries::operator==", "csharpFile": "binding/SkiaSharp/SKColorSpace.cs", "skillToUse": "add-api", "effort": "trivial", "userValue": "Compare custom primaries without manual field-by-field comparison."}, {"name": "SkCanvas::saveLayerAlphaf", "category": "canvas", "priority": "medium", "impact": "moderate", "description": "M111 added a float-alpha overload of SkCanvas::saveLayerAlpha (saveLayerAlphaf). SkiaSharp only exposes SaveLayer overloads with SKPaint (no direct float-alpha).", "bindingStatus": "missing", "source": "release-notes", "milestone": 111, "skiaApi": "SkCanvas::saveLayerAlphaf", "csharpFile": "binding/SkiaSharp/SKCanvas.cs", "userValue": "Convenience for translucent layers without constructing an SKPaint.", "skillToUse": "add-api", "effort": "trivial"}, {"name": "SaveLayerRec backdrop tile mode", "category": "canvas", "priority": "medium", "impact": "moderate", "description": "SaveLayerRec gained an optional backdrop tilemode controlling how backdrop content is sampled outside the previous layer's image. SkiaSharp's SKCanvasSaveLayerRec exposes Bounds/Paint/Backdrop/Flags but no backdrop tilemode field.", "bindingStatus": "missing", "source": "release-notes", "milestone": 131, "skiaApi": "SkCanvas::SaveLayerRec backdropTileMode", "csharpMethod": "SKCanvasSaveLayerRec", "csharpFile": "binding/SkiaSharp/SKCanvas.cs", "userValue": "Controls how backdrop sampling behaves at the edge of the prior layer (matches CSS backdrop-filter behaviour).", "skillToUse": "add-api", "effort": "small"}, {"name": "SkSurfaceProps::kAlwaysDither_Flag", "category": "canvas", "priority": "low", "impact": "minor", "description": "m114 added kAlwaysDither_Flag to globally enable dithering on a surface. SkiaSharp exposes SKSurfaceProperties / SKSurfacePropsFlags but not this flag.", "bindingStatus": "missing", "source": "release-notes", "milestone": 114, "skiaApi": "SkSurfaceProps::kAlwaysDither_Flag", "csharpFile": "binding/SkiaSharp/SKSurfaceProperties.cs", "skillToUse": "add-api", "effort": "trivial", "userValue": "Force dithering on a target without per-paint flags.", "labels": ["hidden-api", "quick-win"]}, {"name": "SkSurfaceProps::kDefault_Flag", "category": "utility", "priority": "low", "impact": "minor", "description": "m123 added a self-documenting kDefault_Flag = 0 to SkSurfaceProps::Flags. Pure readability.", "bindingStatus": "missing", "source": "release-notes", "milestone": 123, "skiaApi": "SkSurfaceProps::kDefault_Flag", "skillToUse": "add-api", "effort": "trivial", "userValue": "Readability when constructing default-flag SkSurfaceProps."}, {"name": "SkSurfaces::ResolveMSAA", "category": "canvas", "priority": "medium", "impact": "moderate", "description": "M100 added SkSurface::resolveMSAA, M114 added kAlwaysDither_Flag, M116 relocated to SkSurfaces::ResolveMSAA. Not bound in SkiaSharp.", "bindingStatus": "missing", "source": "release-notes", "milestone": 100, "milestones": [116], "skiaApi": "SkSurfaces::ResolveMSAA", "csharpFile": "binding/SkiaSharp/SKSurface.cs", "userValue": "Force MSAA resolve when wrapping a client texture as the resolve target — important for interop with native GPU pipelines.", "skillToUse": "add-api", "effort": "small"}, {"name": "SkColorTable", "category": "color", "priority": "medium", "impact": "moderate", "description": "m116 introduced SkColorTable to own the LUT data and avoid duplicating tables when wrapping color filters. SkiaSharp wraps SkColorFilters::Table via byte arrays; binding SkColorTable would let advanced clients share table memory.", "bindingStatus": "missing", "source": "release-notes", "milestone": 116, "skiaApi": "SkColorTable", "csharpFile": "binding/SkiaSharp/SKColorFilter.cs", "userValue": "Avoids duplicating LUT memory when applying table-based color filters.", "skillToUse": "add-api", "effort": "small", "labels": ["hidden-api"]}, {"name": "SkColorFilters::Blend with SkColor4f + SkColorSpace", "category": "color", "priority": "medium", "impact": "moderate", "description": "M106 added SkColorFilters::Blend(const SkColor4f&, sk_sp<SkColorSpace>, SkBlendMode) to complement the SkColor variant.", "bindingStatus": "missing", "source": "release-notes", "milestone": 106, "skiaApi": "SkColorFilters::Blend(SkColor4f, sk_sp<SkColorSpace>, SkBlendMode)", "csharpMethod": "SKColorFilter.CreateBlendMode (only SKColor)", "csharpFile": "binding/SkiaSharp/SKColorFilter.cs", "userValue": "Wide-gamut color filters.", "skillToUse": "add-api", "effort": "small"}, {"name": "SkShaders::Color with SkColor4f always color-managed", "category": "behavior_change", "priority": "medium", "impact": "moderate", "description": "M134: SkShaders::Color(SkColor4f, sk_sp<SkColorSpace>) now always applies the color space even when rendering to a legacy non-color-managed surface (assumed sRGB target). Apps that relied on the old pass-through behavior may see color shifts.", "bindingStatus": "not_applicable", "source": "release-notes", "labels": ["behavior-change"], "milestone": 134, "skiaApi": "SkShaders::Color", "bindingActionNeeded": false, "userValue": "Document for users binding SKShader.CreateColor(SKColorF, SKColorSpace) so they expect the new behavior."}, {"name": "SkNamedTransferFn::kRec709 changed to BT.1886 gamma 2.4", "category": "behavior_change", "priority": "medium", "impact": "moderate", "description": "m141 changed kHLG and kPQ to use new skcms representations (also affects MakeCICP). m142 updated kRec709 to ITU-R BT.1886 pure-2.4. Apps using these named transfer functions in SkiaSharp may see subtle color shifts after the next Skia bump.", "bindingStatus": "not_applicable", "source": "release-notes", "labels": ["behavior-change"], "milestone": 142, "skiaApi": "SkNamedTransferFn::kRec709", "bindingActionNeeded": false, "userValue": "Be aware of subtle color shifts in HDR / Rec.709 pipelines after upgrade."}, {"name": "SkNamedTransferFn::kHLG / kPQ updated to skcms representations", "category": "behavior_change", "priority": "medium", "impact": "moderate", "description": "M141 changed kHLG/kPQ to new skcms representations, also affecting SkColorSpace::MakeCICP outputs.", "bindingStatus": "not_applicable", "source": "release-notes", "labels": ["behavior-change"], "milestone": 141, "skiaApi": "SkNamedTransferFn::kHLG, kPQ", "bindingActionNeeded": false}, {"name": "Anisotropic filtering in SkSamplingOptions", "category": "performance", "priority": "medium", "impact": "moderate", "description": "M103 added anisotropic filtering to SkSamplingOptions (GPU only). SkiaSharp's SKSamplingOptions has no Aniso constructor / max-anisotropy field.", "bindingStatus": "missing", "source": "release-notes", "milestone": 103, "skiaApi": "SkSamplingOptions::Aniso(int maxAniso)", "csharpFile": "binding/SkiaSharp/SKSamplingOptions.cs", "userValue": "Higher quality sampling on heavily transformed textures (3D-style tilted images, large minification).", "skillToUse": "add-api", "effort": "small"}, {"name": "SkJpegEncoder XMP metadata + SkYUVAPixmaps encode", "category": "codec", "priority": "medium", "impact": "moderate", "description": "M112 added XMP metadata parameter to SkJpegEncoder::Options and direct encoding of SkYUVAPixmaps. SkiaSharp's SKJpegEncoderOptions internal struct has xmpMetadata defaulted but never publicly exposed.", "bindingStatus": "partial", "source": "binding-audit", "labels": ["quick-win"], "milestone": 112, "skiaApi": "SkJpegEncoder::Options::xmpMetadata, SkJpegEncoder::Encode(YUVAPixmaps)", "csharpMethod": "SKJpegEncoderOptions (xmpMetadata field private)", "csharpFile": "binding/SkiaSharp/Definitions.cs", "userValue": "Round-trip XMP metadata through JPEG encoding. Native plumbing exists; just needs a public field.", "skillToUse": "add-api", "effort": "trivial"}, {"name": "SkWebpEncoder XMP metadata", "category": "codec", "priority": "low", "impact": "minor", "description": "SKWebpEncoderOptions internal struct in SkiaApi.generated.cs declares xmpMetadata but the public option type does not expose it.", "bindingStatus": "partial", "source": "binding-audit", "labels": ["quick-win"], "skiaApi": "SkWebpEncoder::Options::xmpMetadata", "csharpFile": "binding/SkiaSharp/Definitions.cs", "skillToUse": "add-api", "effort": "trivial"}, {"name": "SkColorSpace::MakeCICP", "category": "color", "priority": "high", "impact": "significant", "description": "CICP colorspace creation is already exposed through SKColorSpace.CreateCicp and backed by sk_colorspace_new_cicp. This closes an important part of the modern color-management story.", "bindingStatus": "full", "source": "release-notes", "milestone": 133, "skiaApi": "SkColorSpace::MakeCICP", "cApiFunction": "sk_colorspace_new_cicp", "csharpMethod": "SKColorSpace.CreateCicp", "csharpFile": "binding/SkiaSharp/SKColorSpace.cs", "labels": ["behavior-change"], "effort": "trivial", "skillToUse": "add-api"}, {"name": "SkImage::makeRawShader", "category": "shader", "priority": "high", "impact": "significant", "description": "M98 added SkImage::makeRawShader for non-color-data images (normals, heightmaps, materials). No color-space conversion or premul. SkiaSharp wraps this as SKImage.ToRawShader, correctly calling sk_image_make_raw_shader.", "bindingStatus": "full", "source": "release-notes", "milestone": 98, "skiaApi": "SkImage::makeRawShader", "cApiFunction": "sk_image_make_raw_shader", "csharpMethod": "SKImage.ToRawShader", "csharpFile": "binding/SkiaSharp/SKImage.cs", "userValue": "Makes runtime-effect workflows with non-color textures practical.", "effort": "trivial", "skillToUse": "add-api"}, {"name": "SkImageFilters::Magnifier (new lens-bounds + zoom signature)", "category": "image_filter", "priority": "medium", "impact": "moderate", "description": "M115 deprecated old (srcRect, inset) signature and added (lensBounds, zoomAmount, inset, sampling, ...) signature. M116 removed the deprecated factory. SkiaSharp's CreateMagnifier matches the new signature.", "bindingStatus": "full", "source": "release-notes", "milestone": 115, "milestones": [116], "csharpMethod": "SKImageFilter.CreateMagnifier", "csharpFile": "binding/SkiaSharp/SKImageFilter.cs"}, {"name": "SkBlenders::Arithmetic intermediate clamp", "category": "behavior_change", "priority": "low", "impact": "minor", "description": "M116 changed SkBlenders::Arithmetic so the intermediate color is always clamped to [0,1] before enforcePremul. SkiaSharp wraps via SKBlender.CreateArithmetic.", "bindingStatus": "full", "source": "release-notes", "labels": ["behavior-change"], "milestone": 116, "csharpMethod": "SKBlender.CreateArithmetic", "csharpFile": "binding/SkiaSharp/SKBlender.cs", "bindingActionNeeded": false}, {"name": "SkSurfaces / SkImages namespace migration", "category": "image", "priority": "high", "impact": "moderate", "description": "M113-M116 moved most SkImage and SkSurface factories into the SkImages and SkSurfaces namespaces (e.g. MakeFromEncoded->DeferredFromEncodedData, MakeRenderTarget->RenderTarget, etc.). SkiaSharp's C API still uses the old function names internally; mono/skia fork wraps the new ones in the existing C entry points.", "bindingStatus": "full", "source": "release-notes", "milestone": 113, "milestones": [115], "skiaApi": "SkImages::*, SkSurfaces::*", "bindingActionNeeded": false, "labels": ["behavior-change"]}, {"name": "SkCanvas drawColor/clear with SkColor4f", "category": "canvas", "priority": "low", "impact": "minor", "description": "M85 added SkColor4f overloads to SkCanvas::clear and drawColor. SkiaSharp exposes Clear/DrawColor with SKColor only.", "bindingStatus": "missing", "source": "release-notes", "milestone": 85, "csharpFile": "binding/SkiaSharp/SKCanvas.cs", "skillToUse": "add-api", "effort": "trivial"}, {"name": "SkBitmap::clear / clearColor with SkColor4f", "category": "image", "priority": "low", "impact": "minor", "description": "M103 added SkColor4f overloads on SkBitmap::clear and SkBitmap::clearColor.", "bindingStatus": "missing", "source": "release-notes", "milestone": 103, "csharpFile": "binding/SkiaSharp/SKBitmap.cs", "skillToUse": "add-api", "effort": "trivial"}, {"name": "SkFontArguments::Palette overrides", "category": "font", "priority": "medium", "impact": "moderate", "description": "M99 added the ability to specify palette overrides via SkFontArguments (FreeType backend). M122 changed the override index to uint16_t to match the OpenType CPAL table exactly.", "bindingStatus": "missing", "source": "release-notes", "milestone": 99, "milestones": [120], "skiaApi": "SkFontArguments::Palette::Override", "csharpFile": "binding/SkiaSharp/SKFont.cs", "userValue": "Override individual palette entries on color-palette fonts (CPAL) — required for many emoji/icon fonts.", "skillToUse": "add-api", "effort": "small", "labels": ["hidden-api"]}, {"name": "SkTiledImageUtils::DrawImage / DrawImageRect", "category": "canvas", "priority": "medium", "impact": "moderate", "description": "m117 added SkTiledImageUtils that automatically tile huge SkBitmap-backed SkImages so drawImage works even when the image exceeds the GPU's max texture size. Currently SkiaSharp users who try drawing large bitmaps on GPU will silently fail.", "bindingStatus": "missing", "source": "release-notes", "milestone": 117, "skiaApi": "SkTiledImageUtils::DrawImage, DrawImageRect", "userValue": "Robust drawing of very large images that wouldn't otherwise fit in a single GPU texture.", "skillToUse": "add-api", "effort": "small"}, {"name": "SkCodecs::DeferredImage", "category": "image", "priority": "low", "impact": "moderate", "description": "M124 added SkCodecs::DeferredImage similar to SkImages::DeferredFromEncodedData but accepting an SkCodec directly so callers don't depend on built-in codec registration.", "bindingStatus": "missing", "source": "release-notes", "milestone": 124, "skiaApi": "SkCodecs::DeferredImage", "skillToUse": "add-api", "effort": "trivial"}, {"name": "Perlin noise rotation correctness + raster speedup", "category": "performance", "priority": "low", "impact": "minor", "description": "M124 fixed Perlin noise (MakeFractalNoise / MakeTurbulence) so that it rotates correctly when transformed and significantly improved raster performance.", "bindingStatus": "not_applicable", "source": "release-notes", "labels": ["perf"], "milestone": 124, "bindingActionNeeded": false}, {"name": "SkRuntimeEffect::Options.fName", "category": "shader", "priority": "low", "impact": "minor", "description": "m135 added SkRuntimeEffect::Options::fName so clients can label runtime effects (used in pipeline keys / debug). Useful for Graphite precompile but also debugging.", "bindingStatus": "missing", "source": "release-notes", "milestone": 135, "csharpFile": "binding/SkiaSharp/SKRuntimeEffect.cs", "effort": "trivial", "userValue": "Named runtime effects for telemetry, debugging, and Graphite precompile.", "labels": ["hidden-api"], "skillToUse": "add-api"}, {"name": "SkAnimCodecPlayer removed from public API", "category": "deprecation", "priority": "medium", "impact": "minor", "description": "M123 removed SkAnimCodecPlayer from the public API. SkiaSharp does not currently wrap it.", "bindingStatus": "correctly_absent", "source": "release-notes", "milestone": 123, "milestoneRemoved": 123}, {"name": "SkPath::asArc removed", "category": "deprecation", "priority": "low", "impact": "minor", "description": "M141 removed SkPath::asArc. Verify SkiaSharp does not wrap it (audit confirms no IsArc/AsArc on SKPath).", "bindingStatus": "correctly_absent", "source": "release-notes", "milestone": 141, "milestoneRemoved": 141}, {"name": "SkPathEffect DashType / DashInfo / asADash removed", "category": "deprecation", "priority": "low", "impact": "minor", "description": "M132 removed DashType / DashInfo / asADash. SkiaSharp's SKPathEffect.AsDash is no longer backed upstream — verify the SkiaSharp fork still supports it (mono/skia may keep it).", "bindingStatus": "action_needed", "source": "release-notes", "labels": ["action-needed"], "milestone": 132, "milestoneRemoved": 132, "csharpMethod": "SKPathEffect-related dash query (if present)", "csharpFile": "binding/SkiaSharp/SKPathEffect.cs", "obsoleteMessage": "SkPathEffect dash-info APIs were removed in Skia M132. Track the dash parameters in the caller instead.", "effort": "small"}, {"name": "SkDrawLooper removed", "category": "deprecation", "priority": "low", "impact": "minor", "description": "M125 removed SkDrawLooper. SkiaSharp does not appear to wrap it (no SKDrawLooper class).", "bindingStatus": "correctly_absent", "source": "release-notes", "milestone": 125, "milestoneRemoved": 125}, {"name": "SkImageFilters::AlphaThreshold removed", "category": "deprecation", "priority": "low", "impact": "minor", "description": "m116 removed SkImageFilters::AlphaThreshold. Replacement is Blend(kSrcIn, input, Picture(region)). SkiaSharp does not currently expose AlphaThreshold — correctly absent.", "bindingStatus": "correctly_absent", "source": "release-notes", "milestone": 116, "milestoneRemoved": 116}, {"name": "SkShaderMaskFilters / SkTableMaskFilters deprecated", "category": "deprecation", "priority": "low", "impact": "minor", "description": "M127 deprecated SkShaderMaskFilters and SkTableMaskFilters; will be removed in a future release. SkiaSharp's SKMaskFilter.CreateTable is built on the table mask filter and should be marked obsolete.", "bindingStatus": "action_needed", "source": "release-notes", "labels": ["action-needed"], "milestone": 127, "milestoneDeprecated": 127, "csharpMethod": "SKMaskFilter.CreateTable / CreateGamma / CreateClip (table-based)", "csharpFile": "binding/SkiaSharp/SKMaskFilter.cs", "obsoleteMessage": "Skia is removing SkTableMaskFilters / SkShaderMaskFilters. Use a color filter or runtime effect instead.", "effort": "small"}, {"name": "SkCodec::getEncodedData removed (M147)", "category": "deprecation", "priority": "medium", "impact": "minor", "description": "M147 removed SkCodec::getEncodedData() from the public API. Verify no SkiaSharp callers depend on it.", "bindingStatus": "correctly_absent", "source": "release-notes", "milestone": 147, "milestoneRemoved": 147}, {"name": "SkImageFilters::Image / Picture / Shader return non-null on null input", "category": "behavior_change", "priority": "low", "impact": "minor", "description": "M115 changed SkImageFilters::Image, Picture, and Shader to return a transparent-black filter instead of nullptr when given null inputs. SkiaSharp callers that null-check these results may be silently dead-code now.", "bindingStatus": "not_applicable", "source": "release-notes", "labels": ["behavior-change"], "milestone": 115, "bindingActionNeeded": false}, {"name": "SkTypeface::MakeFrom* deprecated", "category": "deprecation", "priority": "medium", "impact": "moderate", "description": "M120 deprecated SkTypeface::MakeFromName / MakeFromFile / MakeFromStream / MakeFromData and removed MakeDefault, in favor of going through SkFontMgr. SkiaSharp's SKTypeface.FromFamilyName / FromFile / FromStream / FromData all use the deprecated factories.", "bindingStatus": "action_needed", "source": "release-notes", "labels": ["action-needed"], "milestone": 120, "milestoneDeprecated": 120, "csharpMethod": "SKTypeface.FromFamilyName, FromFile, FromStream, FromData, FromTypeface", "csharpFile": "binding/SkiaSharp/SKTypeface.cs", "obsoleteMessage": "Skia deprecated the static SkTypeface factories. Use SKFontManager.Default to create typefaces.", "effort": "medium"}, {"name": "SkFont::getTypeface no longer returns nullptr default", "category": "behavior_change", "priority": "low", "impact": "moderate", "description": "m122 changed SkFont::getTypeface() to never fall back to a default typeface. Apps relying on 'pass null typeface to SKFont' will silently render with an empty typeface and produce no glyphs.", "bindingStatus": "not_applicable", "source": "release-notes", "labels": ["behavior-change"], "milestone": 121, "bindingActionNeeded": false, "userValue": "Diagnose silent missing-glyph regressions after Skia bump."}, {"name": "SkTextBlob::Iter (header-scan)", "category": "font", "priority": "medium", "impact": "moderate", "description": "Public Iter type on SkTextBlob for inspecting glyph runs. Not exposed in SkiaSharp's SKTextBlob.", "bindingStatus": "missing", "source": "header-scan", "labels": ["hidden-api"], "cppClass": "SkTextBlob", "cppHeader": "include/core/SkTextBlob.h", "cppMethod": "Iter (constructor + experimental_next)", "csharpFile": "binding/SkiaSharp/SKTextBlob.cs", "userValue": "Iterate runs/glyphs/typefaces of a text blob — needed for any post-shape inspection.", "skillToUse": "add-api", "effort": "small"}, {"name": "SkBlendMode_AsCoeff (header-scan)", "category": "utility", "priority": "low", "impact": "minor", "description": "SkBlendMode_AsCoeff returns the Porter-Duff src/dst coefficients of a blend mode if expressible.", "bindingStatus": "missing", "source": "header-scan", "labels": ["hidden-api"], "cppHeader": "include/core/SkBlendMode.h", "cppMethod": "SkBlendMode_AsCoeff(SkBlendMode, SkBlendModeCoeff* src, SkBlendModeCoeff* dst)", "skillToUse": "add-api", "effort": "trivial"}, {"name": "SkStream::getData virtual", "category": "utility", "priority": "low", "impact": "minor", "description": "M124 added SkStream::getData() as a virtual; SkStreamMemory::asData was renamed to getData() with the legacy name to be removed.", "bindingStatus": "missing", "source": "release-notes", "milestone": 124, "csharpFile": "binding/SkiaSharp/SKStream.cs", "effort": "trivial"}, {"name": "SkSurfaces::AsImage / AsImageCopy (Graphite-only)", "category": "image", "priority": "low", "impact": "minor", "description": "M116 moved SkSurface::asImage / makeImageCopy to SkSurfaces::AsImage / AsImageCopy. They are Graphite-only — SkiaSharp doesn't bind Graphite.", "bindingStatus": "not_applicable", "source": "release-notes", "milestone": 116, "bindingActionNeeded": false}, {"name": "SkImage::asyncRescaleAndReadPixels / YUV420 / YUVA420", "category": "image", "priority": "high", "impact": "significant", "description": "Async rescale/readback enables non-blocking readback pipelines for image and surface data, including YUV420 and newer YUVA420 variants. SkiaSharp has no matching API surface for these methods on SKImage or SKSurface.", "bindingStatus": "missing", "source": "release-notes", "milestone": 88, "milestones": [109, 117], "skiaApi": "SkImage::asyncRescaleAndReadPixels, SkImage::asyncRescaleAndReadPixelsYUV420, SkImage::asyncRescaleAndReadPixelsYUVA420; matching SkSurface APIs", "userValue": "Non-blocking pixel readback for video pipelines — a significant performance pattern.", "skillToUse": "add-api", "effort": "large", "labels": ["hidden-api"], "notes": "Fallback C API only exposes sk_surface_read_pixels.", "cppClass": "SkSurface", "cppHeader": "include/core/SkSurface.h", "cppMethod": "asyncRescaleAndReadPixels(...), asyncRescaleAndReadPixelsYUV420(...), asyncRescaleAndReadPixelsYUVA420(...)"}, {"name": "Encoder ICC/XMP/gainmap fields exist only as hidden interop plumbing", "category": "codec", "priority": "high", "impact": "moderate", "description": "Generated interop and Definitions.cs already contain private fields for encoder ICC profile, XMP metadata, and PNG gainmap metadata, but the public option types do not expose them. This is a quick managed-surface opportunity for encoder metadata scenarios.", "bindingStatus": "partial", "source": "binding-audit", "labels": ["quick-win"], "milestone": 108, "skiaApi": "SkPngEncoder::Options::fICCProfile/fXmpMetadata, SkJpegEncoder::Options::xmpMetadata, SkWebpEncoder::Options::fICCProfile", "csharpFile": "binding/SkiaSharp/SkiaApi.generated.cs; binding/SkiaSharp/Definitions.cs", "userValue": "Embeds XMP and HDR gainmap metadata when encoding JPEG/PNG — important for HDR/photo workflows.", "skillToUse": "add-api", "effort": "small", "milestones": [112], "csharpMethod": "SKJpegEncoderOptions / SKPngEncoderOptions / SKWebpEncoderOptions public option surfaces", "cApiFunction": "sk_jpegencoder_encode, sk_pngencoder_encode, sk_webpencoder_encode", "cApiFile": "binding/SkiaSharp/SkiaApi.generated.cs"}, {"name": "Gradient interpolation in P3/Rec2020/A98/ProPhoto color spaces (CSS Color L4)", "category": "shader", "priority": "high", "impact": "significant", "description": "Skia gradient shaders can interpolate in CSS Color Level 4 spaces and hue modes, including Display P3 and Rec.2020. SkiaSharp still only exposes older gradient factories, so users miss a visible quality upgrade for wide-gamut and perceptual gradients.", "bindingStatus": "missing", "source": "release-notes", "milestone": 114, "milestones": [134], "skiaApi": "SkGradientShader::Interpolation, MakeLinear/Radial/Sweep with Interpolation", "userValue": "CSS-quality gradient interpolation; dramatically smoother color transitions through perceptual color spaces.", "skillToUse": "add-api", "effort": "medium"}, {"name": "SkCodecs::Register / explicit decoder registration (m115)", "category": "codec", "priority": "high", "impact": "significant", "description": "Upstream Skia moved to explicit codec registration, but SkiaSharp ships a curated native build and does not expose decoder wiring as an app-level API. This is important for native dependency awareness, but it is not a direct managed binding gap today.", "bindingStatus": "not_applicable", "source": "release-notes", "labels": ["action-needed", "behavior-change"], "milestone": 115, "skiaApi": "SkCodecs::Register, SkCodecs::Decoder, SK_DISABLE_LEGACY_INIT_DECODERS", "userValue": "Avoid silent loss of decoders when Skia removes the legacy init.", "skillToUse": "native-dependency-update", "effort": "medium", "notes": "Confirm SkiaSharp's native shim opts in to the modern registration path before upstream removes the fallback.", "bindingActionNeeded": false}, {"name": "SkTypeface::MakeFromName/File/Stream/Data deprecation", "category": "deprecation", "priority": "high", "impact": "significant", "description": "m120 deprecated SkTypeface::MakeFromName/MakeFromFile/MakeFromStream/MakeFromData and SkTypeface::MakeDefault. Clients must use an SkFontMgr. SkiaSharp's SKTypeface.FromFamilyName/FromFile/FromStream/FromData wrap these and will eventually break.", "bindingStatus": "action_needed", "source": "release-notes", "labels": ["action-needed"], "milestone": 120, "skiaApi": "SkTypeface::MakeFromName/File/Stream/Data", "csharpMethod": "SKTypeface.FromFamilyName/FromFile/FromStream/FromData", "csharpFile": "binding/SkiaSharp/SKTypeface.cs", "replacement": "Use SkFontMgr (SKFontManager) APIs", "obsoleteMessage": "SkTypeface::MakeFromX is deprecated upstream. Prefer SKFontManager.MatchFamily/CreateTypeface."}, {"name": "SkFontMgr::RefDefault removal (m122)", "category": "deprecation", "priority": "high", "impact": "significant", "description": "m122 deleted SkFontMgr::RefDefault. Clients must construct their own SkFontMgr. SkiaSharp uses SkFontMgr::RefDefault implicitly to back SKFontManager.Default. Already-handled in modern releases via per-platform fontmgr factories — verify that SkiaSharp still works after the bump.", "bindingStatus": "action_needed", "source": "release-notes", "labels": ["action-needed"], "milestone": 122, "skiaApi": "SkFontMgr::RefDefault", "csharpMethod": "SKFontManager.Default", "csharpFile": "binding/SkiaSharp/SKFontManager.cs", "userValue": "Avoid runtime crash from missing RefDefault."}, {"name": "SKCodec planar YUVA decode", "category": "codec", "priority": "high", "impact": "significant", "description": "Upstream SkCodec can query planar YUVA output requirements and decode directly into YUVA planes. That capability is absent from the fallback C API and from the public SkiaSharp codec surface.", "bindingStatus": "missing", "source": "header-scan", "labels": ["hidden-api"], "cppClass": "SkCodec", "cppHeader": "include/codec/SkCodec.h", "cppMethod": "queryYUVAInfo(...), getYUVAPlanes(const SkYUVAPixmaps&)", "skiaApi": "SkCodec::queryYUVAInfo / getYUVAPlanes", "userValue": "Avoids unnecessary RGB conversion when feeding video, camera, or GPU-upload pipelines.", "notes": "binding/SkiaSharp/SKCodec.cs still has a TODO for missing YUV plane members.", "skillToUse": "add-api", "effort": "large"}, {"name": "SKImage color-space conversion and retagging", "category": "image", "priority": "medium", "impact": "moderate", "description": "Upstream SkImage exposes makeColorSpace(...), makeColorTypeAndColorSpace(...), and reinterpretColorSpace(...) for converting or re-tagging images without dropping back to raw pixels. The fallback C API has none of these entry points, so SkiaSharp cannot build modern color-managed image pipelines directly on SKImage.", "bindingStatus": "missing", "source": "header-scan", "labels": ["hidden-api"], "cppClass": "SkImage", "cppHeader": "include/core/SkImage.h", "cppMethod": "makeColorSpace(...), makeColorTypeAndColorSpace(...), reinterpretColorSpace(sk_sp<SkColorSpace>) const", "skillToUse": "add-api", "effort": "small", "milestone": 79, "userValue": "Lets apps convert or re-tag images in-place for color-managed workflows instead of round-tripping through decoded pixels.", "notes": "Verified in upstream SkImage.h and absent from the fallback C API headers and managed bindings.", "skiaApi": "SkImage::makeColorSpace, SkImage::makeColorTypeAndColorSpace, SkImage::reinterpretColorSpace"}, {"name": "Modern still-image codecs (AVIF / JPEG XL)", "category": "codec", "priority": "medium", "impact": "moderate", "description": "Skia added AVIF and limited JPEG XL support. SkiaSharp exposes enum values for these formats but does not present obvious managed codec-specific surface area, so practical support depends on native build configuration and remains only partially visible to users.", "bindingStatus": "partial", "source": "release-notes", "milestone": 88, "milestones": [98, 106], "skiaApi": "SkHeifCodec AVIF decode, JPEG XL decode", "userValue": "Enables decoding newer still-image formats through the existing image-loading APIs.", "skillToUse": "native-dependency-update", "effort": "medium", "notes": "The managed surface is generic; exact runtime availability still depends on native build configuration and codec registration.", "csharpMethod": "SKCodec.EncodedFormat", "cApiFunction": "sk_codec_get_encoded_format", "cApiFile": "externals/skia/include/c/sk_codec.h", "csharpFile": "binding/SkiaSharp/SKCodec.cs"}, {"name": "Anisotropic sampling options", "category": "shader", "priority": "medium", "impact": "moderate", "description": "Sampling options gained anisotropic filtering so texture sampling can remain sharp at oblique angles. SkiaSharp already exposes the max-aniso field through SKSamplingOptions(int maxAniso).", "bindingStatus": "full", "source": "release-notes", "milestone": 103, "skiaApi": "SkSamplingOptions::fMaxAniso", "userValue": "Improves texture clarity for zoomed or angled content.", "cApiFile": "include/c/sk_types.h", "csharpMethod": "SKSamplingOptions(int maxAniso)", "csharpFile": "binding/SkiaSharp/Definitions.cs"}, {"name": "SkColorFilters::Blend(SkColor4f, SkColorSpace, SkBlendMode)", "category": "color", "priority": "medium", "impact": "moderate", "description": "M106 added SkColorFilters::Blend(const SkColor4f&, sk_sp<SkColorSpace>, SkBlendMode) overload to complement the SkColor version. SkiaSharp only exposes the SkColor variant.", "bindingStatus": "missing", "source": "release-notes", "milestone": 106, "skiaApi": "SkColorFilters::Blend(const SkColor4f&, sk_sp<SkColorSpace>, SkBlendMode)", "csharpMethod": "SKColorFilter.CreateBlendMode (SKColor only)", "csharpFile": "SKColorFilter.cs", "skillToUse": "add-api", "effort": "small", "labels": ["quick-win"], "userValue": "Wide-gamut blend color filters."}, {"name": "SkPaint::getFillPath -> skpathutils::FillPathWithPaint", "category": "path", "priority": "medium", "impact": "moderate", "description": "m146 changed SkContourMeasure::getSegment, SkPathEffect::filterPath, SkPathMeasure::getSegment, and skpathutils::FillPathWithPaint to also accept SkPathBuilder. The SkPath overloads still work today but binding the SkPathBuilder versions avoids extra path copies in hot paths.", "bindingStatus": "missing", "source": "release-notes", "labels": ["action-needed"], "milestone": 110, "milestones": [146], "skiaApi": "skpathutils::FillPathWithPaint", "csharpMethod": "SKPaint.GetFillPath", "csharpFile": "SKPaint.cs", "obsoleteMessage": "Replace with skpathutils::FillPathWithPaint; M146 prefers SkPathBuilder for the destination.", "userValue": "Avoid copies when chaining path measurement / effect operations."}, {"name": "SkJpegEncoder XMP metadata + YUVA encoding (M112)", "category": "codec", "priority": "medium", "impact": "moderate", "description": "M112 added an XMP metadata parameter to SkJpegEncoder::Options and direct support for encoding SkYUVAPixmaps. Native plumbing for xmpMetadata is present in SkiaApi.generated.cs but not exposed publicly.", "bindingStatus": "partial", "source": "release-notes", "labels": ["quick-win"], "milestone": 112, "skiaApi": "SkJpegEncoder::Options::xmpMetadata, SkJpegEncoder for YUVA", "csharpFile": "binding/SkiaSharp/Definitions.cs", "skillToUse": "add-api", "effort": "small", "cApiFunction": "sk_jpegencoder_encode", "cApiFile": "binding/SkiaSharp/SkiaApi.generated.cs", "csharpMethod": "SKJpegEncoderOptions"}, {"name": "Runtime Effects CPU rewrite", "category": "performance", "priority": "medium", "impact": "moderate", "description": "m114 rewrote the CPU backend for SkRuntimeEffect; expect perf and quality differences when runtime effects render to a raster surface. No binding action required, but worth communicating to authors of complex effects.", "bindingStatus": "not_applicable", "source": "release-notes", "labels": ["behavior-change", "perf"], "milestone": 114, "bindingActionNeeded": false}, {"name": "SkImageFilters::Magnifier (new signature)", "category": "image_filter", "priority": "medium", "impact": "moderate", "description": "m115 deprecated SkImageFilters::Magnifier(srcRect, inset) in favor of the variant with lensBounds + zoomAmount + sampling; m116 removed the old factory entirely. SkiaSharp's CreateMagnifier already takes the new signature (lensBounds, zoomAmount, inset, sampling). Confirm legacy overloads are gone or marked obsolete.", "bindingStatus": "full", "source": "release-notes", "milestone": 115, "milestones": [116], "skiaApi": "SkImageFilters::Magnifier", "cApiFunction": "sk_imagefilter_new_magnifier", "csharpMethod": "SKImageFilter.CreateMagnifier"}, {"name": "SkColorFilters::Table / TableARGB", "category": "color", "priority": "medium", "impact": "minor", "description": "M116 made SkColorFilters::Table/TableARGB the canonical table-color-filter API, deprecating SkTableColorFilter (since removed). SkiaSharp's SKColorFilter.CreateTable already wraps sk_colorfilter_new_table_argb.", "bindingStatus": "full", "source": "release-notes", "milestone": 116, "skiaApi": "SkColorFilters::Table, SkColorFilters::TableARGB", "csharpMethod": "SKColorFilter.CreateTable", "csharpFile": "SKColorFilter.cs"}, {"name": "SkImage::makeWithFilter deprecated", "category": "deprecation", "priority": "medium", "impact": "moderate", "description": "m118 deprecated SkImage::makeWithFilter; replacement is SkImages::MakeWithFilter (with backend-specific overloads). SkiaSharp's sk_image_make_with_filter / sk_image_make_with_filter_raster currently call the old method.", "bindingStatus": "action_needed", "source": "release-notes", "milestone": 118, "skiaApi": "SkImage::makeWithFilter, SkImages::MakeWithFilter", "cApiFunction": "sk_image_make_with_filter, sk_image_make_with_filter_raster", "csharpMethod": "SKImage.ApplyImageFilter", "userValue": "Stay aligned with the modern factory API; required when the deprecated method is removed."}, {"name": "GrDirectContext::submit / flushAndSubmit enum migration", "category": "behavior_change", "priority": "medium", "impact": "moderate", "description": "m119 changed GrDirectContext::submit / flushAndSubmit / performDeferredCleanup / purgeUnlockedResources to take GrSyncCpu / GrPurgeResourceOptions enums instead of bool. SkiaSharp's GRContext methods that take bool will need to forward to the enum-based API.", "bindingStatus": "action_needed", "source": "release-notes", "labels": ["action-needed", "behavior-change"], "milestone": 119, "skiaApi": "GrDirectContext::submit, GrDirectContext::flushAndSubmit, GrSyncCpu, GrPurgeResourceOptions", "csharpMethod": "GRContext.Submit / Flush", "csharpFile": "binding/SkiaSharp/GRContext.cs"}, {"name": "SkPicture serialization fImageProc / SkSerialProcs (no-PNG default)", "category": "behavior_change", "priority": "medium", "impact": "moderate", "description": "m119 changed SkPicture so it no longer serializes images to PNG by default; clients must supply SkSerialProcs::fImageProc. m120 added SkDeserialImageFromDataProc and AlphaType override. SkiaSharp's SKPicture.Serialize() may silently produce pictures that drop image data on round-trip after the bump.", "bindingStatus": "action_needed", "source": "release-notes", "labels": ["action-needed", "behavior-change"], "milestone": 119, "milestones": [120, 144, 146], "skiaApi": "SkSerialProcs::fImageProc, SkDeserialImageFromDataProc", "csharpMethod": "SKPicture.Serialize / Deserialize", "userValue": "Avoid losing images in round-tripped SkPictures.", "skillToUse": "add-api", "effort": "medium"}, {"name": "SkColorFilter::filterColor4f (filterColor removed)", "category": "color", "priority": "medium", "impact": "moderate", "description": "m130 removed SkColorFilter::filterColor (filterColor4f is the replacement; deprecated in m124). SkiaSharp's SKColorFilter does not currently expose either; FilterColor methods on SKColorFilter are not present, but if any path uses the removed C API it will break.", "bindingStatus": "correctly_absent", "source": "release-notes", "milestone": 124, "milestoneRemoved": 130, "skiaApi": "SkColorFilter::filterColor4f", "userValue": "If filtering individual colors is exposed in the future, must use filterColor4f.", "notes": "Verify no shim still references the removed SkColorFilter::filterColor."}, {"name": "SkAndroidCodec::getGainmapAndroidCodec", "category": "codec", "priority": "medium", "impact": "moderate", "description": "m130 added an SkAndroidCodec-returning version of getGainmapAndroidCodec (deprecated the SkStream variant). Relevant only if SkiaSharp ever exposes Android gainmap decoding (Adobe HDR).", "bindingStatus": "missing", "source": "release-notes", "milestone": 130, "skiaApi": "SkAndroidCodec::getGainmapAndroidCodec", "skillToUse": "add-api", "effort": "small", "userValue": "Decode HDR gainmap-encoded JPEGs / HEICs on Android."}, {"name": "SKCanvas clipShader", "category": "canvas", "priority": "medium", "impact": "moderate", "description": "Upstream SkCanvas can clip against a shader, not just geometric primitives. SkiaSharp only exposes rect, path, round-rect, and region clipping today.", "bindingStatus": "missing", "source": "header-scan", "labels": ["hidden-api"], "cppClass": "SkCanvas", "cppHeader": "include/core/SkCanvas.h", "cppMethod": "clipShader(sk_sp<SkShader>, SkClipOp = SkClipOp::kIntersect)", "skiaApi": "SkCanvas::clipShader", "userValue": "Enables mask-like clipping driven by procedural or image shaders without extra offscreen passes.", "notes": "No sk_canvas_clip_shader wrapper was found in the fallback C API or C# bindings.", "skillToUse": "add-api", "effort": "medium"}, {"name": "SKCodec HDR metadata introspection", "category": "codec", "priority": "medium", "impact": "moderate", "description": "SkCodec exposes getHdrMetadata() so callers can inspect encoded HDR mastering/light-level metadata before decoding. The fallback C API has no equivalent, so managed callers cannot decide how to preserve or tone-map HDR inputs based on container metadata.", "bindingStatus": "missing", "source": "header-scan", "labels": ["hidden-api"], "cppClass": "SkCodec", "cppHeader": "include/codec/SkCodec.h", "cppMethod": "const skhdr::Metadata& getHdrMetadata() const", "skiaApi": "SkCodec::getHdrMetadata", "userValue": "Lets apps inspect HDR metadata before decode and make better tone-mapping or export decisions.", "skillToUse": "add-api", "effort": "medium"}, {"name": "SKColorSpace serialization", "category": "color", "priority": "medium", "impact": "moderate", "description": "Upstream SkColorSpace can serialize to SkData and deserialize back into a color-space object. SkiaSharp only exposes profile conversion helpers, which is narrower than round-tripping the SkColorSpace itself.", "bindingStatus": "missing", "source": "header-scan", "labels": ["hidden-api"], "cppClass": "SkColorSpace", "cppHeader": "include/core/SkColorSpace.h", "cppMethod": "serialize() const, Deserialize(const void*, size_t)", "skiaApi": "SkColorSpace::serialize / SkColorSpace::Deserialize", "userValue": "Lets apps persist and restore exact color-space objects without converting them through a looser profile abstraction.", "skillToUse": "add-api", "effort": "medium"}, {"name": "SKPath binary serialization", "category": "path", "priority": "medium", "impact": "moderate", "description": "Upstream SkPath can serialize to SkData and rebuild from a binary memory blob. SkiaSharp exposes SVG conversion helpers, but not the native binary round-trip.", "bindingStatus": "missing", "source": "header-scan", "labels": ["hidden-api"], "cppClass": "SkPath", "cppHeader": "include/core/SkPath.h", "cppMethod": "serialize() const, readFromMemory(const void*, size_t)", "skiaApi": "SkPath::serialize / SkPath::readFromMemory", "userValue": "Makes path caching, transport, and persistence cheaper and more faithful than SVG text conversion.", "skillToUse": "add-api", "effort": "medium"}, {"name": "SKPath interpolation helpers", "category": "path", "priority": "medium", "impact": "significant", "description": "Upstream SkPath can check compatibility for morphing and produce interpolated paths directly. SkiaSharp has path inspection helpers, but not these path-morphing APIs.", "bindingStatus": "missing", "source": "header-scan", "labels": ["hidden-api"], "cppClass": "SkPath", "cppHeader": "include/core/SkPath.h", "cppMethod": "isInterpolatable(const SkPath&) const, interpolate(const SkPath&, SkScalar, SkPath*) const", "skiaApi": "SkPath::isInterpolatable / interpolate", "userValue": "Enables smooth vector morph animations without reimplementing path compatibility checks in user code.", "skillToUse": "add-api", "effort": "medium"}, {"name": "SKPixmap.setColorSpace", "category": "image", "priority": "medium", "impact": "moderate", "description": "The C API already exposes sk_pixmap_set_colorspace, but SKPixmap only exposes ColorSpace and WithColorSpace() rather than an in-place setter. This is a quick managed wrapper gap.", "bindingStatus": "partial", "source": "header-scan", "labels": ["hidden-api", "quick-win"], "cppClass": "SkPixmap", "cppHeader": "include/core/SkPixmap.h", "cppMethod": "setColorSpace(sk_sp<SkColorSpace>)", "skiaApi": "SkPixmap::setColorSpace", "userValue": "Allows in-place color metadata fixes for borrowed pixel buffers.", "cApiFunction": "sk_pixmap_set_colorspace", "cApiFile": "externals/skia/include/c/sk_pixmap.h", "csharpFile": "binding/SkiaSharp/SKPixmap.cs", "notes": "Public SKPixmap exposes ColorSpace and WithColorSpace(), but no in-place setter.", "skillToUse": "add-api", "effort": "small"}, {"name": "SKTextBlob iteration and serialization", "category": "font", "priority": "medium", "impact": "moderate", "description": "Public iterator over the runs of a SkTextBlob (per-run typeface, glyph count, glyph IDs). Lets clients introspect text blobs for glyph caching, atlasing, or debug visualisation.", "bindingStatus": "missing", "source": "header-scan", "labels": ["hidden-api"], "cppClass": "SkTextBlob", "cppHeader": "include/core/SkTextBlob.h", "cppMethod": "Iter, serialize(...), Deserialize(const void*, size_t, ...)", "skillToUse": "add-api", "effort": "medium", "userValue": "Makes it possible to inspect shaped runs and cache pre-shaped text blobs.", "skiaApi": "SkTextBlob::Iter / serialize / Deserialize"}, {"name": "SKTypeface serialization", "category": "font", "priority": "medium", "impact": "moderate", "description": "Upstream SkTypeface can serialize a descriptor or local font data and reconstruct a matching typeface later. SkiaSharp covers variation axes and streams, but not typeface persistence itself.", "bindingStatus": "missing", "source": "header-scan", "labels": ["hidden-api"], "cppClass": "SkTypeface", "cppHeader": "include/core/SkTypeface.h", "cppMethod": "serialize(...), MakeDeserialize(SkStream*, sk_sp<SkFontMgr>)", "skiaApi": "SkTypeface::serialize / SkTypeface::MakeDeserialize", "userValue": "Helps cache, transport, or rehydrate font selections without reopening the original source each time.", "skillToUse": "add-api", "effort": "medium"}, {"name": "SkGradientShader.h header removed (use SkGradient.h)", "category": "deprecation", "priority": "medium", "impact": "moderate", "description": "m146 removed include/effects/SkGradientShader.h; clients must use include/effects/SkGradient.h. Affects SkiaSharp's native shim (sk_shader.cpp) which #includes SkGradientShader.h to call MakeLinear/Radial/Sweep/TwoPointConical. Will fail to compile after the M146+ Skia bump unless updated.", "bindingStatus": "correctly_absent", "source": "release-notes", "labels": ["action-needed", "behavior-change"], "milestoneRemoved": 146, "skiaApi": "include/effects/SkGradientShader.h", "cApiFile": "externals/skia/src/c/sk_shader.cpp", "userValue": "Avoid native shim build break during the M146 bump."}, {"name": "SkTableMaskFilter / SkShaderMaskFilter deprecation", "category": "deprecation", "priority": "medium", "impact": "moderate", "description": "m127 deprecated SkShaderMaskFilters and SkTableMaskFilters with planned removal. SkiaSharp's SKMaskFilter exposes CreateTable / CreateShader equivalents — investigate.", "bindingStatus": "action_needed", "source": "release-notes", "labels": ["action-needed"], "milestoneDeprecated": 127, "skiaApi": "SkShaderMaskFilters, SkTableMaskFilters", "csharpMethod": "SKMaskFilter.CreateTable / CreateShader", "csharpFile": "binding/SkiaSharp/SKMaskFilter.cs"}, {"name": "SkColorInfo / SkAlphaType / SkColorType header split", "category": "color", "priority": "low", "impact": "minor", "description": "M101 broke SkAlphaType and SkColorType into their own headers. No binding impact.", "bindingStatus": "not_applicable", "source": "release-notes", "milestone": 101, "skiaApi": "SkAlphaType, SkColorType", "bindingActionNeeded": false}, {"name": "SkBlenders namespace (Arithmetic)", "category": "behavior_change", "priority": "low", "impact": "minor", "description": "M116 made the intermediate color from SkBlenders::Arithmetic always clamped to [0,1] before applying enforcePremul.", "bindingStatus": "full", "source": "release-notes", "labels": ["behavior-change"], "milestone": 104, "skiaApi": "SkBlenders::Arithmetic", "csharpMethod": "SKBlender.CreateArithmetic"}, {"name": "SkCanvas::getBaseProps / getTopProps", "category": "canvas", "priority": "low", "impact": "minor", "description": "m104 added SkCanvas::getBaseProps (replaces deprecated getProps) and getTopProps (returns props of the active layer). SkiaSharp exposes SKCanvas.SurfaceProps which appears to call the legacy entry point.", "bindingStatus": "missing", "source": "release-notes", "milestone": 104, "skiaApi": "SkCanvas::getBaseProps, SkCanvas::getTopProps", "effort": "trivial", "notes": "Verified absent from both the fallback C API and binding/SkiaSharp."}, {"name": "SkRuntimeEffectBuilder named children + uniforms (string_view)", "category": "shader", "priority": "low", "impact": "minor", "description": "M104 changed SkRuntimeEffect::Uniform/Child to use std::string_view for names, and Builder methods accept std::string_view.", "bindingStatus": "not_applicable", "source": "release-notes", "milestone": 104, "skiaApi": "SkRuntimeEffect::findUniform/findChild, Builder::uniform/child", "notes": "The string_view change is source-level C++ API churn; SkiaSharp runtime effect APIs already accept managed strings and do not need a separate binding surface."}, {"name": "SkColor4f::toBytes_RGBA / FromBytes_RGBA exported (m107)", "category": "color", "priority": "low", "impact": "minor", "description": "Upstream exported utility byte-conversion helpers for SkColor4f, but SkiaSharp already has managed SKColor/SKColorF conversion helpers and no dedicated native binding surface here. This is primarily a convenience change in C++, not a blocker for managed callers.", "bindingStatus": "not_applicable", "source": "release-notes", "milestone": 107, "skiaApi": "SkColor4f::toBytes_RGBA / FromBytes_RGBA"}, {"name": "SkPerlinNoiseShader -> SkShaders::MakeFractalNoise/MakeTurbulence", "category": "shader", "priority": "low", "impact": "minor", "description": "M116 moved Perlin noise factories into the SkShaders namespace; the C API and SkiaSharp's SKShader.CreatePerlinNoiseFractalNoise/Turbulence already exist and continue to work.", "bindingStatus": "full", "source": "release-notes", "milestone": 116, "skiaApi": "SkShaders::MakeFractalNoise / MakeTurbulence", "csharpMethod": "SKShader.CreatePerlinNoiseFractalNoise / CreatePerlinNoiseTurbulence"}, {"name": "SkCanvas::recorder / recordingContext now const", "category": "behavior_change", "priority": "low", "impact": "minor", "description": "m117 made SkCanvas::recordingContext() and recorder() const. Source-compatible; ABI unaffected for SkiaSharp.", "bindingStatus": "not_applicable", "source": "release-notes", "milestone": 117}, {"name": "SkCanvas::recordingContext / recorder", "category": "canvas", "priority": "low", "impact": "minor", "description": "M117 marked these as const (no functional change).", "bindingStatus": "not_applicable", "source": "release-notes", "milestone": 117, "skiaApi": "SkCanvas::recordingContext, SkCanvas::recorder", "bindingActionNeeded": false}, {"name": "Perlin noise rotation correctness + raster perf", "category": "performance", "priority": "low", "impact": "moderate", "description": "m124: SkShaders::MakeFractalNoise/MakeTurbulence now rotate correctly under transforms; raster performance significantly improved. Automatic for SkiaSharp users — no binding work, but a noticeable behavior + perf change.", "bindingStatus": "not_applicable", "source": "release-notes", "labels": ["behavior-change", "perf"], "milestone": 124, "skiaApi": "SkShaders::MakeFractalNoise, SkShaders::MakeTurbulence", "userValue": "Automatic speedup and correctness fix for apps using Perlin noise on raster.", "bindingActionNeeded": false}, {"name": "SkColorFilter::filterColor removed", "category": "deprecation", "priority": "low", "impact": "minor", "description": "M124 deprecated and M130 removed SkColorFilter::filterColor in favor of filterColor4f. SkiaSharp does not expose either; both new and old are absent.", "bindingStatus": "correctly_absent", "source": "release-notes", "milestone": 124, "milestoneRemoved": 130, "skiaApi": "SkColorFilter::filterColor / filterColor4f", "replacement": "filterColor4f", "skillToUse": "add-api", "effort": "trivial"}, {"name": "GpuStats / GpuFinishedWithStats callbacks", "category": "performance", "priority": "low", "impact": "moderate", "description": "M133 added GPU elapsed-time stats callbacks for both Graphite (skgpu::graphite::GpuFinishedWithStatsProc + GpuStats) and Ganesh (GrGpuFinishedWithStatsProc, set on GrFlushInfo with a stats flag). Implemented for Dawn and GL/GLES/WebGL.", "bindingStatus": "missing", "source": "release-notes", "labels": ["perf"], "milestone": 133, "skiaApi": "GrFlushInfo + GrGpuFinishedWithStatsProc, skgpu::graphite::GpuFinishedWithStatsProc, skgpu::GpuStats", "userValue": "Lets profilers and games measure actual GPU work time per submit/recording.", "skillToUse": "add-api", "effort": "medium"}, {"name": "SkAnimatedImage / SkAnimCodecPlayer removal", "category": "deprecation", "priority": "low", "impact": "minor", "description": "M123 removed SkAnimCodecPlayer from the public API.", "bindingStatus": "correctly_absent", "source": "release-notes", "milestoneRemoved": 123, "skiaApi": "SkAnimCodecPlayer"}, {"name": "SkBlendMode_AsCoeff (Porter-Duff coefficient query)", "category": "utility", "priority": "low", "impact": "minor", "description": "The Porter-Duff coefficient query helper from upstream Skia is still absent from the shim and managed API. It is mainly useful for completeness and tooling.", "bindingStatus": "missing", "source": "header-scan", "labels": ["hidden-api"], "cppHeader": "include/core/SkBlendMode.h", "cppMethod": "SkBlendMode_AsCoeff", "skillToUse": "add-api", "effort": "small"}, {"name": "SkCanvas::flush() removed", "category": "deprecation", "priority": "low", "impact": "minor", "description": "m117 removed SkCanvas::flush(). Migrate to GrAsDirectContext(canvas->recordingContext())->flushAndSubmit(). SkiaSharp's SKCanvas.Flush has long been routed through SKSurface; verify the shim doesn't still call SkCanvas::flush.", "bindingStatus": "action_needed", "source": "release-notes", "milestoneRemoved": 117, "skiaApi": "SkCanvas::flush", "csharpMethod": "SKCanvas.Flush", "csharpFile": "binding/SkiaSharp/SKCanvas.cs"}, {"name": "SkImageFilters::Image single-arg removed", "category": "deprecation", "priority": "low", "impact": "minor", "description": "M116 removed the single-argument SkImageFilters::Image; SkSamplingOptions must now be supplied (kLinear recommended over the previous bicubic default).", "bindingStatus": "correctly_absent", "source": "release-notes", "milestoneRemoved": 116, "skiaApi": "SkImageFilters::Image"}, {"name": "SkMergePathEffect / Matrix / Stroke / StrokeAndFill removed", "category": "deprecation", "priority": "low", "impact": "minor", "description": "m119 removed SkMergePathEffect, SkMatrixPathEffect, SkStrokePathEffect, SkStrokeAndFillPathEffect from public API. Clients can implement on SkPath directly. Confirm no SkiaSharp wrappers reference these.", "bindingStatus": "correctly_absent", "source": "release-notes", "milestoneRemoved": 119, "skiaApi": "SkMergePathEffect/SkMatrixPathEffect/SkStrokePathEffect/SkStrokeAndFillPathEffect"}], "nextSteps": [{"severity": "critical", "action": "Bind SkPathBuilder before bumping past M143", "reason": "SkPath is becoming immutable. Once SK_HIDE_PATH_EDIT_METHODS becomes the default, every SKPath edit method (MoveTo/LineTo/QuadTo/CubicTo/ArcTo/etc.) breaks. SkPathBuilder is the only supported way to construct paths going forward.", "milestone": 143, "skillToUse": "add-api", "effort": "large"}, {"severity": "critical", "action": "Remove fICCProfile / fICCProfileDescription from SKPngEncoderOptions, SKJpegEncoderOptions, SKWebpEncoderOptions", "reason": "These struct members were deleted upstream in M142. The next Skia bump will produce a struct ABI mismatch and likely undefined behavior in encoders.", "milestone": 142, "skillToUse": "issue-fix", "effort": "medium"}, {"severity": "high", "action": "Add SkPath immutable factories (Raw/Rect/Oval/Circle/RRect/Polygon/Line) and mark SKPath edit methods [Obsolete]", "reason": "Gives SkiaSharp users a forward-compatible migration path before the edit methods disappear.", "milestone": 143, "skillToUse": "add-api", "effort": "medium"}, {"severity": "high", "action": "Bind gradient interpolation (SkGradientShader::Interpolation) for all gradient factories", "reason": "CSS Color Level 4 gradient interpolation is a visible quality improvement; M134 added P3/Rec2020/A98/ProPhoto color spaces. High-value across UI, charts, web rendering.", "milestone": 114, "skillToUse": "add-api", "effort": "medium"}, {"severity": "high", "action": "Bind SkImageFilters::Crop with SkTileMode and SkImageFilters::RuntimeShader", "reason": "Crop with tile mode is the modern way to bound filter output; RuntimeShader unlocks custom GPU effects in filter graphs.", "milestone": 119, "skillToUse": "add-api", "effort": "medium"}, {"severity": "high", "action": "Bind SkMesh / SkMeshSpecification", "reason": "Enables a new category of apps (custom geometry, particle systems, deformable meshes). Long-running gap (m106).", "milestone": 106, "skillToUse": "add-api", "effort": "large"}, {"severity": "high", "action": "Wire AVIF and JPEG XL decoders through SkCodecs::Register", "reason": "Enums exist but decoders aren't registered, so SkiaSharp users can't actually decode AVIF/JXL even though the upstream code is available.", "milestone": 115, "skillToUse": "add-api", "effort": "medium"}, {"severity": "medium", "action": "Bind async rescale & readback (RGBA/YUV420/YUVA420)", "reason": "Critical for video and streaming workflows; non-blocking GPU readback is the correct pattern.", "milestone": 117, "skillToUse": "add-api", "effort": "large"}, {"severity": "medium", "action": "Replace deprecated SkTypeface static factories with SKFontManager-based paths", "reason": "SkTypeface::MakeFrom* was deprecated in M120; SkiaSharp's SKTypeface.FromFamilyName/FromFile/FromStream/FromData all rely on these.", "milestone": 120, "skillToUse": "issue-fix", "effort": "medium"}, {"severity": "medium", "action": "Mark SKMaskFilter table-based factories [Obsolete]", "reason": "SkTableMaskFilters / SkShaderMaskFilters are deprecated upstream. Users need warning before removal.", "milestone": 127, "skillToUse": "issue-fix", "effort": "small"}, {"severity": "medium", "action": "Bind skhdr::Metadata + HDR PNG metadata accessors", "reason": "HDR is a transformative capability for image-rendering apps; without bindings users can't preserve or author HDR PNGs.", "milestone": 142, "skillToUse": "add-api", "effort": "large"}, {"severity": "medium", "action": "Add SkImage.makeScaled, SkImage.reinterpretColorSpace, SkBitmap.SetColorSpace, SkPixmap/SkBitmap.GetColor4f", "reason": "Cluster of small image/bitmap helpers users hit constantly; each is small, the set is significant.", "milestone": 128, "skillToUse": "add-api", "effort": "medium"}, {"severity": "medium", "action": "Quick wins: SaveLayerAlphaf, SaveLayerRec backdrop tile mode, SkData.Equals, SkColorSpacePrimaries equality, SkSurfaceProps kAlwaysDither_Flag/kDefault_Flag, SkShaders.CoordClamp, SkColorFilters.Blend(SkColor4f), SkImageFilters.DropShadow(SkColor4f), expose XMP metadata fields on JPEG/WebP encoder options", "reason": "All trivial-to-small effort, mostly net-new helpers that fill obvious gaps and (for the encoder XMP fields) only need a public wrapper around already-generated plumbing.", "skillToUse": "add-api", "effort": "small"}, {"severity": "medium", "action": "Bind codec introspection: isAnimated, hasHighBitDepthEncodedData, fMaxDecodeMemory + kOutOfMemory, SelectionPolicy", "reason": "Defensive decoding is essential for apps that load untrusted images; isAnimated removes ambiguity around RepetitionCount.", "milestone": 145, "skillToUse": "add-api", "effort": "small"}, {"severity": "low", "action": "Document behavior changes for downstream users: kRec709 gamma 2.4 (M142), kHLG/kPQ skcms (M141), SkShaders::Color always color-managed (M134), SkCodec::getImage honors EXIF (M123), SkImageFilters Image/Picture/Shader return non-null (M115)", "reason": "These are silent behavior changes that may produce slight color shifts, rotated images, or different null-handling after the next bump. Worth a single 'behavior changes' release-notes entry.", "effort": "trivial"}, {"severity": "critical", "action": "Create SkPathBuilder bindings and a managed migration story", "reason": "SkPath editing APIs are moving away from mutable paths and SkiaSharp has no replacement surface.", "milestone": 143, "skillToUse": "add-api", "effort": "large"}, {"severity": "high", "action": "Bind SkImageFilters::RuntimeShader and Crop-with-tile-mode", "reason": "These are high-value image-filter gaps that unlock modern effect graphs and are still fully absent.", "milestone": 119, "skillToUse": "add-api", "effort": "medium"}, {"severity": "high", "action": "Plan HDR metadata support (skhdr::Metadata + AGTM + HDR PNG)", "reason": "This unlocks end-to-end HDR image workflows that SkiaSharp currently cannot expose.", "milestone": 142, "skillToUse": "add-api", "effort": "large"}, {"severity": "high", "action": "Evaluate SkMesh binding design", "reason": "SkMesh is a transformative GPU feature and remains completely unavailable to SkiaSharp users.", "milestone": 106, "skillToUse": "add-api", "effort": "large"}, {"severity": "high", "action": "Add async rescale/readback APIs for SKImage and SKSurface", "reason": "Non-blocking readback is a major gap for video, streaming, and GPU interop scenarios.", "milestone": 117, "skillToUse": "add-api", "effort": "large"}, {"severity": "high", "action": "Expose gradient interpolation options for wide-gamut gradients", "reason": "Users miss a visible quality improvement for CSS Color 4 style gradients.", "milestone": 114, "skillToUse": "add-api", "effort": "medium"}, {"severity": "high", "action": "Audit encoder metadata option types against M142 changes", "reason": "Generated interop still contains removed ICC fields while newer metadata fields are still hidden from public APIs.", "milestone": 142, "skillToUse": "add-api", "effort": "medium"}, {"severity": "medium", "action": "Promote hidden C-API plumbing like SKPixmap.setColorSpace and encoder metadata", "reason": "Several quick wins already have native or generated plumbing and only need managed surface area.", "milestone": 125, "skillToUse": "add-api", "effort": "small"}, {"severity": "medium", "action": "Add codec introspection helpers (isAnimated, high-bit-depth, max decode memory)", "reason": "These fill practical codec gaps around safety and format inspection.", "milestone": 132, "skillToUse": "add-api", "effort": "medium"}, {"severity": "medium", "action": "Audit native codec configuration for AVIF and JPEG XL", "reason": "Managed enums exist, but practical support still depends on native dependency configuration and should be confirmed before advertising full support.", "milestone": 106, "skillToUse": "native-dependency-update", "effort": "medium"}]};</script>
<div class="shell mx-auto px-3 py-3">
<div class="d-flex align-items-center justify-content-between mb-2">
<div><strong style="font-size:1.15rem">Skia Feature Scout</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('nextsteps')">Next Steps <span id="nextsteps-count" class="count-badge"></span></button>
</div>
<!-- Findings Tab -->
<div class="tab-pane active" id="tab-findings">
<div class="filter-bar" id="findings-filters">
<div class="filter-row">
<span class="filter-label">Search:</span>
<input type="text" class="search-box" style="width:300px" id="search" placeholder="Search findings...">
</div>
<div class="filter-row">
<span class="filter-label">Milestone:</span>
<input type="number" id="ms-min" class="search-box" style="width:70px;text-align:right" placeholder="min" oninput="updateMsFilter()">
<span style="color:var(--muted);font-size:.75rem">–</span>
<input type="number" id="ms-max" class="search-box" style="width:70px;text-align:right" placeholder="max" oninput="updateMsFilter()">
</div>
<div class="filter-row" id="fr-priority"></div>
<div class="filter-row" id="fr-impact"></div>
<div class="filter-row" id="fr-status"></div>
<div class="filter-row" id="fr-category"></div>
<div class="filter-row" id="fr-label"></div>
<div class="filter-row">
<span class="filter-label">Group by:</span>
<select class="ctrl-select" id="group-by" onchange="rebuildList()">
<option value="none">None</option>
<option value="priority">Priority</option>
<option value="impact">Impact</option>
<option value="bindingStatus">Status</option>
<option value="category">Category</option>
<option value="milestone">Milestone</option>
<option value="source">Source</option>
</select>
<button class="dir-btn" id="group-dir" onclick="toggleDir('group-dir');rebuildList()" title="Toggle group order">▼</button>
<span style="width:16px"></span>
<span class="filter-label" style="width:55px">Sort by:</span>
<select class="ctrl-select" id="sort-by" onchange="rebuildList()">
<option value="action">Action Score ↕</option>
<option value="priority">Priority</option>
<option value="impact">Impact</option>
<option value="milestone">Milestone</option>
<option value="name">Name</option>
<option value="status">Status</option>
<option value="category">Category</option>
</select>
<button class="dir-btn" id="sort-dir" onclick="toggleDir('sort-dir');rebuildList()" title="Toggle sort order">▼</button>
</div>
</div>
<div class="text-muted d-flex justify-content-between" style="font-size:.72rem;padding:0 12px 4px">
<span id="filter-count"></span>
<button class="reset-btn" onclick="resetFilters('findings')">Reset filters</button>
</div>
<div id="list"></div>
</div>
<!-- Next Steps Tab -->
<div class="tab-pane" id="tab-nextsteps">
<div class="filter-bar" id="nextsteps-filters">
<div class="filter-row">
<span class="filter-label">Search:</span>
<input type="text" class="search-box" style="width:300px" id="ns-search" placeholder="Search steps...">
</div>
<div class="filter-row" id="nfr-severity"></div>
<div class="filter-row" id="nfr-effort"></div>
<div class="filter-row" id="nfr-skill"></div>
</div>
<div class="text-muted d-flex justify-content-between" style="font-size:.72rem;padding:0 12px 4px">
<span id="ns-filter-count"></span>
<button class="reset-btn" onclick="resetFilters('nextsteps')">Reset filters</button>
</div>
<div id="next-steps"></div>
</div>
<div class="meta-footer" id="meta-footer"></div>
</div>
<script>
const SL={full:'✅ Full',partial:'🟡 Partial',missing:'❌ Missing',action_needed:'⚠️ Action',not_applicable:'N/A',correctly_absent:'N/A'};
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'};
const PO={critical:0,high:1,medium:2,low:3};
const SO={missing:0,action_needed:1,partial:2,full:3,not_applicable:4,correctly_absent:5};
const IMO={transformative:0,significant:1,moderate:2,minor:3};
const IML={transformative:'🚀 Transformative',significant:'⭐ Significant',moderate:'📦 Moderate',minor:'🔹 Minor'};
let fF={priority:new Set(),status:new Set(),category:new Set(),label:new Set(),impact:new Set()};
let nF={severity:new Set(),effort:new Set(),skill:new Set()};
let sT='',nsT='',msMin=0,msMax=999;
function esc(s){const d=document.createElement('div');d.textContent=s||'';return d.innerHTML;}
function toggleDir(id){const b=document.getElementById(id);b.textContent=b.textContent==='▼'?'▲':'▼';}
function getDir(id){return document.getElementById(id).textContent==='▲'?-1:1;}
function switchTab(t){document.querySelectorAll('.tab-pane').forEach(p=>p.classList.remove('active'));document.querySelectorAll('.tab-btn').forEach(b=>b.classList.remove('active'));document.getElementById('tab-'+t).classList.add('active');document.querySelector(`[onclick="switchTab('${t}')"]`).classList.add('active');}
function tF(scope,type,val){const f=scope==='findings'?fF:nF;f[type].has(val)?f[type].delete(val):f[type].add(val);scope==='findings'?applyAndRebuild():aNF();}
function resetFilters(scope){if(scope==='findings'){Object.values(fF).forEach(s=>s.clear());sT='';msMin=0;msMax=999;document.getElementById('search').value='';document.getElementById('ms-min').value='';document.getElementById('ms-max').value='';applyAndRebuild();}else{Object.values(nF).forEach(s=>s.clear());nsT='';document.getElementById('ns-search').value='';aNF();}}
function updateMsFilter(){msMin=parseInt(document.getElementById('ms-min').value)||0;msMax=parseInt(document.getElementById('ms-max').value)||999;applyAndRebuild();}
function passesFilter(f){if(fF.priority.size&&!fF.priority.has(f.priority))return false;if(fF.status.size&&!fF.status.has(f.bindingStatus))return false;if(fF.category.size&&!fF.category.has(f.category))return false;if(fF.label.size&&!(f.labels||[]).some(l=>fF.label.has(l)))return false;if(fF.impact.size&&!fF.impact.has(f.impact))return false;if(sT&&!JSON.stringify(f).toLowerCase().includes(sT))return false;const m=f.milestone||0;if(m&&(m<msMin||m>msMax))return false;return true;}
function sortFindings(arr){const s=document.getElementById('sort-by').value;const dir=getDir('sort-dir');const EO={trivial:4,small:3,medium:2,large:1};return[...arr].sort((a,b)=>{let r=0;switch(s){case'priority':r=(PO[a.priority]??4)-(PO[b.priority]??4);if(r===0)r=(b.milestone||0)-(a.milestone||0);break;case'milestone':r=(a.milestone||0)-(b.milestone||0);break;case'name':r=(a.name||'').localeCompare(b.name||'');break;case'status':r=(SO[a.bindingStatus]??9)-(SO[b.bindingStatus]??9);break;case'category':r=(a.category||'').localeCompare(b.category||'');break;case'impact':r=(IMO[a.impact]??9)-(IMO[b.impact]??9);if(r===0)r=(PO[a.priority]??4)-(PO[b.priority]??4);break;case'action':const sa=(4-(IMO[a.impact]??3))*(4-(PO[a.priority]??3))*(EO[a.effort]||2);const sb=(4-(IMO[b.impact]??3))*(4-(PO[b.priority]??3))*(EO[b.effort]||2);r=sb-sa;break;}return r*dir;});}
function groupKey(f){const g=document.getElementById('group-by').value;switch(g){case'priority':return f.priority;case'bindingStatus':return f.bindingStatus;case'category':return f.category;case'milestone':return f.milestone?'m'+f.milestone:'Unknown';case'source':return f.source||'unknown';case'impact':return f.impact||'unset';default:return null;}}
function groupLabel(key,by){switch(by){case'priority':return key.charAt(0).toUpperCase()+key.slice(1);case'bindingStatus':return SL[key]||key;case'category':return CL[key]||key;case'impact':return IML[key]||key;default:return key;}}
function applyAndRebuild(){rebuildList();document.querySelectorAll('#tab-findings .pill[data-filter]').forEach(p=>{const[t,v]=p.dataset.filter.split(':');p.classList.toggle('dim',fF[t]?.size>0&&!fF[t].has(v));});}
function rebuildList(){const filtered=DATA.findings.filter(passesFilter);const sorted=sortFindings(filtered);document.getElementById('filter-count').textContent=`Showing ${filtered.length} of ${DATA.findings.length}`;const by=document.getElementById('group-by').value;const el=document.getElementById('list');if(by==='none'){el.innerHTML=sorted.map(renderRow).join('');return;}
const groups={};sorted.forEach(f=>{const k=groupKey(f);if(!groups[k])groups[k]=[];groups[k].push(f);});const gOrder=Object.keys(groups);const gDir=getDir('group-dir');if(by==='priority')gOrder.sort((a,b)=>((PO[a]??9)-(PO[b]??9))*gDir);else if(by==='bindingStatus')gOrder.sort((a,b)=>((SO[a]??9)-(SO[b]??9))*gDir);else if(by==='impact')gOrder.sort((a,b)=>((IMO[a]??9)-(IMO[b]??9))*gDir);else if(by==='milestone')gOrder.sort((a,b)=>{const na=parseInt(a.replace('m',''))||0,nb=parseInt(b.replace('m',''))||0;return(na-nb)*gDir;});else gOrder.sort((a,b)=>a.localeCompare(b)*gDir);
el.innerHTML=gOrder.map(k=>`<div class="group-header">${groupLabel(k,by)} <span class="count-badge">${groups[k].length}</span></div>`+groups[k].map(renderRow).join('')).join('');}
function aNF(){let n=0;document.querySelectorAll('.ns-item').forEach(el=>{const d=JSON.parse(el.dataset.step);let v=true;if(nF.severity.size&&!nF.severity.has(d.severity))v=false;if(nF.effort.size&&!nF.effort.has(d.effort))v=false;if(nF.skill.size&&!nF.skill.has(d.skillToUse))v=false;if(nsT&&!JSON.stringify(d).toLowerCase().includes(nsT))v=false;el.style.display=v?'':'none';if(v)n++;});document.getElementById('ns-filter-count').textContent=`Showing ${n} of ${(DATA.nextSteps||[]).length}`;document.querySelectorAll('#tab-nextsteps .pill[data-filter]').forEach(p=>{const[t,v]=p.dataset.filter.split(':');p.classList.toggle('dim',nF[t]?.size>0&&!nF[t].has(v));});}
function renderSummary(s){document.getElementById('summary-bar').innerHTML=[['Total',s.totalFindings||0,'#1f2328'],['Missing',s.byStatus?.missing||0,'#991b1b'],['Partial',s.byStatus?.partial||0,'#92400e'],['Full',s.byStatus?.full||0,'#166534'],['Action',s.byStatus?.action_needed||0,'#9d174d'],['Hidden',s.bySource?.['header-scan']||0,'#5b21b6']].map(([l,n,c])=>`<div class="text-center"><div class="summary-num" style="color:${c}">${n}</div><div class="summary-label">${l}</div></div>`).join('');}
function addPillRow(rowId,label,scope,type,vals,pre){const row=document.getElementById(rowId);row.innerHTML=`<span class="filter-label">${label}:</span>`+vals.map(v=>{const cls=pre?`${pre}${v}`:'l-pill';const lb=type==='category'?(CL[v]||v):type==='status'?(SL[v]||v):v;return`<span class="pill ${cls}" data-filter="${type}:${v}" onclick="tF('${scope}','${type}','${v}')">${lb}</span>`;}).join(' ');}
function renderRow(f){const ms=f.milestone?`<span class="ms">m${f.milestone}</span>`:'';const xms=(f.milestones||[]).map(m=>`<span class="ms" style="opacity:.5">m${m}</span>`).join(' ');const labels=(f.labels||[]).map(l=>`<span class="pill l-pill" data-filter="label:${l}" onclick="event.stopPropagation();tF('findings','label','${l}')">${l}</span>`).join(' ');const cat=`<span class="pill c-${f.category}" data-filter="category:${f.category}" onclick="event.stopPropagation();tF('findings','category','${f.category}')">${CL[f.category]||f.category}</span>`;const pri=`<span class="pill p-${f.priority}" data-filter="priority:${f.priority}" onclick="event.stopPropagation();tF('findings','priority','${f.priority}')">${f.priority}</span>`;const st=`<span class="pill s-${f.bindingStatus}" data-filter="status:${f.bindingStatus}" onclick="event.stopPropagation();tF('findings','status','${f.bindingStatus}')">${SL[f.bindingStatus]||f.bindingStatus}</span>`;const imp=f.impact?`<span class="pill imp-${f.impact}" data-filter="impact:${f.impact}" onclick="event.stopPropagation();tF('findings','impact','${f.impact}')">${IML[f.impact]||f.impact}</span>`:'';
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> (${esc(f.cppHeader||'')})</div>`;if(f.cApiFunction)det+=`<div class="detail-label">C API</div><div class="detail-value"><code>${esc(f.cApiFunction)}</code> (${esc(f.cApiFile||'')})</div>`;else if(f.bindingStatus==='missing')det+=`<div class="detail-label">C API</div><div class="detail-value" style="color:#991b1b">Not in C API</div>`;if(f.csharpMethod)det+=`<div class="detail-label">C#</div><div class="detail-value"><code>${esc(f.csharpMethod)}</code> (${esc(f.csharpFile||'')})</div>`;else if(f.bindingStatus==='missing'||f.bindingStatus==='partial')det+=`<div class="detail-label">C#</div><div class="detail-value" style="color:#991b1b">Not bound</div>`;if(f.replacement)det+=`<div class="detail-label">Replace</div><div class="detail-value"><code>${esc(f.replacement)}</code></div>`;if(f.obsoleteMessage)det+=`<div class="detail-label">[Obsolete]</div><div class="detail-value"><code>${esc(f.obsoleteMessage)}</code></div>`;if(f.impact)det+=`<div class="detail-label">Impact</div><div class="detail-value">${esc(f.impact)}</div>`;if(f.milestoneDeprecated)det+=`<div class="detail-label">Deprecated</div><div class="detail-value">m${f.milestoneDeprecated}</div>`;if(f.milestoneRemoved)det+=`<div class="detail-label">Removed</div><div class="detail-value">m${f.milestoneRemoved}</div>`;if(f.skillToUse)det+=`<div class="detail-label">Skill</div><div class="detail-value"><code>${esc(f.skillToUse)}</code>${f.effort?` (${f.effort})`:''}</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" data-finding='${JSON.stringify(f).replace(/'/g,"&#39;")}' 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)}</div><div class="row-pills">${ms} ${cat} ${pri} ${imp} ${st} ${labels} ${xms}</div><div class="row-desc">${esc(f.description)}</div><div class="row-detail">${det}</div></div></div></div>`;}
function renderNS(steps){if(!steps||!steps.length)return;document.getElementById('next-steps').innerHTML=steps.map(s=>{const cls='ns-'+(s.severity||'low');return`<div class="next-step ns-item ${cls}" data-step='${JSON.stringify(s).replace(/'/g,"&#39;")}'><div><strong>${esc(s.action)}</strong></div><div style="margin-top:3px"><span class="pill p-${s.severity||'low'}" data-filter="severity:${s.severity}" onclick="event.stopPropagation();tF('nextsteps','severity','${s.severity}')">${s.severity||''}</span>${s.milestone?` <span class="ms">m${s.milestone}</span>`:''}${s.effort?` <span class="pill" style="background:#e5e7eb;color:#374151" data-filter="effort:${s.effort}" onclick="event.stopPropagation();tF('nextsteps','effort','${s.effort}')">${s.effort}</span>`:''}${s.skillToUse?` <span class="pill l-pill" data-filter="skill:${s.skillToUse}" onclick="event.stopPropagation();tF('nextsteps','skill','${s.skillToUse}')">${s.skillToUse}</span>`:''}</div><div style="color:var(--muted);font-size:.78rem;margin-top:3px">${esc(s.reason)}</div></div>`;}).join('');}
document.addEventListener('DOMContentLoaded',()=>{if(typeof DATA==='undefined'){document.body.innerHTML='<p style="padding:2rem;color:red">No DATA found.</p>';return;}const m=DATA.meta;const mode=m.mode==='windowed'?`m${m.previousMilestone}→m${m.currentMilestone}`:`m${m.currentMilestone} (full)`;document.getElementById('subtitle').textContent=`${m.date} · ${mode}`;renderSummary(DATA.summary);
addPillRow('fr-priority','Priority','findings','priority',['critical','high','medium','low'],'p-');
const imps=[...new Set(DATA.findings.map(f=>f.impact).filter(Boolean))];if(imps.length)addPillRow('fr-impact','Impact','findings','impact',['transformative','significant','moderate','minor'].filter(v=>imps.includes(v)),'imp-');
addPillRow('fr-status','Status','findings','status',['missing','partial','full','action_needed','not_applicable'],'s-');
addPillRow('fr-category','Category','findings','category',[...new Set(DATA.findings.map(f=>f.category))].sort(),'c-');
const lbls=[...new Set(DATA.findings.flatMap(f=>f.labels||[]))].sort();if(lbls.length)addPillRow('fr-label','Label','findings','label',lbls,'');
const sev=[...new Set((DATA.nextSteps||[]).map(s=>s.severity).filter(Boolean))];
const eff=[...new Set((DATA.nextSteps||[]).map(s=>s.effort).filter(Boolean))];
const sk=[...new Set((DATA.nextSteps||[]).map(s=>s.skillToUse).filter(Boolean))];
if(sev.length)addPillRow('nfr-severity','Severity','nextsteps','severity',sev,'p-');
if(eff.length)addPillRow('nfr-effort','Effort','nextsteps','effort',eff,'');
if(sk.length)addPillRow('nfr-skill','Skill','nextsteps','skill',sk,'');
document.getElementById('findings-count').textContent=DATA.findings.length;document.getElementById('nextsteps-count').textContent=(DATA.nextSteps||[]).length;renderNS(DATA.nextSteps);document.getElementById('meta-footer').innerHTML=`Source: <a href="${m.releaseNotesSource}">RELEASE_NOTES.md</a> · ${m.date}`+(m.skiaSubmoduleCommit?` · <a href="https://github.com/mono/skia/commit/${m.skiaSubmoduleCommit}">${m.skiaSubmoduleCommit.slice(0,10)}</a>`:'');
document.getElementById('search').addEventListener('input',e=>{sT=e.target.value.toLowerCase();applyAndRebuild();});document.getElementById('ns-search').addEventListener('input',e=>{nsT=e.target.value.toLowerCase();aNF();});rebuildList();aNF();});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment