Skip to content

Fix SkiaSharp obsolete warnings in generated output#526

Merged
wieslawsoltes merged 6 commits into
masterfrom
codex/fix-skiasharp-obsolete-warnings
May 12, 2026
Merged

Fix SkiaSharp obsolete warnings in generated output#526
wieslawsoltes merged 6 commits into
masterfrom
codex/fix-skiasharp-obsolete-warnings

Conversation

@wieslawsoltes
Copy link
Copy Markdown
Owner

Fix SkiaSharp Obsolete Warnings in Generated Output

Summary

This PR fixes the SkiaSharp obsolete-warning area reported in issue #293 by migrating generated SkiaSharp 3 output away from deprecated text, font, and image-filter APIs.

The main change is that generated C# now emits SKFont and SKSamplingOptions for SkiaSharp 3 drawing calls instead of placing font and sampling state on SKPaint.

What Changed

  • Added shim support for SkiaSharp 3-style APIs:
    • SKFont and SKFontEdging
    • SKSamplingOptions, SKFilterMode, SKMipmapMode, and SKCubicResampler
    • SKCanvas.DrawImage(..., SKSamplingOptions, SKPaint?)
    • SKCanvas.DrawText(..., SKTextAlign, SKFont, SKPaint)
    • SKCanvas.DrawTextOnPath(..., SKTextAlign, SKFont, SKPaint)
    • SKTextBlob.CreatePositioned(string?, SKFont, SKPoint[]?)
  • Updated Svg.CodeGen.Skia so generated output no longer emits obsolete SkiaSharp 3 APIs such as:
    • SKPaint.Typeface
    • SKPaint.TextSize
    • SKPaint.LcdRenderText
    • SKPaint.SubpixelText
    • SKPaint.TextEncoding
    • SKPaint.FilterQuality
    • SKPaint.ToFont()
    • SKCanvas.DrawText(string, float, float, SKPaint)
  • Preserved legacy shim command compatibility by mapping existing SKPaint.FilterQuality values to SKSamplingOptions when generating code from older command shapes.
  • Preserved explicit command-level SKSamplingOptions when the new image overload is used.
  • Added clone and code-generation tests covering the new shim/font/sampling paths.
  • Tightened two nullable-flow warnings in text shaping/source-generator measurement code.

Root Cause

Projects that consume build/SkiaSharp.v3.props compile generated C# against SkiaSharp 3. The generated source still used SkiaSharp 2-style APIs where font state and filter quality lived on SKPaint, so clean Release builds emitted obsolete warnings from generated files.

SkiaSharp 3 moved text/font state to SKFont, moved image sampling to SKSamplingOptions, and added drawing overloads that take explicit SKTextAlign, SKFont, and sampling options.

Upstream API Notes

The migration was checked against upstream SkiaSharp sources:

  • binding/SkiaSharp/SKCanvas.cs
  • binding/SkiaSharp/SKPaint.cs
  • binding/SkiaSharp/SKFont.cs
  • binding/SkiaSharp/Definitions.cs
  • SkiaSharp 3.0 changelog

Important mappings used:

  • SKPaint.TextSize -> new SKFont(typeface, size)
  • SKPaint.Typeface -> new SKFont(typeface, size)
  • SKPaint.LcdRenderText -> SKFont.Edging = SKFontEdging.SubpixelAntialias
  • SKPaint.SubpixelText -> SKFont.Subpixel
  • SKFilterQuality.None -> new SKSamplingOptions(SKFilterMode.Nearest, SKMipmapMode.None)
  • SKFilterQuality.Low -> new SKSamplingOptions(SKFilterMode.Linear, SKMipmapMode.None)
  • SKFilterQuality.Medium -> new SKSamplingOptions(SKFilterMode.Linear, SKMipmapMode.Linear)
  • SKFilterQuality.High -> new SKSamplingOptions(SKCubicResampler.Mitchell)

Validation

  • dotnet format Svg.Skia.slnx --no-restore
  • dotnet clean Svg.Skia.slnx -c Release
  • dotnet build Svg.Skia.slnx -c Release
  • dotnet test Svg.Skia.slnx -c Release

Validation result:

  • Release build passed with 148 existing unrelated warnings and 0 errors.
  • The generated-code SkiaSharp obsolete-warning grep returned 0 matches for the migrated API area.
  • Full test suite passed: 2175 passed, 533 skipped, 0 failed.

Remaining warnings are existing non-scope warnings such as NuGet advisory warnings, nullable warnings in other projects, and unrelated SvgDeferredPaintServer obsolete warnings.

