Improve SkiaSharp imaging provider and bump Magick.NET#25427
Merged
Conversation
- Add SkiaSharpImageCompressorContributor and SkiaSharpCompressOptions so Volo.Abp.Imaging.SkiaSharp ships a compressor alongside its existing resizer. - Bump Magick.NET-Q16-AnyCPU 14.9.1 -> 14.13.0 to clear the NU1901/NU1902/NU1903 advisories.
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a SkiaSharp-based image compressor contributor (paired options class) so the SkiaSharp imaging package now ships both a resizer and a compressor, and bumps Magick.NET to clear NuGet vulnerability advisories.
Changes:
- New
SkiaSharpImageCompressorContributor+SkiaSharpCompressOptions(auto-registered viaITransientDependency) supporting JPEG/PNG/WebP. - xUnit tests covering JPEG/PNG/WebP compression and stream/byte[] equivalence with
Quality = 50. - Bump
Magick.NET-Q16-AnyCPU14.9.1 → 14.13.0 inDirectory.Packages.propsand document it inpackage-version-changes.md.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| framework/src/Volo.Abp.Imaging.SkiaSharp/Volo/Abp/Imaging/SkiaSharpImageCompressorContributor.cs | New compressor contributor mirroring the existing resizer/MagickNet pattern. |
| framework/src/Volo.Abp.Imaging.SkiaSharp/Volo/Abp/Imaging/SkiaSharpCompressOptions.cs | New options class (Quality, default 75). |
| framework/test/Volo.Abp.Imaging.SkiaSharp.Tests/Volo/Abp/Imaging/SkiaSharpImageCompressorTests.cs | Unit tests for jpg/png/webp and stream vs byte[] parity. |
| Directory.Packages.props | Magick.NET version bump for security advisories. |
| docs/en/package-version-changes.md | Documents the Magick.NET version bump. |
Update the image-manipulation reference to list SkiaSharp alongside ImageSharp and Magick.NET, and add a SkiaSharp Provider section with installation steps and the SkiaSharpResizerOptions / SkiaSharpCompressOptions configuration knobs.
Match the ImageSharp and Magick.NET resizer contributors which return the result stream positioned at the start, so downstream consumers read the bytes instead of getting an empty read at end-of-stream.
Existing tests only checked Result.Length which doesn't depend on Position, so a contributor that forgot to seek the result stream back to 0 would still pass. Add an explicit Position == 0 assertion plus a CopyToAsync round-trip that compares the copied length against the result stream length, so the same regression cannot slip in again.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## rel-10.4 #25427 +/- ##
============================================
+ Coverage 49.30% 49.34% +0.04%
============================================
Files 3668 3670 +2
Lines 123235 123461 +226
Branches 9412 9434 +22
============================================
+ Hits 60757 60922 +165
- Misses 60666 60724 +58
- Partials 1812 1815 +3 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
- SkiaSharp resizer now honors ImageResizeArgs.Mode (Stretch, Max, Min, Crop, Pad, BoxPad) instead of stretching to the target dimensions regardless of mode. None and Default normalize to Crop to match the ImageSharp contributor. - BoxPad now Max-fits the source into the target box first when the source exceeds it, instead of cropping with negative offsets. - SkiaSharp compressor caches the bitmap stream length before SKBitmap.Decode takes ownership of it, so the post-encode size check no longer accesses a disposed stream and tolerates non-seekable inputs reaching the contributor directly. - Add resizer tests for every mode (exact target size for fixed modes, bounded size for Max/Min, pixel-level transparency check for BoxPad with a source larger than the target), and a compressor test that feeds a non-seekable stream directly to the contributor.
Magick.NETMagick.NET
Previously the SkiaSharp resizer ignored ImageResizeArgs.Mode entirely and always stretched, so callers that never specified a mode were implicitly relying on stretch output. Map None and Default to Stretch to preserve that observable behavior while still honoring explicit Max/Min/Crop/Pad/BoxPad values.
EngincanV
approved these changes
May 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Brings the SkiaSharp imaging provider to parity with ImageSharp and Magick.NET so Account.Pro can default to it (volosoft/volo#22327).
The resizer used to ignore
ImageResizeArgs.Modeand always stretch to (W, H). It now implements every mode - Stretch, Max, Min, Crop, Pad, BoxPad - and normalizes None/Default to Crop to match the ImageSharp contributor. BoxPad max-fits the source into the box when it's larger than the target.New
SkiaSharpImageCompressorContributor+SkiaSharpCompressOptionson top of the existing resizer.A few bugs fixed along the way: resized stream position reset to 0 before return; the compressor caches the bitmap-stream length before
SKBitmap.Decodedisposes it (also makes it tolerate non-seekable inputs reaching the contributor directly); animated GIFs returnUnsupportedinstead of throwingNullReferenceException.Magick.NET-Q16-AnyCPU14.9.1 -> 14.13.0 clearsNU1901/NU1902/NU1903advisories.Docs updated in
image-manipulation.md.