SkiaSharp.QrCode

November 16, 2025 · View on GitHub

Build release

License: MIT NuGet

SkiaSharp.QrCode

SkiaSharp.QrCode provides high-performance QR code generation with SkiaSharp integration.

Performance Benchmark

Benchmark results comparing SkiaSharp.QrCode with other libraries. See samples/Benchmark for details.

Many existing QR code libraries rely on System.Drawing, which has well-known GDI+ limitations and cross-platform issues. SkiaSharp.QrCode was created to provide high performance, minimum memory allocation, a simpler and more intuitive API while leveraging SkiaSharp's cross-platform capabilities. Generate a QR code in a single line, or customize every detail - the choice is yours.

You can create professional-looking QR codes like this with just a few lines of code:

Instagram-style Gradient QR Icon QR

See samples/ConsoleApp for code examples generating these styles.

Overview

SkiaSharp.QrCode is a modern, high-performance QR code generation library built on SkiaSharp. SkiaSharp.QrCode allocates memory only for the actual QR code data, with zero additional allocations during processing.

  • Simple API: One-liner QR code generation with sensible defaults
  • High Performance: Optimal speed and minimum memory allocation
  • Highly Customizable: Gradients, icons, custom shapes, colors, and more
  • Cross-Platform: Windows, Linux, macOS, iOS, Android, WebAssembly
  • Zero Dependencies: QR generation without external libraries (SkiaSharp for rendering only)
  • No System.Drawing: Avoids GDI+ issues and Windows dependencies
  • NativeAOT Ready: Full support for .NET Native AOT compilation
  • Modern .NET: .NET Standard 2.0, 2.1, .NET 8+

Installation

Visit SkiaSharp.QrCode on NuGet.org

dotnet add package SkiaSharp.QrCode

Quick Start

Simplest Example

Single line QR code generation:

using SkiaSharp.QrCode.Image;

// one-liner save to file
QRCodeImageBuilder.SavePng("Hello", "qrcode.png");

// Or get bytes
var pngBytes = QRCodeImageBuilder.GetPngBytes("https://example.com");

Common Use Cases

Generate QR Code for URL.

var pngBytes = QRCodeImageBuilder.GetPngBytes("https://example.com");
File.WriteAllBytes("qrcode.png", pngBytes);

WiFi QR Code.

var wifiString = "WIFI:T:WPA;S:MyNetwork;P:MyPassword;;";
QRCodeImageBuilder.SavePng(wifiString, "wifi-qr.png");

Generate with Custom Settings.

var qrCode = new QRCodeImageBuilder("https://example.com")
    .WithSize(512, 512)
    .WithErrorCorrection(ECCLevel.H)
    .ToByteArray();

Save Directly to Stream

using SkiaSharp.QrCode.Image;

using var stream = File.OpenWrite("qrcode.png");
QRCodeImageBuilder.SavePng("Your content here", stream, ECCLevel.M, size: 512);

Migration

  • v0.11.0 introduces further improvements to Icon handling. See the IconData section below.
  • v0.9.0 introduces significant performance improvements and API changes. Here's what you need to know to upgrade:

from 0.10.0 to 0.11.0 and higher

Take advantage of new capabilities:

  • Logo customization - Now you can customize center placed logos. Library offers icons with both images and text.

For complete migration details and examples, see Release 0.11.0.

⚠️ IconData.Data changed Icon from SKBitmap to IconShape

Before (0.10.0):

using var bitmap = SKBitmap.Decode(File.ReadAllBytes(iconPath));

// Old code
var icon = new IconData
{
    Icon = bitmap;
    IconSizePercent = 15,
    IconBorderWidth = 10
};

After (0.11.0):

using var bitmap = SKBitmap.Decode(File.ReadAllBytes(iconPath));

// New code Image only (Short hand)
var icon = IconData.FromImage(bitmap, iconSizePercent: 15, iconBorderWidth: 10);

// New code Image only
var icon = new IconData
{
    Icon = new ImageIconShape(bitmap),
    IconSizePercent = 15,
    IconBorderWidth = 10
};

// New approach with text
var icon = new IconData
{
    Icon = new ImageTextIconShape(bitmap, "Text", SKColors.Black, font),
    IconSizePercent = 15,
    IconBorderWidth = 10
};

from 0.8.0 to 0.9.0 and higher

Take advantage of new capabilities:

  • Gradient colors - Create eye-catching QR codes with color gradients
  • Enhanced customization - More control over module shapes and colors
  • Better performance - Dramatically faster generation with lower memory usage

For complete migration details and examples, see Release 0.9.0.

🔄 Primary API Change: QrCodeQRCodeImageBuilder

The QrCode class is now obsolete. Replace it with QRCodeImageBuilder:

Before (0.8.0):

var qrCode = new QrCode(content, new Vector2Slim(512, 512), SKEncodedImageFormat.Png);
using (var output = new FileStream(path, FileMode.OpenOrCreate))
{
    qrCode.GenerateImage(output);
}

After (0.9.0) - Simple approach:

var pngBytes = QRCodeImageBuilder.GetPngBytes(content);
File.WriteAllBytes(path, pngBytes);

After (0.9.0) - Builder pattern:

using var stream = File.OpenWrite(path);
new QRCodeImageBuilder(content)
    .WithSize(512, 512)
    .WithErrorCorrection(ECCLevel.H)
    .SaveTo(stream);

🗑️ Remove using Statements

QRCodeData and QRCodeRenderer are no longer IDisposable:

Before (0.8.0):

using var qrCodeData = QRCodeGenerator.CreateQrCode("Hello", ECCLevel.L);
using var renderer = new QRCodeRenderer();
renderer.Render(...);

After (0.9.0):

var qrCodeData = QRCodeGenerator.CreateQrCode("Hello", ECCLevel.L);
QRCodeRenderer.Render(...);  // Now a static method

📦 Update Namespace for IconData

If using icons in QR codes:

// Add this namespace
using SkiaSharp.QrCode.Image;

🚫 Removed Features

The following features have been removed:

  • forceUtf8 parameter
  • ISO-8859-2 encoding support
  • Compression feature
  • Kanji encoding mode

If you were using these features, you'll need to adjust your code accordingly.

  • forceUtf8: SkiaSharp.QrCode now automatically selects UTF-8 when needed.
  • ISO-8859-2 and Kanji: Currently not supported; UTF-8 is recommended for most use cases.
  • Compression: Removed to simplify the API and improve performance. Please handle compression externally if needed.

Here's an example of how to handle compression externally using NativeCompressions:

// 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);

API Overview

SkiaSharp.QrCode provides three main APIs for different use cases.

Recommendation: Start with QRCodeImageBuilder for most scenarios. Use QRCodeRenderer when you need canvas control. Use QRCodeGenerator only for custom rendering implementations.

FeatureQRCodeImageBuilderQRCodeRendererQRCodeGenerator
Ease of useHighMediumLow
FlexibilityMediumHighHighest
Built-in renderingYesYesNo
Custom canvas controlNoYesN/A
Recommended for beginnersYesNoNo

Best for Most use cases - simple to advanced QR code generation. The high-level, fluent API for generating QR codes with minimal code. Provides a builder pattern with sensible defaults and extensive customization options.

Key Features:

  • One-liner generation with GetPngBytes(), SavePng()
  • Fluent API with WithXxx() methods
  • Built-in support for colors, gradients, icons, shapes
  • Multiple output formats (PNG, JPEG, WebP)
  • Stream and byte array output

When to use:

  • Quick QR code generation
  • Standard customization needs
  • File or stream output

Example:

var pngBytes = QRCodeImageBuilder.GetPngBytes("content");

QRCodeRenderer (Advanced)

Best for Custom rendering with full control over the canvas. The mid-level API that renders QR data onto an existing SkiaSharp canvas. Useful when you need to integrate QR codes into existing graphics or add custom post-processing.

Key Features:

  • Render to existing SKCanvas
  • Full control over rendering area (SKRect)
  • Combine with other SkiaSharp drawing operations
  • Custom effects and post-processing

When to use:

  • Integrating QR codes into existing graphics
  • Adding custom decorations or effects
  • Multiple elements on the same canvas

Example:

var qrData = QRCodeGenerator.CreateQrCode("content", ECCLevel.M);
var canvas = surface.Canvas;
QRCodeRenderer.Render(canvas, area, qrData, SKColors.Black, SKColors.White);

QRCodeGenerator (Low-Level)

Best for QR data generation without rendering. The low-level API that generates raw QR code data (QRCodeData). Use this when you need the QR data structure itself, not the image.

Key Features:

  • Generates QRCodeData (boolean module matrix stored as 1D byte array)
  • Specify ECC level, ECI mode, quiet zone size
  • No rendering logic included
  • Smallest API surface

When to use:

  • Custom rendering implementations
  • Non-image output (e.g., ASCII art, LED displays)
  • Maximum control over the rendering process

Example:

var qrData = QRCodeGenerator.CreateQrCode("content", ECCLevel.M, quietZoneSize: 4);
// Use qrData for custom rendering
// Access individual modules: bool isDark = qrData[row, col];

Platform-Specific Considerations

Linux Support

SkiaSharp requires native dependencies on Linux. You have two options:

Requires libfontconfig1:

sudo apt update && apt install -y libfontconfig1
<PackageReference Include="SkiaSharp.QrCode" Version="0.9.0" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="3.119.1" />

Option 2: No Dependencies (QR code only)

If you don't need advanced font operations:

<PackageReference Include="SkiaSharp.QrCode" Version="0.9.0" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="3.119.1" />

Note: NoDependencies can still draw text but cannot search fonts based on characters or use system fonts.

See: SkiaSharp Issue #964

NativeAOT Support

SkiaSharp.QrCode fully supports .NET NativeAOT. You need to include platform-specific native assets:

<PropertyGroup>
  <PublishAot>true</PublishAot>
  <PublishTrimmed>true</PublishTrimmed>
  <InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>

Warning

When using PublishTrimmed, ensure that your QR code content and rendering logic doesn't rely on reflection or dynamic code that might be trimmed.

Windows

<PackageReference Include="SkiaSharp.QrCode" Version="0.9.0" />
<PackageReference Include="SkiaSharp.NativeAssets.Win32" Version="3.119.1" />

Linux

<PackageReference Include="SkiaSharp.QrCode" Version="0.9.0" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="3.119.1" />

macOS

<PackageReference Include="SkiaSharp.QrCode" Version="0.9.0" />
<PackageReference Include="SkiaSharp.NativeAssets.macOS" Version="3.119.1" />

Performance

SkiaSharp.QrCode is designed with performance as a top priority. The library minimizes memory allocations and maximizes throughput for QR code generation.

Key Performance Characteristics

  • Minimal Memory Allocation: Memory is only allocated for the final QR code data structure. The generation algorithm avoids intermediate allocations, resulting in minimal GC pressure.
  • Zero-Copy Rendering: Direct rendering to SkiaSharp canvas without unnecessary buffer copies.
  • Optimized Encoding: Efficient encoding mode selection and bit packing minimize QR code size and generation time.
  • Native Performance: Leverages SkiaSharp's native rendering engine for maximum speed.

Benchmark Results

Benchmark results show SkiaSharp.QrCode outperforming other popular .NET QR code libraries in both speed and memory usage.

  • Fastest Generation: Outperforms other .NET QR code libraries in most scenarios
  • Lowest Memory Usage: Minimal allocations reduce GC overhead
  • Consistent Performance: Predictable performance across different QR code sizes and complexity

For detailed benchmark code and results, see the samples/Benchmark directory.

FAQ

Why choose SkiaSharp.QrCode?

SkiaSharp offers several advantages for QR code generation:

  • Performance: Native-level performance with hardware acceleration support
  • Cross-Platform: Runs on Windows, Linux, macOS, iOS, Android, and WebAssembly
  • Modern .NET Support: First-class support for .NET 6+ and .NET Core
  • No GDI+ Dependencies: Avoids System.Drawing's Windows-specific issues
  • Rich Graphics API: Advanced rendering capabilities (gradients, shapes, effects)
  • Active Development: Well-maintained with regular updates

Can I use this in ASP.NET Core?

Yes, SkiaSharp.QrCode works great in ASP.NET Core. SkiaSharp.QrCode also supports IBufferWriter for efficient memory usage.

app.MapGet("/qr", (string url) =>
{
    var pngBytes = QRCodeImageBuilder.GetPngBytes(url);
    return Results.File(pngBytes, "image/png");
});

Does it support Blazor WebAssembly?

Yes, SkiaSharp works in Blazor WebAssembly. See the samples/BlazorWasm folder for a complete example.

What about NativeAOT and trimming?

Yes, fully supported. See the Platform-Specific Considerations section for details on required native assets.

ISO-8859-2 and other encodings supports

Currently, SkiaSharp.QrCode supports ISO-8859-1 and UTF-8. Other encodings (e.g., ISO-8859-2, Shift JIS) are not supported at this time. This is mainly due to almost all QR code use cases being UTF-8 compatible nowadays. ISO-8859-2 and other legacy encodings are rarely used in practice.

SupportedEncoding ModeEncoding
SupportedNumericISO-8859-1
SupportedAlphanumericISO-8859-1
SupportedByteUTF-8
Not SupportedKanjiShift-JIS

Any plan to support QR code scanning?

Currently, SkiaSharp.QrCode focuses on QR code generation. QR code scanning is not planned at this time, but contributions are welcome.

What QR code style provides the best scan reliability?

For optimal scan reliability, we recommend:

  • Use rectangular modules (default): Rectangle-shaped modules (RectangleModuleShape) provide the lowest error rate when scanning QR codes.
  • Avoid gaps between modules: Using smaller module sizes or shapes like Circle or RoundRect creates gaps between modules, which increases scan error rates.
  • Use ECCLevel.H for non-standard styles: If you need to use Circle, RoundRect, or other custom module shapes, we strongly recommend setting the error correction level to ECCLevel.H (High - 30% recovery capacity) to compensate for the reduced readability.
  • Always use ECCLevel.H with icons/logos: When embedding icons or logos using IconData, ECCLevel.H is required to ensure the QR code remains scannable even when the center is partially obscured.

Example:

// Best reliability - default settings with rectangular modules
var pngBytes = QRCodeImageBuilder.GetPngBytes("https://example.com");

// If using Circle or RoundRect - use High error correction
var qrCode = new QRCodeImageBuilder("https://example.com")
    .WithSize(800, 800)
    .WithErrorCorrection(ECCLevel.H) // Required for custom shapes
    .WithModuleShape(CircleModuleShape.Default);

// When using icons/logos - always use High error correction
using var logo = SKBitmap.Decode(File.ReadAllBytes("logo.png"));
var icon = IconData.FromImage(logo, iconSizePercent: 15);

var qrCodeWithIcon = new QRCodeImageBuilder("https://example.com")
    .WithSize(800, 800)
    .WithErrorCorrection(ECCLevel.H) // Required for icons
    .WithIcon(icon);

How can I display QR codes in LINQPad?

Following shows how to display a QRCode inside a LINQPad Results pane.

Bitmap.FromStream(new MemoryStream(QRCodeImageBuilder.GetPngBytes("WIFI:T:WPA;S:mynetwork;P:mypass;;"))).Dump();

QR code Specifications

ECC Level (Error Correction Levels)

QR codes support four levels of error correction, which allow the code to remain readable even when partially damaged or obscured:

ECC LevelError Correction CapabilityUse Case
L (Low)~7% recoveryClean environments, maximum data capacity
M (Medium)~15% recoveryGeneral purpose (default recommended)
Q (Quartile)~25% recoveryOutdoor use, moderate damage expected
H (High)~30% recoveryRequired when adding logos/icons, harsh environments

Tip: Use ECC Level H when embedding icons in QR codes to ensure readability even when the center is obscured.

Encoding Modes

QR codes support different encoding modes optimized for specific character types:

ModeCharacter SetBits per CharacterExample
Numeric0-9~3.3 bitsPhone numbers, postal codes
Alphanumeric0-9, A-Z, space, $ % * + - . / :~5.5 bitsURLs (uppercase), product codes
ByteISO-8859-1, UTF-88 bitsText, URLs (mixed case), binary data
KanjiShift JIS characters13 bitsJapanese text

Note: The library automatically selects the most efficient encoding mode for your content.

Version and Size

QR codes come in 40 versions (sizes), from Version 1 (21×21 modules) to Version 40 (177×177 modules). Each version adds 4 modules per side.

  • Version 1: 21×21 modules
  • Version 2: 25×25 modules
  • ...
  • Version 40: 177×177 modules

The library automatically selects the minimum version that can fit your content based on the selected ECC level.

Data Capacity Reference

The actual capacity depends on the encoding mode, ECC level, and version. Below are practical capacities including overhead (based on test data using 'あ' for UTF-8 multi-byte characters):

Quick Reference (Version 10, Common Use Cases)

ECC LevelNumericAlphanumericByte (ASCII)Byte (UTF-8 Multi-byte*)
L652395~27090
M513311~21070
Q364221~15050
H288174~11739

UTF-8 multi-byte: Japanese hiragana 'あ' (3 bytes/char). For ASCII text (1 byte/char), the capacity is approximately 3× the values shown.

Full capacity tables for all versions (1-40) and ECC levels are available in the Data Capacity Tables section below.

Data Capacity Tables

Full capacity tables for all QR code versions and ECC Levels.

Test Characters:

  • Numeric: '1' (digit)
  • Alphanumeric: 'A' (uppercase letter)
  • Byte (ASCII): 'a' (lowercase, 1 byte)
  • Byte (UTF-8): 'あ' (hiragana, 3 bytes)

Important Notes:

  • Numeric: Pure digit count (0-9)
  • Alphanumeric: Uppercase letters, digits, and symbols (45 character set)
  • Byte: UTF-8 encoded Japanese characters (ひらがな 'あ' = 3 bytes per character)
    • For ASCII characters (1 byte each), multiply the byte value by ~3
    • For theoretical byte capacity, refer to ISO/IEC 18004 Table 7

ECC Level: L

Click to expand full capacity tables
VersionNumericAlphanumericByte (UTF-8 Multi-byte*)
141255
2774710
31277717
418711425
525515435
632219544
737022451
846127963
955233576
1065239590
11772468106
12883535122
131022619141
141101667152
151250758173
161408854195
171548938214
1817251046239
1919031153263
2020611249285
2122321352309
2224091460334
2326201588363
2428121704390
2530571853424
2632831990455
2735172132488
2836692223509
2939092369542
3041582520577
3144172677613
3246862840650
3349653009689
3452533183729
3555293351767
3658363537810
3761533729854
3864793927899
3967434087936
4070894296984

ECC Level: M

Click to expand full capacity tables
VersionNumericAlphanumericByte (UTF-8 Multi-byte*)
134204
263388
31016113
41499020
520212227
625515435
729317840
836522150
943226259
1051331170
1160436683
1269141995
13796483110
14871528120
15991600137
161082656149
171212734167
181346816186
191500909207
201600970221
2117081035236
2218721134259
2320591248285
2421881326303
2523951451332
2625441542352
2727011637374
2828571732396
2930351839421
3032891994456
3134862113483
3236932238512
3339092369542
3441342506573
3543432632602
3645882780636
3747752894662
3850393054699
3953133220737
4055963391776

ECC Level: Q

Click to expand full capacity tables
VersionNumericAlphanumericByte (UTF-8 Multi-byte*)
127163
248296
3774710
41116715
51448719
617810824
720712528
825915735
931218943
1036422150
1142725958
1248929667
1358035280
1462137685
1570342697
16775470107
17876531121
18948574131
191063644147
201159702160
211224742169
221358823188
231468890203
241588963220
2517181041238
2618041094250
2719331172268
2820851263289
2921811322302
3023581429327
3124731499343
3226701618370
3328051700389
3429491787409
3530811867427
3632441966450
3734172071474
3835992181499
3937912298526
4039932420554

ECC Level: H

Click to expand full capacity tables
VersionNumericAlphanumericByte (UTF-8 Multi-byte*)
117102
234204
358357
4825011
51066414
61398419
71549321
820212227
923514332
1028817439
1133120045
1237422751
1342725958
1446828364
1553032173
1660236583
1767440893
18746452103
19813493112
20919557127
21969587134
221056640146
231108672153
241228744170
251286779178
261425864197
271501910208
281581958219
2916771016232
3017821080247
3118971150263
3220221226280
3321571307299
3423011394319
3523611431327
3625241530350
3726251591364
3827351658379
3929271774406
4030571852424

Usage Examples

Basic Usage

Using Builder Pattern

using SkiaSharp.QrCode.Image;

var qrCode = new QRCodeImageBuilder("https://example.com")
    .WithSize(800, 800)
    .WithErrorCorrection(ECCLevel.H)
    .WithQuietZone(4);

var pngBytes = qrCode.ToByteArray();
File.WriteAllBytes("qrcode.png", pngBytes);

Direct File Output

using SkiaSharp.QrCode.Image;
using var stream = File.OpenWrite("qrcode.png");

new QRCodeImageBuilder("https://example.com")
    .WithSize(1024, 1024)
    .WithErrorCorrection(ECCLevel.H)
    .SaveTo(stream);

Advanced Usage

Custom Colors

using SkiaSharp;
using SkiaSharp.QrCode.Image;

new QRCodeImageBuilder("https://example.com")
    .WithSize(800, 800)
    .WithColors(
        codeColor: SKColor.Parse("#000080"),      // Navy
        backgroundColor: SKColor.Parse("#FFE4B5"), // Moccasin
        clearColor: SKColors.Transparent)
    .ToByteArray();

Gradient QR code

using SkiaSharp;
using SkiaSharp.QrCode.Image;

var gradient = new GradientOptions(
    [SKColors.Blue, SKColors.Purple, SKColors.Pink],
    GradientDirection.TopLeftToBottomRight,
    [0f, 0.5f, 1f]);

var qrCode = new QRCodeImageBuilder("https://example.com")
    .WithSize(800, 800)
    .WithErrorCorrection(ECCLevel.H)
    .WithGradient(gradient)
    .WithModuleShape(RoundedRectangleModuleShape.Default, sizePercent: 0.9f);

var pngBytes = qrCode.ToByteArray();

QR code with Logo (icon only)

using SkiaSharp;
using SkiaSharp.QrCode.Image;

using var logo = SKBitmap.Decode(File.ReadAllBytes("logo.png"));
var icon = IconData.FromImage(logo, iconSizePercent: 15, iconBorderWidth: 10);
var qrCode = new QRCodeImageBuilder("https://example.com")
    .WithSize(800, 800)
    .WithErrorCorrection(ECCLevel.H) // High ECC recommended for icons
    .WithIcon(icon);

var pngBytes = qrCode.ToByteArray();

QR code with Logo (icon and text)

using SkiaSharp;
using SkiaSharp.QrCode.Image;

using var logo = SKBitmap.Decode(File.ReadAllBytes("logo.png"));
using var font = new SKFont
{
    Size = 18,
    Typeface = SKTypeface.FromFamilyName("sans-serif", SKFontStyle.Bold)
};
var icon = new IconData
{
    // Default text is placed below the icon image, centered
    Icon = new ImageTextIconShape(logo, "FooBar", SKColors.Black, font, textPadding: 2),
    IconSizePercent = 13,
    IconBorderWidth = 18,
};
var qrCode = new QRCodeImageBuilder("https://example.com")
    .WithSize(800, 800)
    .WithErrorCorrection(ECCLevel.H) // High ECC recommended for icons
    .WithIcon(icon);

var pngBytes = qrCode.ToByteArray();

Custom Module Shapes

using SkiaSharp;
using SkiaSharp.QrCode.Image;

var qrCode = new QRCodeImageBuilder("https://example.com")
    .WithSize(800, 800)
    .WithModuleShape(CircleModuleShape.Default, sizePercent: 0.95f)
    .WithColors(codeColor: SKColors.DarkBlue);

var pngBytes = qrCode.ToByteArray();

Custom Finder Pattern

var qrCode = new QRCodeImageBuilder("https://example.com")
    .WithSize(512, 512)
    .WithFinderPatternShape(RoundedRectangleFinderPatternShape.Default)
    .WithColors(codeColor: SKColors.DarkBlue);

var pngBytes = qrCode.ToByteArray();

Gradient QR Code

var instagramGradient = new GradientOptions([
        SKColor.Parse("FCAF45"),  // Orange
        SKColor.Parse("F77737"),  // Orange-Red
        SKColor.Parse("E1306C"),  // Pink
        SKColor.Parse("C13584"),  // Purple
        SKColor.Parse("833AB4")   // Deep Purple
    ],
    GradientDirection.TopLeftToBottomRight,
    [0f, 0.25f, 0.5f, 0.75f, 1f]);

var qrCode = new QRCodeImageBuilder(content)
    .WithSize(512, 512)
    .WithColors(backgroundColor: SKColors.White, clearColor: SKColors.White)
    .WithModuleShape(CircleModuleShape.Default, sizePercent: 0.95f)
    .WithFinderPatternShape(RoundedRectangleCircleFinderPatternShape.Default)
    .WithGradient(instagramGradient);

var pngBytes = qrCode.ToByteArray();

Low-Level Canvas Rendering

For maximum control over rendering:

using SkiaSharp;
using SkiaSharp.QrCode;

// Generate QR data
var qrData = QRCodeGenerator.CreateQrCode("https://example.com", ECCLevel.M, quietZoneSize: 4);

// Create canvas
var info = new SKImageInfo(800, 800);
using var surface = SKSurface.Create(info);
var canvas = surface.Canvas;

// Render QR code
canvas.Render(qrData, info.Width, info.Height);

// Save
using var image = surface.Snapshot();
using var data = image.Encode(SKEncodedImageFormat.Png, 100);
using var stream = File.OpenWrite("qrcode.png");
data.SaveTo(stream);

License

MIT

Acknowledgments