Skip to content

Releases: guitarrapc/FeatherQR

1.2.0

Choose a tag to compare

@github-actions github-actions released this 04 Sep 00:14
Immutable release. Only release title and notes can be modified.
08ddc4f

Overview

This release adds rMQR (ISO/IEC 23941) as a third symbology, teaches every decoder to read Kanji mode segments, and introduces an options struct on every generator so new settings no longer need new parameter lists. Encoding and decoding are faster across all symbologies on both x64 (AVX2 / GFNI) and ARM64 (NEON), and the package now ships XML documentation.

Everything is source compatible with v1.1.1. Two APIs are marked [Obsolete] and will be removed in 2.0.0.

Highlights

rMQR support

rMQR is a rectangular symbol that fits banners, cable labels, and other narrow spaces where a square QR code wastes room. All 32 versions (R7x43 - R17x139) are supported for generation and decoding, including image decoding.

using SkiaSharp.QrCode;
using SkiaSharp.QrCode.Image;

// One-liner PNG
var pngBytes = RmQRCodeImageBuilder.GetPngBytes("https://example.com/r/12345", RmQREccLevel.M, size: 512);

// Fix the height and let the width be selected
var data = RmQRCodeGenerator.CreateRmQRCode("https://example.com/r/12345", RmQREccLevel.M, new RmQRCodeGeneratorOptions
{
    Height = RmQRHeight.H9,
    FitStrategy = RmQRFitStrategy.MinimizeArea,
});

// Decode back
if (RmQRCodeDecoder.TryDecode(data, out var text, out var info))
    Console.WriteLine($"{text} ({info.Version})");

Mixed-mode segmentation

By default the whole content is encoded in a single mode, so one lowercase letter pushes an otherwise numeric payload into Byte mode. Opt into segmentation and the content is split into the Numeric / Alphanumeric / Byte runs that cost the fewest bits, which often drops the symbol by a version or more. The result is never larger than the single-mode symbol, and identical when splitting would not help.

var standard = QRCodeGenerator.CreateQrCode(
    "https://example.com/item?id=123456789012345678901234567890",
    ECCLevel.M,
    new QRCodeGeneratorOptions { Segmentation = QRCodeSegmentation.Optimal }); // version 3 instead of 4

var rmqr = RmQRCodeGenerator.CreateRmQRCode(
    "https://example.com/p/1234567890123456",
    RmQREccLevel.M,
    new RmQRCodeGeneratorOptions { Segmentation = RmQRSegmentation.Optimal }); // R15x43 instead of R11x77

Micro QR has MicroQRSegmentation.Optimal as well, respecting each version's available modes.

Generator options

Every generator entry point gained an overload taking an options struct. The parameter list overloads keep their signatures, exceptions, and output; new options are added to the struct from here on. default means "all defaults".

var data = QRCodeGenerator.CreateQrCode("https://example.com", ECCLevel.M, new QRCodeGeneratorOptions
{
    Version = new QRCodeVersionRange(10, 20),   // inclusive range; an int pins a single version
    BoostEccLevel = true,                       // treat ECC as a floor, raise it within the same symbol size
    MaskPattern = 3,                            // pin a data mask instead of automatic selection
    QuietZoneSize = 0,
});
  • Version ranges (QRCodeVersionRange, MicroQRVersionRange) express a pinned version, a range, or a lower bound in one setting. A pinned version that cannot hold the content now throws a message naming the version, ECC level, and mode.
  • ECC boost picks the version for the level you request, then raises the level as far as that version's spare capacity allows without growing the symbol. Recommended when robustness matters more than an exact level, especially with icons. The builder spells it WithErrorCorrectionBoost().
  • Mask pattern pinning reproduces a symbol produced elsewhere byte for byte, and lets you exercise scanners against every pattern.

Kanji mode decoding

All three decoders now read ISO/IEC 18004 Kanji mode segments. Symbols that previously returned UnsupportedContent now decode to text. Encoding is unchanged: the generators still write Japanese text as UTF-8 in Byte mode.

if (QRCodeDecoder.TryDecode(bitmap, out var text, out var info))
    Console.WriteLine(text);
else if (info.Status == QRCodeDecodeStatus.UnmappedCharacter)
    // A Kanji segment used a CP932-only character (NEC row 13); route it to a CP932-capable reader.
    Console.WriteLine("CP932-only content");

The mapping is JIS X 0208, not CP932. The new UnmappedCharacter status is deliberately distinct from UnsupportedContent, so those symbols can be routed elsewhere instead of being silently rewritten.

Module rectangles for vector output

GetModuleRectangles returns the dark modules as merged, disjoint rectangles in module coordinates, typically halving the element count compared to one rectangle per module. Useful for SVG paths and draw-call based graphics APIs.

foreach (var r in qrData.GetModuleRectangles())
    sb.Append(CultureInfo.InvariantCulture, $"M{r.X},{r.Y}h{r.Width}v{r.Height}h{-r.Width}z");

For allocation-free use, size a pooled buffer with GetModuleRectanglesMaxCount() and call TryGetModuleRectangles(Span<ModuleRect>, out int). Available on QRCodeData, MicroQRCodeData, and RmQRCodeData.

Buffer sizing is Try-only

"The content does not fit" is a data-dependent answer, not a defect, so sizing reports it with a bool rather than an exception. TryGetRequiredBufferSize is available on all three generators, and its options parameter is optional.

if (!MicroQRCodeGenerator.TryGetRequiredBufferSize(userInput, MicroQREccLevel.L, out var size))
    return "Content does not fit a Micro QR symbol.";

Invalid arguments still throw, so a configuration mistake is never reported as "content too long".

Migration

Nothing breaks on upgrade. See the Migration Guide for the deprecations and the Kanji behaviour change.

What's Changed

  • [dotnet format] Automated changes by @github-actions[bot] in #356
  • chore(deps): bump the dependencies group with 3 updates by @dependabot[bot] in #357
  • chore(deps): bump actions/stale from 10.4.0 to 11.0.0 in the dependencies group by @dependabot[bot] in #358
  • feat: add rMQR Implementation by @guitarrapc in #359
  • feat: Optimizes RmQR performance by @guitarrapc in #360
  • feat: Improve StandardQR performance by @guitarrapc in #361
  • feat: improve result-object packing and quiet-zone placement by @guitarrapc in #362
  • feat: Improve rMQR Auto fit to table scan by @guitarrapc in #363
  • [dotnet format] Automated changes by @github-actions[bot] in #364
  • feat: improve rMQR image-decode by @guitarrapc in #365
  • feat: rMQR Encode ECI support by @guitarrapc in #366
  • feat: Improve ARM NEON rMQR and related handling by @guitarrapc in #367
  • docs: update benchmarks to latest code base by @guitarrapc in #368
  • fix: rmqr decoder correction cap from Full RS strength to Per-block cap read from a 64-row capacity table, by @guitarrapc in #369
  • feat: Support rMQR segmentation by @guitarrapc in #371
  • feat: add Kanji decode support by @guitarrapc in #372
  • chore(deps): bump actions/attest-build-provenance from 4.1.0 to 4.2.2 in the dependencies group by @dependabot[bot] in #374
  • feat: add TryGetRequiredBufferSize api by @guitarrapc in #373
  • feat: Introduce QRCodeGeneratorOptions by @guitarrapc in #375
  • feat: make buffer sizing Try-only, obsolete parameter-list GetRequiredBufferSize by @guitarrapc in #376
  • feat: add EccBoost is same version but ecc can be upgraded. by @guitarrapc in #377
  • ci: aot analysis by @guitarrapc in #378
  • feat: cut off MaskPlacer when it is possible by @guitarrapc in #379
  • feat: allow specify MaskPattern to use. (StandardQR / MicroQR) by @guitarrapc in #380
  • chore: add negative test for intended 1 module broke, picture approved test by @guitarrapc in #381
  • feat: Add GetModuleRectanglesMaxCount to retrieve merged geometries by @guitarrapc in #382
  • feat: Mixed-mode segmentation to StandardQR and MicroQR by @guitarrapc in #384
  • chore: compression no referenced and should be obsolete by @guitarrapc in #385
  • chore: add xml document to package by @guitarrapc in #386
  • chore: add docs page in playground by @guitarrapc in #387
  • chore: trim xml comments by @guitarrapc in #388