@wieslawsoltes wieslawsoltes marked this pull request as ready for review May 12, 2026 14:12
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 496678a558

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +209 to +211
public void DrawImage(SKImage image, SKRect source, SKRect dest, SKSamplingOptions sampling, SKPaint? paint = null)
{
AddCommand(new DrawImageCanvasCommand(image, source, dest, paint, sampling));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor sampling when replaying image commands

When a caller records an image with the new DrawImage(..., SKSamplingOptions, ...) overload, the retained model stores Sampling, but SkiaModel.Draw still renders every DrawImageCanvasCommand via the old skCanvas.DrawImage(image, source, dest, paint) path and never reads drawImageCanvasCommand.Sampling (checked src/Svg.Skia/SkiaModel.cs lines 1845-1849). As a result, models that request cubic/linear sampling display with whatever the paint/default legacy path chooses, while codegen emits the requested sampling, so runtime rendering and generated output diverge.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 568a1081a9bbc4720aecaa0b3507ef49330aa46f.

SkiaModel.Draw now reads DrawImageCanvasCommand.Sampling during retained replay. Because Svg.Skia currently compiles against a SkiaSharp runtime surface where SKCanvas.DrawImage(..., SKSamplingOptions, ...) is not available, the replay path translates the retained shim SKSamplingOptions to the closest runtime SKFilterQuality and applies it to a scoped cloned SKPaint. This keeps cached paints immutable and makes retained runtime rendering honor explicit recorded sampling instead of falling back to the legacy paint/default choice.

I also added SKSvgRebuildFromModelTests.ToSKPicture_UsesRecordedImageSampling, which renders a retained two-color image with explicit linear sampling and verifies blended pixels are produced.

Validation run after the fix:

  • dotnet format Svg.Skia.slnx --no-restore
  • dotnet build Svg.Skia.slnx -c Release --no-restore -v:minimal /nologo
  • dotnet test Svg.Skia.slnx -c Release --no-restore -v:minimal /nologo

The full build passed with existing unrelated warnings only, and the full test run passed. Svg.Skia.UnitTests now reports 1848 passed and 533 skipped.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional refinement landed in 7b535163dcaa3bdb0a97d85bd62937798f1ba36a.

The initial fix in 568a1081a9bbc4720aecaa0b3507ef49330aa46f made retained replay consume DrawImageCanvasCommand.Sampling. This follow-up removes the remaining lossy behavior for newer SkiaSharp runtimes: when SKSamplingOptions and the matching SKCanvas.DrawImage overload are present at runtime, replay now constructs the native sampling options and invokes that exact overload. The SKFilterQuality mapping remains only as the SkiaSharp 2.88 fallback path.

This was validated with the focused retained replay tests plus the full release build and full release test suite after the follow-up commit.

Comment on lines +234 to +236
public void DrawText(string text, float x, float y, SKTextAlign textAlign, SKFont font, SKPaint paint)
{
AddCommand(new DrawTextCanvasCommand(text, x, y, paint, textAlign, font));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor recorded SKFont when replaying text

The new overload records TextAlign and SKFont, but the renderer does not consume those fields: SkiaModel.Draw still calls TryDrawShapedText(..., paint) / skCanvas.DrawText(text, x, y, paint) for DrawTextCanvasCommand (checked src/Svg.Skia/SkiaModel.cs lines 1922-1924), and the same applies to the old path-based draw call. Any retained model created through this overload with a font size/typeface/scale or alignment that differs from the paint will render incorrectly even though codegen preserves the font.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 568a1081a9bbc4720aecaa0b3507ef49330aa46f.

Retained text replay now consumes the recorded font and alignment state instead of relying only on the paint. The fix adds runtime SKFont conversion for shim SKPaint and shim SKFont, including typeface resolution, size, scale, skew, edging, subpixel, and embolden state. TryDrawShapedText now shapes with the selected SKFont and uses the selected SKTextAlign for shaped-text alignment.

For fallback/runtime APIs that still draw text through SKPaint, SkiaModel.Draw creates a scoped cloned paint when the retained command provides an explicit font or alignment, applies those fields to that clone, and leaves cached render paints untouched. The same pattern is applied to DrawTextOnPathCanvasCommand. Positioned text replay now also keys its blob cache by SKFont signature and uses SKTextBlob.Font when the retained blob carries an explicit font.

I added SKSvgRebuildFromModelTests.ToSKPicture_UsesRecordedTextFontAndAlignment, which renders a retained text command whose recorded font/alignment intentionally differs from the paint and verifies the rendered output lands on the expected side with the larger font.

Validation run after the fix:

  • dotnet format Svg.Skia.slnx --no-restore
  • dotnet build Svg.Skia.slnx -c Release --no-restore -v:minimal /nologo
  • dotnet test Svg.Skia.slnx -c Release --no-restore -v:minimal /nologo

The full build passed with existing unrelated warnings only, and the full test run passed. Svg.Skia.UnitTests now reports 1848 passed and 533 skipped.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 568a1081a9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/Svg.Skia/SkiaModel.cs
Comment on lines +1330 to +1332
if (samplingOptions.UseCubic)
{
return SkiaSharp.SKFilterQuality.High;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve exact image sampling during replay

Fresh evidence in this revision is that the replay path now reads Sampling, but this helper collapses every cubic/custom SKSamplingOptions to legacy SKFilterQuality.High (and similarly loses mipmap details for some filter modes). When a retained model records DrawImage with SKCubicResampler.CatmullRom or another non-default sampling option, SkiaModel.Draw still replays a different sampler while generated code emits the exact SKSamplingOptions, so runtime playback can diverge from generated output; use the SKCanvas.DrawImage overload that accepts SKSamplingOptions or convert the options losslessly.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 7b535163dcaa3bdb0a97d85bd62937798f1ba36a.

Svg.Skia still compiles against SkiaSharp 2.88.9, so the exact SKSamplingOptions runtime types are not available at compile time. I added a runtime reflection bridge that detects SkiaSharp.SKSamplingOptions, SKFilterMode, SKMipmapMode, SKCubicResampler, and the SKCanvas.DrawImage(SKImage, SKRect, SKRect, SKSamplingOptions, SKPaint) overload when a newer SkiaSharp runtime provides them.

When that API is present, retained replay now constructs native sampling options from the shim values, including custom cubic B and C, and invokes the exact native DrawImage overload. The previous SKFilterQuality conversion is now ToLegacySKFilterQuality and is only used as the SkiaSharp 2.88 fallback path. I also added a retained image null guard and conditioned the linker annotations so net461 and netstandard2.0 keep compiling while modern targets remain trim-analysis clean.

Validation:

  • dotnet build src/Svg.Skia/Svg.Skia.csproj -c Release --no-restore -v:minimal /nologo -> passed, 0 warnings, 0 errors
  • dotnet build tests/Svg.Skia.UnitTests/Svg.Skia.UnitTests.csproj -c Release --no-restore -v:minimal /nologo -> passed, 0 warnings, 0 errors
  • dotnet test tests/Svg.Skia.UnitTests/Svg.Skia.UnitTests.csproj -c Release --no-restore --filter "FullyQualifiedName~SKSvgRebuildFromModelTests.ToSKPicture_UsesRecorded" -v:minimal /nologo -> passed, 2 passed, 0 failed
  • dotnet format Svg.Skia.slnx --no-restore -> completed; unrelated externals/SVG formatter churn was restored
  • dotnet build Svg.Skia.slnx -c Release --no-restore -v:minimal /nologo -> passed, 13 existing package advisory warnings, 0 errors
  • dotnet test Svg.Skia.slnx -c Release --no-restore -v:minimal /nologo -> passed; Svg.Skia.UnitTests reported 1848 passed and 533 skipped

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7b535163dc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/Svg.Skia/SkiaModel.cs Outdated
var typefaceResolution = ResolveSKTypeface(paint.Typeface);
var skFont = new SkiaSharp.SKFont(typefaceResolution.Typeface, paint.TextSize)
{
Edging = paint.LcdRenderText ? SkiaSharp.SKFontEdging.SubpixelAntialias : SkiaSharp.SKFontEdging.Antialias,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve aliased text when creating fonts from paint

When a text command is replayed or generated from an SKPaint with IsAntialias=false (for example SVG rendering hints that request crisp text), this conversion still creates an SKFont with Edging=Antialias unless LCD rendering is enabled. The new SkiaSharp text APIs draw with the SKFont's edging, so paint-only text that used to render aliased through SKPaint.IsAntialias=false is now replayed/generated as antialiased; map non-antialiased paints to SKFontEdging.Alias here (and in the codegen helper) to keep output consistent.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in d52959951bac927cf3504ae7259ec58bef4036ce.

SkiaModel.ToSKFont(SKPaint) now maps paint-derived fonts with IsAntialias = false to SKFontEdging.Alias. LCD/subpixel edging is only selected when antialiasing remains enabled, so paint-only replay keeps the old aliased text behavior after moving to the newer SKFont text APIs.

I also updated the C# codegen helper for paint-derived fonts to emit SKFontEdging.Alias when the retained SKPaint has antialiasing disabled. That keeps generated output aligned with runtime replay for crisp/aliased text hints.

Regression coverage added:

  • SKSvgRebuildFromModelTests.ToSKFont_MapsAliasedPaintToAliasEdging
  • SkiaCSharpCodeGenTests.Generate_UsesAliasFontEdgingForAliasedTextPaint

Validation:

  • dotnet test tests/Svg.Skia.UnitTests/Svg.Skia.UnitTests.csproj -c Release --no-restore --filter "FullyQualifiedName~ToSKFont_MapsAliasedPaintToAliasEdging|FullyQualifiedName~Generate_UsesAliasFontEdgingForAliasedTextPaint" -v:minimal /nologo -> passed, 2 passed, 0 failed
  • dotnet format Svg.Skia.slnx --no-restore -> completed; unrelated externals/SVG formatter churn was restored
  • dotnet build Svg.Skia.slnx -c Release --no-restore -v:minimal /nologo -> passed, 135 existing unrelated warnings, 0 errors
  • dotnet test Svg.Skia.slnx -c Release --no-restore -v:minimal /nologo -> passed; Svg.Skia.UnitTests reported 1850 passed and 533 skipped

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant