Releases: guitarrapc/SkiaSharp.QrCode
0.12.0
Overview
fix: remove gray borders around QR code modules by disabling antialiasing.
Fixed: Gray Borders Around QR Code Modules
Previous versions displayed unwanted gray borders around rectangular QR code modules when antialiasing was enabled. This occurred because antialiasing creates semi-transparent pixels at edges, which blend with the background to create visible gray lines between modules.
Solution:
- Introduced shape-specific antialiasing control via
ModuleShape.RequiresAntialiasingproperty - Rectangular modules now render without antialiasing for crisp, border-free edges
- Circular and rounded rectangle modules maintain antialiasing for smooth curves
- Text rendering in icons preserves antialiasing for optimal readability
Before:
Rectangular modules had visible gray borders between them.
After:
Sharp, clean module edges with no visual artifacts.
What's Changed
- [dotnet format] Automated changes by @github-actions[bot] in #280
- fix: remove gray borders around QR code modules by disabling antialiasing by @guitarrapc in #282
Full Changelog: 0.11.0...0.12.0
0.11.0
Overview
This release introduces customizable icon rendering with new IconShape support, enabling you to add both images and text to your QR codes. You can now create QR codes with branded logos accompanied by text labels for enhanced visual appeal.
Also QRCodeImageBuilder now accept QRCodeData directly, enabling you to directly render compress/decompress scenario.
Breaking change
Customizable Icon Rendering
The IconData class now supports flexible icon rendering through the new IconShape abstraction:
ImageIconShape- Display images only (replaces directSKBitmapusage)ImageTextIconShape- Combine images with text labels- Future extensibility for custom icon shapes
Breaking Change: IconData.Icon Property
Before (0.10.0 and earlier):
using var bitmap = SKBitmap.Decode(File.ReadAllBytes(iconPath));
var icon = new IconData
{
Icon = bitmap, // Direct SKBitmap
IconSizePercent = 15,
IconBorderWidth = 10
};After (0.11.0):
using var bitmap = SKBitmap.Decode(File.ReadAllBytes(iconPath));
// Option 1: Quick creation with helper method
var icon = IconData.FromImage(bitmap, iconSizePercent: 15, iconBorderWidth: 18);
// Option 2: Image-only icon
var icon = new IconData
{
Icon = new ImageIconShape(bitmap),
IconSizePercent = 15,
IconBorderWidth = 10
};
// Option 3: Image with text label
using var font = new SKFont
{
Size = 18,
Typeface = SKTypeface.FromFamilyName("sans-serif", SKFontStyle.Bold)
};
var icon = new IconData
{
Icon = new ImageTextIconShape(bitmap, "FooBar", SKColors.Black, font, textPadding: 2),
IconSizePercent = 13,
IconBorderWidth = 18
};Enhanced QRCodeData Support
Added QRCodeImageBuilder constructor overload accepting QRCodeData directly, enabling advanced scenarios:
// compression to zstandard ...
var qrCodeData = QRCodeGenerator.CreateQrCode("Hello", ECCLevel.L);
var src = qrCodeData.GetRawData();
var size = qrCodeData.GetRawDataSize();
var maxSize = NativeCompressions.Zstandard.GetMaxCompressedLength(size);
var compressed = new byte[maxSize];
NativeCompressions.Zstandard.Compress(src, compressed, NativeCompressions.ZstandardCompressionOptions.Default);
// decompression from zstandard ...
var decompressed = NativeCompressions.Zstandard.Decompress(compressed);
// render QR code
var qr = new QRCodeData(decompressed, 4);
var pngBytes = QRCodeImageBuilder.GetPngBytes(qr, 512);
File.WriteAllBytes(path, pngBytes);Migration Guide
If you're using IconData in your code, update the Icon property from SKBitmap to IconShape:
- Quick fix: Use
IconData.FromImage()helper method - Image only: Wrap with
ImageIconShape - Image + Text: Use
ImageTextIconShapewith font configuration
⚠️ Note: Always useECCLevel.Hwhen adding icons to ensure QR code readability.
What's Changed
- feat: Add QRCodeData overload for QRCodeImageBuilder by @guitarrapc in #278
- [Breaking change] feat: Add Iconshape to handle QR Icon. by @guitarrapc in #279
Full Changelog: 0.10.0...0.11.0
0.10.0
Overview
This release adds .NET 10.0 support and introduces customizable Finder Pattern rendering, allowing you to create QR codes with distinctive corner styles while maintaining full scan compatibility.
Custom Finder Pattern Rendering
New WithFinderPatternShape() API enables distinctive corner designs:
RectangleFinderPatternShape- Classic rectangular patternRoundedRectangleFinderPatternShape- Smooth rounded cornersCircleFinderPatternShape- Circular cornersRoundedRectangleCircleFinderPatternShape- Hybrid style with rounded rectangles and circles
Here's sample QR Code with customize finder pattern by RoundedRectangleCircleFinderPatternShape.
Code Example
Create a QR code with rounded finder patterns:
var qrCode = new QRCodeImageBuilder("https://example.com")
.WithSize(512, 512)
.WithFinderPatternShape(RoundedRectangleFinderPatternShape.Default)
.WithColors(codeColor: SKColors.DarkBlue);What's Changed
- [dotnet format] Automated changes by @github-actions[bot] in #272
- chore(deps): bump actions/upload-artifact from 4.6.2 to 5.0.0 by @dependabot[bot] in #274
- chore(deps): bump actions/download-artifact from 5.0.0 to 6.0.0 by @dependabot[bot] in #273
- feat: add .NET 10 support by @guitarrapc in #275
- feat: Add cusomize FinderPattern rendering with your defined shape. by @guitarrapc in #276
- feat: show gradient and medium size of styled QR on BlazorWasm QR page by @guitarrapc in #277
Full Changelog: 0.9.0...0.10.0
0.9.0
🎉 Overview
v0.9.0 is a major update that includes significant performance improvements, API redesign, and the introduction of a more user-friendly Builder pattern.
⚡ Performance Improvements
This release dramatically improves QR code generation speed and memory efficiency:
Performance for several data types.
Memory Allocation Reduction
- Redesigned QRCodeData implementation from
List<BitArray>to 1Dbyte[]array (#233, #186) - Used
stackallocfor small datasets to reduce allocations (#236, #209, #212) - Leveraged ArrayPool for large datasets (#228, #237)
- Reused temporary QRCode objects during mask pattern selection (#207, #234)
- Removed allocations during FinderPattern placement (#243)
- Reduced re-allocations when adding masks by including QuietZone in constructor (#252)
Algorithm Optimization
- Replaced LINQ linear search O(n) with lookup table O(1) (#173)
- Removed LINQ from hot paths (#184, #198, #202)
- Eliminated Reflection usage (MaskCode) (#191)
- Executed penalty calculations in a single pass (#231, #241)
- Optimized Penalty3 calculation with sliding window 11-bit masking (#232)
- Optimized QR code module placement with bitmask (#244)
- Pre-calculated mod/div for mask pattern selection (#248)
- Wrote multiple bits at once (#225)
- Single-pass text analysis with TextAnalyzer (#229)
Data Structure Improvements
- Changed QR Code data tables from instance to Lazy static, initializing once (#171)
- Changed Point & Rectangle from class to readonly record struct (#166)
- Made Vector2Slim immutable (#196)
- Changed CodewordBlock to readonly struct (#199)
- Replaced string format/version operations with uint/ushort (#208)
Other Optimizations
- Changed char array access to range check for Numeric/AlphaNumeric (#168, #169)
- Used StringBuilder with capacity (#172)
- Specified List capacity (#185)
- Faster ISO-8859-1 validation with range check (#180)
- Removed unnecessary modulo in Galois multiplication (#247)
- Removed temporary collections with collection expressions (#211)
💥 Breaking Changes
API Changes
1. QrCode Class Deprecation and Migration to QRCodeImageBuilder
The QrCode class is now obsolete. Please use the new QRCodeImageBuilder instead. See detail #266
Before (Old API):
var qrCode = new QrCode(content, new Vector2Slim(512, 512), SKEncodedImageFormat.Png);
using (var output = new FileStream(path, FileMode.OpenOrCreate))
{
qrCode.GenerateImage(output);
}After (New API):
Simple QR code generation with static method:
var pngBytes = QRCodeImageBuilder.GetPngBytes(content);
File.WriteAllBytes(path, pngBytes);Advanced usage with builder pattern:
using var stream = File.OpenWrite(path);
var pngBytes = new QRCodeImageBuilder(content)
.WithSize(512, 512)
.WithErrorCorrection(ECCLevel.H)
.SaveTo(stream);2. IDisposable Removal
- Removed IDisposable from QRCodeData (#214)
- Removed IDisposable from QRCodeRenderer and changed to static class (#216, #220)
Remove using statements for these classes if you were using them.
// 0.8.0 and before
using var qrCodeData = QRCodeGenerator.CreateQrCode("Hello, World!", ECCLevel.L);
// 0.9.0 and after
var qrCodeData = QRCodeGenerator.CreateQrCode("Hello, World!", ECCLevel.L);// 0.8.0 and before
using var renderer = new QRCodeRenderer();
renderer.Render(...);
// 0.9.0 and after
QRCodeRenderer.Render(...);3. Stricter Null Checking
QRCodeRenderer.Render now throws ArgumentNullException when null data is passed (#218)
4. IconData Namespace Move
IconData moved to SkiaSharp.QrCode.Image namespace (#239)
Update your using directives:
using SkiaSharp.QrCode.Image;5. QuietZone Size Specification
QRCodeData(byte[] rawData) now accepts QuietZoneSize specification (#253)
Removed Features
- Removed
forceUtf8parameter (#177) - Removed ISO-8859-2 encoding support (#178)
- Removed compression feature (#256)
- Removed Kanji encoding mode (#269)
Encoding Changes
- Fixed empty data encoding from Numeric to Byte (#240)
✨ New Features
QRCodeImageBuilder - New Builder Pattern
Introduced a new API that provides static QR code image generation with a fluent builder pattern:
using var image = QRCodeImageBuilder.Create()
.WithContent("Hello, World!")
.WithECCLevel(ECCLevel.H)
.WithSize(pixelsPerModule: 10)
.WithColors(SKColors.Black, SKColors.White)
.WithIcon(iconData, iconSizePercent: 15)
.Build();Related PR: #264
Enhanced Rendering Features
- Gradient color support (#263)
- Enhanced QR code rendering customization options (#261)
- Consolidated rendering methods and batch rendering (#259, #260)
Binary Data Support
- Added binary data encoding for QR codes (#204)
- Changed Binary CreateQrCode to receive
ReadOnlySpan<char>(#205)
New APIs
🐛 Bug Fixes
- Fixed invalid QR code generation when upgraded version (EciMode.Default UTF8 fallback) (#179)
- Fixed ECC encoding to ensure the remainder always produces the expected number of ECC words (#183)
- Fixed Penalty3 calculation to execute for rows and columns separately (#242)
- Fixed GetVersion not considering UTF8 BOM header bytes (#250)
🔧 Refactoring & Internal Improvements
Encoding Redesign
0.8.0
This release follow to Upstream SkiaSharp package 3.119.1
What's Changed
- chore: bump docker sample to use latest package by @guitarrapc in #75
- chore(deps): bump Microsoft.Fast.Components.FluentUI from 3.4.1 to 3.5.0 by @dependabot[bot] in #77
- chore(deps): bump xunit from 2.6.3 to 2.6.4 by @dependabot[bot] in #80
- chore(deps): bump xunit.runner.visualstudio from 2.5.5 to 2.5.6 by @dependabot[bot] in #81
- chore(deps): bump Microsoft.VisualStudio.Azure.Containers.Tools.Targets from 1.19.5 to 1.19.6 by @dependabot[bot] in #82
- chore(deps): bump xunit from 2.6.4 to 2.6.5 by @dependabot[bot] in #83
- chore(deps): bump xunit from 2.6.5 to 2.6.6 by @dependabot[bot] in #84
- chore(deps): bump Microsoft.AspNetCore.Components.WebAssembly from 8.0.0 to 8.0.1 by @dependabot[bot] in #85
- chore(deps): bump SkiaSharp.NativeAssets.NanoServer and SkiaSharp by @dependabot[bot] in #86
- chore(deps): bump SkiaSharp from 2.88.6 to 2.88.7 by @dependabot[bot] in #87
- chore(deps): bump Microsoft.AspNetCore.Components.WebAssembly.DevServer from 8.0.0 to 8.0.1 by @dependabot[bot] in #88
- chore(deps): bump Microsoft.Fast.Components.FluentUI from 3.5.0 to 3.5.2 by @dependabot[bot] in #90
- chore(deps): bump peter-evans/create-pull-request from 5 to 6 by @dependabot[bot] in #94
- chore(deps): bump Microsoft.AspNetCore.Components.WebAssembly from 8.0.1 to 8.0.2 by @dependabot[bot] in #98
- ci: bump download-artifact/upload-artifact to v4 by @guitarrapc in #115
- chore(deps): bump coverlet.collector from 6.0.0 to 6.0.2 by @dependabot[bot] in #103
- chore(deps): bump SkiaSharp.Views.Blazor and SkiaSharp by @dependabot[bot] in #106
- chore(deps): bump SkiaSharp.NativeAssets.Linux.NoDependencies and SkiaSharp by @dependabot[bot] in #107
- ci: docker-compose to docker compose by @guitarrapc in #117
- chore(deps): bump Microsoft.Fast.Components.FluentUI from 3.5.2 to 3.7.8 by @dependabot[bot] in #114
- chore(deps): bump Microsoft.NET.Test.Sdk from 17.8.0 to 17.11.0 by @dependabot[bot] in #116
- chore(deps): bump xunit.runner.visualstudio from 2.5.6 to 2.8.2 by @dependabot[bot] in #124
- chore(deps): bump Microsoft.AspNetCore.Components.WebAssembly.DevServer from 8.0.1 to 8.0.8 by @dependabot[bot] in #123
- chore(deps): bump peter-evans/create-pull-request from 6 to 7 by @dependabot[bot] in #119
- refactor: use full qualified name by @guitarrapc in #125
- chore(deps): bump xunit from 2.6.6 to 2.9.0 by @dependabot[bot] in #121
- chore(deps): bump coverlet.msbuild from 6.0.0 to 6.0.2 by @dependabot[bot] in #120
- chore(deps): bump SkiaSharp.NativeAssets.NanoServer and SkiaSharp by @dependabot[bot] in #122
- chore(deps): bump Microsoft.AspNetCore.Components.WebAssembly from 8.0.2 to 8.0.10 by @dependabot[bot] in #131
- chore(deps): bump Microsoft.AspNetCore.Components.WebAssembly.DevServer from 8.0.8 to 8.0.10 by @dependabot[bot] in #133
- chore(deps): bump Microsoft.NET.Test.Sdk from 17.11.0 to 17.12.0 by @dependabot[bot] in #135
- chore(deps): bump Microsoft.Fast.Components.FluentUI from 3.7.8 to 3.8.0 by @dependabot[bot] in #136
- chore(deps): bump Microsoft.VisualStudio.Azure.Containers.Tools.Targets from 1.19.6 to 1.21.0 by @dependabot[bot] in #127
- chore: drop net 6.7, 7.0 by @guitarrapc in #143
- ci: pin action sha by @guitarrapc in #142
- ci: dependabot by @guitarrapc in #144
- chore(deps): bump GitHubActionsTestLogger from 2.3.3 to 2.4.1 by @dependabot[bot] in #129
- chore(deps): bump xunit.runner.visualstudio from 2.8.2 to 3.0.2 by @dependabot[bot] in #145
- chore(deps): bump Microsoft.NET.Test.Sdk from 17.12.0 to 17.13.0 by @dependabot[bot] in #146
- chore(deps): bump Microsoft.AspNetCore.Components.WebAssembly.DevServer from 8.0.14 to 9.0.3 by @dependabot[bot] in #147
- chore(deps): bump actions/download-artifact from 4.2.1 to 4.3.0 by @dependabot[bot] in #148
- chore(deps): bump SkiaSharp.Views.Blazor from 3.116.1 to 3.119.0 by @dependabot[bot] in #153
- chore(deps): bump xunit.runner.visualstudio from 3.0.2 to 3.1.0 by @dependabot[bot] in #151
- chore(deps): bump aquaproj/aqua-installer from 3.1.1 to 4.0.0 by @dependabot[bot] in #154
- chore(deps): bump actions/download-artifact from 4.3.0 to 5.0.0 by @dependabot[bot] in #156
- chore(deps): bump actions/checkout from 4.2.2 to 5.0.0 by @dependabot[bot] in #155
- chore: migrate sln to slnx by @guitarrapc in #157
- ci: use NuGet Trusted Publish by @guitarrapc in #158
- chore: nuget and sign key handling by @guitarrapc in #159
- chore: use filescope by @guitarrapc in #160
- chore: bump SkiaSharp packages to 3.119.1 by @guitarrapc in #161
Full Changelog: 0.7.0...0.8.0
0.7.0
Note
This release include security fix for 'SkiaSharp' 2.80.2, which has a known high severity vulnerability, GHSA-j7hp-h8jx-5ppr
What's Changed
- chore: update samples to latest .NET by @guitarrapc in #61
- doc: update README, build name update by @guitarrapc in #62
- feat: bump dotnet runtime support for net8.0 by @guitarrapc in #67
- feat: bump NuGet packages to latest by @guitarrapc in #68
- ci: add dependabot.yaml by @guitarrapc in #69
- chore(deps): bump actions/checkout from 3 to 4 by @dependabot in #72
- chore(deps): bump actions/download-artifact from 2 to 3 by @dependabot in #73
- chore(deps): bump peter-evans/create-pull-request from 4 to 5 by @dependabot in #70
- chore(deps): bump actions/upload-artifact from 2 to 3 by @dependabot in #71
- ci: fix release should dotnet pack single project by @guitarrapc in #74
New Contributors
- @dependabot made their first contribution in #72
Full Changelog: 0.6.0...0.7.0
0.6.0
Strong Name Assembly is supported.
ref: https://docs.microsoft.com/en-us/dotnet/standard/assembly/strong-named
What's Changed
- feat: add dotnet format and .editorconfig by @guitarrapc in #42
- chore: remove warning for color and alpha test by @guitarrapc in #43
- feat: add Blazor WASM sample by @guitarrapc in #44
- chore: build samples by @guitarrapc in #45
- feat: add Background colour overload by @guitarrapc in #46
- chore: Add Color Selection on BlazorWasm Sample by @guitarrapc in #47
- chore: Consolidate multiple target frameworks test to single csproj by @guitarrapc in #48
- feat: add strong name sign key and props by @guitarrapc in #49
- chore: remove LICENSE.md from nuget package and show MIT instead. by @guitarrapc in #50
- feat: add IDE0049 by @guitarrapc in #52
- [dotnet format] Automated changes by @github-actions in #51
New Contributors
- @github-actions made their first contribution in #51
Full Changelog: 0.5.0...0.6.0
0.5.0
What's Changed
- Update README.md by @yangzhongke in #30
- fix #36 by @feiyun0112 in #37
- Update QRCodeRenderer.cs by @roger-castaldo in #39
- feat: support netstandard2.1 by @guitarrapc in #40
- feat: add net6.0 test and samples. drop netcoreapp2.2 by @guitarrapc in #41
New Contributors
- @yangzhongke made their first contribution in #30
- @feiyun0112 made their first contribution in #37
- @roger-castaldo made their first contribution in #39
Full Changelog: 0.4.1...0.5.0
0.4.1
This is Linux package dependency fix.
Feature
n/a
Fix
- fix: libskiasharp dependency. SkiaSharp.QrCode 0.4.0 had explict dependency with
SkiaSharp.NativeAssets.Linux2.80.2, now it's removed.- side effect: Must explict add
SkiaSharp.NativeAssets.Linux.NoDependenciesorSkiaSharp.NativeAssets.Linuxwhen running on Linux. refer README for more detail.
- side effect: Must explict add