Full Changelog: https://github.com/guitarrapc/SkiaSharp.QrCode/compare/1.1.1.....

Read more

1.1.1

Choose a tag to compare

@github-actions github-actions released this 05 Sep 07:23
Immutable release. Only release title and notes can be modified.

What's Changed

  • fix: FinderPattern filled by outer ring dark colour when using transparent bg by @guitarrapc in #355

Full Changelog: 1.1.0...1.1.1

1.1.0

Choose a tag to compare

@github-actions github-actions released this 18 Jul 09:04
Immutable release. Only release title and notes can be modified.

Overview

This release adds Micro QR (ISO/IEC 18004, M1–M4), generation, image rendering, and decoding, alongside the existing Standard QR support. The decoder pipeline also gets SIMD/NEON optimizations on AArch64, and Standard QR masking is faster with AVX2.

QRCodeImageBuilder and the new MicroQRCodeImageBuilder now share a common base class (QRCodeImageBuilderBase<TSelf>). Fluent call chains are source-compatible; recompile if you reference the library as a binary dependency.


Key change: Micro QR support

Micro QR is a compact symbology for small payloads (11×11 to 17×17 modules). This release provides:

API Purpose
MicroQRCodeGenerator Encode text to MicroQRCodeData
MicroQRCodeImageBuilder Render PNG / JPEG / WebP / SVG
MicroQRCodeDecoder Decode from matrix or image

QRCodeDecoder remains Standard QR only. Use MicroQRCodeDecoder for Micro QR images.

image

Generate (one-liner)

using SkiaSharp.QrCode;
using SkiaSharp.QrCode.Image;

// M2-L numeric example; auto-selects the smallest version that fits
var pngBytes = MicroQRCodeImageBuilder.GetPngBytes("01234567", MicroQREccLevel.L, size: 256);
File.WriteAllBytes("microqr.png", pngBytes);

Generate (builder — colors, shapes, gradient)

using SkiaSharp;
using SkiaSharp.QrCode;
using SkiaSharp.QrCode.Image;

var gradient = new GradientOptions(
    [SKColor.Parse("00B894"), SKColor.Parse("0984E3")],
    GradientDirection.TopLeftToBottomRight);

var pngBytes = new MicroQRCodeImageBuilder("SKU-42")
    .WithModulePixelSize(14)
    .WithErrorCorrection(MicroQREccLevel.M)
    .WithColors(codeColor: SKColor.Parse("2D3436"), backgroundColor: SKColors.White)
    .WithModuleShape(RoundedRectangleModuleShape.Default, sizePercent: 0.92f)
    .WithGradient(gradient)
    .ToByteArray();

Decode (matrix and image)

using SkiaSharp;
using SkiaSharp.QrCode;

// From matrix
var micro = MicroQRCodeGenerator.CreateMicroQRCode("01234567", MicroQREccLevel.L);
if (MicroQRCodeDecoder.TryDecode(micro, out var text, out var info))
{
    Console.WriteLine($"{text} ({info.Version}, ECC {info.EccLevel})");
}

// From image — use MicroQRCodeDecoder, not QRCodeDecoder
using var bitmap = SKBitmap.Decode("microqr.png");
if (MicroQRCodeDecoder.TryDecode(bitmap, out var scanned, out _))
{
    Console.WriteLine(scanned);
}

Micro QR constraints: M1 = numeric + error detection only; M2–M4 support numeric/alphanumeric/byte modes with version-specific ECC limits. No ECI mode; no icon overlay or custom finder styling (single finder pattern, limited ECC headroom).


Breaking changes

Binary: QRCodeImageBuilderBase<TSelf>

QRCodeImageBuilder and MicroQRCodeImageBuilder now derive from QRCodeImageBuilderBase<TSelf>. Shared methods (WithSize, WithModulePixelSize, WithFormat, WithQuietZone, WithColors, WithModuleShape, WithGradient, SaveTo, ToByteArray, etc.) moved to the base class.

  • Source compatible — no code changes needed.
  • Binary breaking — recompile assemblies compiled against 1.0.x.

WithQuietZone() default parameter removed

WithQuietZone(int size = 4) is now WithQuietZone(int size) with no default. Calling WithQuietZone() with no argument no longer compiles. Remove the call to use the builder default (4 modules for Standard QR, 2 for Micro QR).

// Before (1.0.x) — no longer compiles
new QRCodeImageBuilder("content").WithQuietZone().ToByteArray();

// After (1.1.0) — omit the call, or pass an explicit value
new QRCodeImageBuilder("content").ToByteArray();
new QRCodeImageBuilder("content").WithQuietZone(4).ToByteArray();

Other improvements

  • Performance: AVX2 masking for Standard QR; NEON optimizations for decoder stages (finder pattern, alignment, sampling, text analysis) on ARM64.
  • Bug fix: FinderPatternShape background color no longer renders as black on GPU surfaces (#338).

Migration

See docs/migration.md for details.

What's Changed

Full Changelog: 1.0.1...1.1.0

1.0.1

Choose a tag to compare

@github-actions github-actions released this 15 Jul 19:06
Immutable release. Only release title and notes can be modified.

Overview

Bug fix: Custom finder-pattern background colors no longer render as black on GPU-backed Skia surfaces (e.g. OpenGL, Metal, Vulkan) when using anti-aliased shapes such as circles or rounded rectangles #337).

What's Changed

  • chore: change xUnit to TUnit by @guitarrapc in #336
  • fix: FinderPatternShape background color renders as black on GPU surfaces by @guitarrapc with @Copilot in #338

New Contributors

Full Changelog: 1.0.0...1.0.1

1.0.0

Choose a tag to compare

@github-actions github-actions released this 12 Jul 04:12
Immutable release. Only release title and notes can be modified.
5a21adb

Announcing Release of 1.0.0

Highlights

  • Faster encoding, SIMD-accelerated Reed–Solomon (GFNI / SSSE3 / ARM AdvSimd / NEON), bit-parallel module placement, optimized masking, and tighter allocation patterns across the encode pipeline.
  • Zero-allocation encoding, CreateQrCode(..., Span<byte> destination) writes the module matrix into a caller-provided buffer with no per-encode heap allocation.
  • Decoding, QRCodeDecoder reads QR codes from module matrices and from images (rotation, mirroring, inverted palettes; Reed–Solomon error correction included).

Overview

  • Major encode-path performance work (ECC, interleaving, bit packing, data placement, masking).
  • SVG output API, GetSvgString(), GetSvgBytes(), SaveSvg(), SaveToSvg(), ToSvgString() on QRCodeImageBuilder (all styling options supported).
  • QrCode class removed, deprecated since 0.9.0; use QRCodeImageBuilder instead.
  • WithModulePixelSize() and WithVersion() for sharper module-aligned output and logo placement.
  • Browser Playground (WebAssembly): https://guitarrapc.github.io/SkiaSharp.QrCode/
  • Update SkiaSharp dependencies to 4.148.0

Usage

Quick start (PNG / SVG)

using SkiaSharp.QrCode.Image;
File.WriteAllBytes("qrcode.png", QRCodeImageBuilder.GetPngBytes("https://example.com"));
File.WriteAllText("qrcode.svg", QRCodeImageBuilder.GetSvgString("https://example.com"));

Zero allocation encode

var calculated = QRCodeGenerator.GetRequiredBufferSize("content", ECCLevel.M);
var buffer = ArrayPool<byte>.Shared.Rent(calculated.BufferSize);
try
{
    var written = QRCodeGenerator.CreateQrCode("content", ECCLevel.M, buffer);
    var matrix = buffer.AsSpan(0, written); // 0 = light, 1 = dark, row-major
}
finally
{
    ArrayPool<byte>.Shared.Return(buffer);
}

Decode

// From generated data (round-trip)
var qrData = QRCodeGenerator.CreateQrCode("content", ECCLevel.M);
if (QRCodeDecoder.TryDecode(qrData, out var text))
    Console.WriteLine(text);
// From an image
using var bitmap = SKBitmap.Decode("qr.png");
if (QRCodeDecoder.TryDecode(bitmap, out var text, out var info))
    Console.WriteLine($"{text} (v{info.Version}, ECC {info.EccLevel})");

Migration

The obsolete QrCode class has been removed in 1.0.0. Replace it with QRCodeImageBuilder.

See the Migration Guide for before/after examples (stream output, format/quality, overlays, and default ECC level change: QrCode used ECCLevel.L, QRCodeImageBuilder defaults to ECCLevel.M).

What's Changed

  • chore(deps): bump actions/checkout from 5.0.0 to 6.0.1 by @dependabot[bot] in #283
  • chore(deps): bump actions/download-artifact from 6.0.0 to 7.0.0 by @dependabot[bot] in #285
  • chore(deps): bump actions/upload-artifact from 5.0.0 to 6.0.0 by @dependabot[bot] in #284
  • chore: add Net.Codecrete.QrCodeGenerator benchmark by @guitarrapc in #286
  • chore: Adds SBOM generation by @guitarrapc in #287
  • ci: Adds SLSA provenance attestation by @guitarrapc in #288
  • chore(deps): bump actions/attest-build-provenance from 3.0.0 to 3.1.0 by @dependabot[bot] in #289
  • chore(deps): bump actions/attest-build-provenance from 3.1.0 to 3.2.0 by @dependabot[bot] in #290
  • chore(deps): bump actions/stale from 10.1.0 to 10.2.0 by @dependabot[bot] in #291
  • Update CodeQL action versions and config file by @guitarrapc in #293
  • chore(deps): bump actions/download-artifact from 7.0.0 to 8.0.0 in the dependencies group by @dependabot[bot] in #295
  • chore(deps): bump the dependencies group with 2 updates by @dependabot[bot] in #297
  • chore(deps): bump github/codeql-action from 4.32.5 to 4.35.1 in the dependencies group by @dependabot[bot] in #298
  • chore(deps): bump NuGet/login from 1.1.0 to 1.2.0 in the dependencies group by @dependabot[bot] in #300
  • chore(deps): bump the dependencies group across 1 directory with 3 updates by @dependabot[bot] in #307
  • chore(deps): bump actions/checkout from 6.0.1 to 7.0.0 in the dependencies group by @dependabot[bot] in #308
  • ci: add pr harness by @guitarrapc in #309
  • Update to SkiaSharp v4.148.0 by @chris-rickman in #306
  • Upgrade Codecrete generator to v3 by @manuelbl in #302
  • fix: FinderPattern background was fixed to White by @guitarrapc in #310
  • feat: add AddVersion to QRCodeImageBuilder by @guitarrapc in #311
  • feat: Add Pixel per module sizing support by @guitarrapc in #312
  • feat: Specify QR Size by WithSize when using WithModuleSize by @guitarrapc in #313
  • feat: Improve EccBinaryEncoder applying for scalar, SSSE3 and GFNI by @guitarrapc in #314
  • feat: Rewrite QR mask pattern selection from byte-per-module processing to a bit-packed pipeline by @guitarrapc in #315
  • chore: remove QrCode class as announced by @guitarrapc in #316
  • feat: improve memory allocation during creating QRCodeData by @guitarrapc in #317
  • feat: Optimized QR data placement with 64-bit buffering, paired module processing, and bounds-check elimination. by @guitarrapc in #318
  • feat: Faster interleaving via sequential writes, branchless column loops, and a single-block copy fast path. by @guitarrapc in #319
  • fix Incorrect handling for Count Indicator/buffer overflow against ISO/IEC 18004 by @guitarrapc in #321
  • feat: Rewrite BitWriter around an MSB-first ulong accumulator with bulk 32/64-bit stores, and update QRBinaryEncoder to use the faster write, flush, and pad paths. by @guitarrapc in #320
  • refactor: remove Galois dead code for constants by @guitarrapc in #322
  • feat: Bake GFNI initial table construction for cold start by @guitarrapc in #323
  • feat: add NEON (macOS, ARM64) improvement for EccBinaryEncoder by @guitarrapc in #324
  • feat: add 0alloc CreateQrCode API to use caller provided Span by @guitarrapc in #325
  • feat: add WASM Playground by @guitarrapc in #326
  • docs: move docs from README by @guitarrapc in #327
  • feat: Performance improvement for Skia image generation by @guitarrapc in #328
  • feat: add SVG API support by @guitarrapc in #329
  • feat: adjust svg api and playground support by @guitarrapc in #330
  • feat: add QRCode decode functionality, matrix & image pipeline with Reed-Solomon error correction by @guitarrapc in #331
  • feat: improve FinderPatternFinder performance by Simd by @guitarrapc in #332
  • feat: improve otsu histogram construction to pixel runs aggregation by @guitarrapc in #333
  • feat: refine Blazor by @guitarrapc in #334

New Contributors

Full Changelog: 0.12.0...1.0.0

0.12.0

Choose a tag to compare

@github-actions github-actions released this 28 Nov 14:27
39e6356

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.RequiresAntialiasing property
  • 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.

image

After:
Sharp, clean module edges with no visual artifacts.

image

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

Choose a tag to compare

@github-actions github-actions released this 16 Nov 12:32
b9d4d79

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 direct SKBitmap usage)
  • 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:

  1. Quick fix: Use IconData.FromImage() helper method
  2. Image only: Wrap with ImageIconShape
  3. Image + Text: Use ImageTextIconShape with font configuration

⚠️ Note: Always use ECCLevel.H when 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

Choose a tag to compare

@github-actions github-actions released this 14 Nov 19:37
7d073e9

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 pattern
  • RoundedRectangleFinderPatternShape - Smooth rounded corners
  • CircleFinderPatternShape - Circular corners
  • RoundedRectangleCircleFinderPatternShape - Hybrid style with rounded rectangles and circles

Here's sample QR Code with customize finder pattern by RoundedRectangleCircleFinderPatternShape.

pattern14_instagram_style

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

Choose a tag to compare

@github-actions github-actions released this 25 Oct 08:54
7a749a6

🎉 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:

image

Performance for several data types.

image

Memory Allocation Reduction

  • Redesigned QRCodeData implementation from List<BitArray> to 1D byte[] array (#233, #186)
  • Used stackalloc for 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);

Related PRs: #265, #264

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 forceUtf8 parameter (#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

  • Added pre-calculate QR size API (#251)
  • Provided GetRawData(IBufferWriter<byte> writer) API (#257)

🐛 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

  • Replaced QR code generation with QREncoder and ECCEncoder (#176)
  • Pipelined CreateQrCode (#197)
  • **QRCodeGenera...
Read more

0.8.0

Choose a tag to compare

@github-actions github-actions released this 30 Sep 19:15
a0c89ed

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