Skip to content

Commit

Permalink
Add ImageSharp renderers for .net 6.0, 5.0 and standart 2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
AntonKorn committed Jul 9, 2023
1 parent dd35a3b commit 2fb0819
Show file tree
Hide file tree
Showing 8 changed files with 302 additions and 16 deletions.
8 changes: 0 additions & 8 deletions QRCoder/Base64QRCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using static QRCoder.Base64QRCode;
using static QRCoder.QRCodeGenerator;

namespace QRCoder
Expand Down Expand Up @@ -88,13 +87,6 @@ private string BitmapToBase64(Bitmap bmp, ImageType imgType)
return base64;
}

public enum ImageType
{
Gif,
Jpeg,
Png
}

}

#if NET6_0_WINDOWS
Expand Down
104 changes: 104 additions & 0 deletions QRCoder/ImageSharp/Base64QRCode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
using System;
using System.IO;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats;
using static QRCoder.QRCodeGenerator;

namespace QRCoder.ImageSharp
{
public class Base64QRCode : AbstractQRCode, IDisposable
{
private QRCode qr;

/// <summary>
/// Constructor without params to be used in COM Objects connections
/// </summary>
public Base64QRCode()
{
qr = new QRCode();
}

public Base64QRCode(QRCodeData data)
: base(data)
{
qr = new QRCode(data);
}

public override void SetQRCodeData(QRCodeData data)
{
qr.SetQRCodeData(data);
}

public string GetGraphic(int pixelsPerModule)
{
return GetGraphic(pixelsPerModule, Color.Black, Color.White, true);
}

public string GetGraphic(int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex, bool drawQuietZones = true, ImageType imgType = ImageType.Png)
{
return GetGraphic(pixelsPerModule, Color.Parse(darkColorHtmlHex), Color.Parse(lightColorHtmlHex), drawQuietZones, imgType);
}

public string GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, bool drawQuietZones = true, ImageType imgType = ImageType.Png)
{
var base64 = string.Empty;
using (Image img = qr.GetGraphic(pixelsPerModule, darkColor, lightColor, drawQuietZones))
{
base64 = BitmapToBase64(img, imgType);
}

return base64;
}

public string GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, Image icon, int iconSizePercent = 15, int iconBorderWidth = 6, bool drawQuietZones = true, ImageType imgType = ImageType.Png)
{
var base64 = string.Empty;
using (Image bmp = qr.GetGraphic(pixelsPerModule, darkColor, lightColor, icon, iconSizePercent, iconBorderWidth, drawQuietZones))
{
base64 = BitmapToBase64(bmp, imgType);
}

return base64;
}

private string BitmapToBase64(Image img, ImageType imgType)
{
var base64 = string.Empty;
IImageEncoder iFormat;
switch (imgType)
{
default:
case ImageType.Png:
iFormat = new SixLabors.ImageSharp.Formats.Png.PngEncoder();
break;
case ImageType.Jpeg:
iFormat = new SixLabors.ImageSharp.Formats.Jpeg.JpegEncoder();
break;
case ImageType.Gif:
iFormat = new SixLabors.ImageSharp.Formats.Gif.GifEncoder();
break;
}

using (var memoryStream = new MemoryStream())
{
img.Save(memoryStream, iFormat);
base64 = Convert.ToBase64String(memoryStream.ToArray(), Base64FormattingOptions.None);
}

return base64;
}
}

public static class ImageSharpBase64QRCodeHelper
{
public static string GetQRCode(string plainText, int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, bool drawQuietZones = true, ImageType imgType = ImageType.Png)
{
using (var qrGenerator = new QRCodeGenerator())
using (var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion))
using (var qrCode = new Base64QRCode(qrCodeData))
{
return qrCode.GetGraphic(pixelsPerModule, darkColorHtmlHex, lightColorHtmlHex, drawQuietZones, imgType);
}
}
}
}
129 changes: 129 additions & 0 deletions QRCoder/ImageSharp/QRCode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
using System;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using static QRCoder.QRCodeGenerator;

namespace QRCoder.ImageSharp
{
public class QRCode : AbstractQRCode, IDisposable
{
/// <summary>
/// Constructor without params to be used in COM Objects connections
/// </summary>
public QRCode() { }

public QRCode(QRCodeData data)
: base(data) { }

public Image GetGraphic(int pixelsPerModule)
{
return GetGraphic(pixelsPerModule, Color.Black, Color.White, true);
}

public Image GetGraphic(int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex, bool drawQuietZones = true)
{
return GetGraphic(pixelsPerModule, Color.Parse(darkColorHtmlHex), Color.Parse(lightColorHtmlHex), drawQuietZones);
}

public Image GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, bool drawQuietZones = true)
{
var moduleOffset = drawQuietZones ? 0 : 4;
var size = (QrCodeData.ModuleMatrix.Count - (moduleOffset * 2)) * pixelsPerModule;

var image = new Image<Rgba32>(size, size);
DrawQRCode(image, pixelsPerModule, moduleOffset, darkColor, lightColor);

return image;
}

public Image GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, Image icon = null, int iconSizePercent = 15, int iconBorderWidth = 0, bool drawQuietZones = true, Color? iconBackgroundColor = null)
{
var img = GetGraphic(pixelsPerModule, darkColor, lightColor, drawQuietZones) as Image<Rgba32>;
if (icon != null && iconSizePercent > 0 && iconSizePercent <= 100)
{
var iconDestWidth = iconSizePercent * img.Width / 100f;
var iconDestHeight = iconDestWidth * icon.Height / icon.Width;
var iconX = (img.Width - iconDestWidth) / 2;
var iconY = (img.Height - iconDestHeight) / 2;
var centerDest = new RectangleF(iconX - iconBorderWidth, iconY - iconBorderWidth, iconDestWidth + (iconBorderWidth * 2), iconDestHeight + (iconBorderWidth * 2));
var iconDestRect = new RectangleF(iconX, iconY, iconDestWidth, iconDestHeight);

if (iconBorderWidth > 0)
{
if (!iconBackgroundColor.HasValue)
{
iconBackgroundColor = lightColor;
}

if (iconBackgroundColor != Color.Transparent)
{
img.ProcessPixelRows(accessor =>
{
for (var y = (int)centerDest.Top; y <= (int)centerDest.Bottom; y++)
{
var pixelRow = accessor.GetRowSpan(y);
for (var x = (int)centerDest.Left; x <= (int)centerDest.Right; x++)
{
pixelRow[x] = iconBackgroundColor ?? lightColor;
}
}
});
}
}

var sizedIcon = icon.Clone(x => x.Resize((int)iconDestWidth, (int)iconDestHeight));
img.Mutate(x => x.DrawImage(sizedIcon, new Point((int)iconDestRect.X, (int)iconDestRect.Y), 1));
}

return img;
}

private void DrawQRCode(Image<Rgba32> image, int pixelsPerModule, int moduleOffset, Color darkColor, Color lightColor)
{
var row = new Rgba32[image.Width];

image.ProcessPixelRows(accessor =>
{
for (var modY = moduleOffset; modY < QrCodeData.ModuleMatrix.Count - moduleOffset; modY++)
{
// Generate row for this y-Module
for (var modX = moduleOffset; modX < QrCodeData.ModuleMatrix.Count - moduleOffset; modX++)
{
for (var idx = 0; idx < pixelsPerModule; idx++)
{
row[((modX - moduleOffset) * pixelsPerModule) + idx] = this.QrCodeData.ModuleMatrix[modY][modX] ? darkColor : lightColor;
}
}
// Copy the prepared row to the image
for (var idx = 0; idx < pixelsPerModule; idx++)
{
var pixelRow = accessor.GetRowSpan(((modY - moduleOffset) * pixelsPerModule) + idx);
row.CopyTo(pixelRow);
}
}
});
}
}

public static class ImageSharpQRCodeHelper
{
public static Image GetQRCode(string plainText, int pixelsPerModule, Color darkColor, Color lightColor, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, Image icon = null, int iconSizePercent = 15, int iconBorderWidth = 0, bool drawQuietZones = true)
{
using (var qrGenerator = new QRCodeGenerator())
using (var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion))
using (var qrCode = new QRCode(qrCodeData))
{
return qrCode.GetGraphic(pixelsPerModule,
darkColor,
lightColor,
icon,
iconSizePercent,
iconBorderWidth,
drawQuietZones);
}
}
}
}
9 changes: 9 additions & 0 deletions QRCoder/ImageType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace QRCoder
{
public enum ImageType
{
Gif,
Jpeg,
Png
}
}
16 changes: 12 additions & 4 deletions QRCoder/QRCoder.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<TargetFrameworks>net35;net40;netstandard1.3;netstandard2.0;net5.0;net5.0-windows;net6.0;net6.0-windows</TargetFrameworks>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
<DefineConstants Condition="'$(TargetFramework)' == 'net5.0-windows'">$(DefineConstants);NET5_0_WINDOWS</DefineConstants>
<DefineConstants Condition="'$(TargetFramework)' == 'net6.0-windows'">$(DefineConstants);NET6_0_WINDOWS</DefineConstants>
<DefineConstants Condition="'$(TargetFramework)' == 'net6.0-windows'">$(DefineConstants);NET6_0_WINDOWS</DefineConstants>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<DisableImplicitNuGetFallbackFolder>true</DisableImplicitNuGetFallbackFolder>
</PropertyGroup>
Expand Down Expand Up @@ -48,9 +48,17 @@
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'net5.0' or '$(TargetFramework)' == 'net5.0-windows' ">
<PackageReference Include="System.Drawing.Common" Version="5.0.3" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net6.0-windows' ">
<PackageReference Include="System.Drawing.Common" Version="6.0.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net6.0-windows' ">
<PackageReference Include="System.Drawing.Common" Version="6.0.0" />
</ItemGroup>
<ItemGroup Condition=" $(TargetFramework.StartsWith('net5.0')) == true or $(TargetFramework.StartsWith('net6.0')) == true or '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.4" />
</ItemGroup>

<ItemGroup Condition=" $(TargetFramework.StartsWith('net5.0')) != true and $(TargetFramework.StartsWith('net6.0')) != true and '$(TargetFramework)' != 'netstandard2.0' ">
<None Include="ImageSharp\*" />
<Compile Remove="ImageSharp\*" />
</ItemGroup>

<PropertyGroup>
<FrameworkPathOverride Condition="'$(TargetFramework)' == 'net35'">$(MSBuildProgramFiles32)\Reference Assemblies\Microsoft\Framework\.NETFramework\v3.5\Profile\Client</FrameworkPathOverride>
Expand Down
47 changes: 44 additions & 3 deletions QRCoderConsole/Program.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
using System;
#if !IMAGE_SHARP
using System.Drawing.Imaging;
#else
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.Formats.Tiff;
#endif
using System.IO;
using NDesk.Options;
using QRCoderConsole.DataObjects;
Expand Down Expand Up @@ -147,6 +157,7 @@ private static void GenerateQRCode(string payloadString, QRCodeGenerator.ECCLeve
case SupportedImageFormat.Gif:
case SupportedImageFormat.Bmp:
case SupportedImageFormat.Tiff:
#if !IMAGE_SHARP
using (var code = new QRCode(data))
{
using (var bitmap = code.GetGraphic(pixelsPerModule, foreground, background, true))
Expand All @@ -155,6 +166,17 @@ private static void GenerateQRCode(string payloadString, QRCodeGenerator.ECCLeve
bitmap.Save(outputFileName, actualFormat);
}
}
#else
using (var code = new QRCoder.ImageSharp.QRCode(data))
{
using (var bitmap = code.GetGraphic(pixelsPerModule, foreground, background, true))
{
var actualFormat = new OptionSetter().GetImageFormat(imgFormat.ToString());
bitmap.Save(outputFileName, actualFormat);
}
}
#endif

break;
case SupportedImageFormat.Svg:
using (var code = new SvgQRCode(data))
Expand Down Expand Up @@ -251,9 +273,7 @@ public QRCodeGenerator.ECCLevel GetECCLevel(string value)
return level;
}

#if NET6_0_WINDOWS
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
#endif
#if !IMAGE_SHARP
public ImageFormat GetImageFormat(string value)
{
switch (value.ToLower())
Expand All @@ -273,6 +293,27 @@ public ImageFormat GetImageFormat(string value)
return ImageFormat.Png;
}
}
#else
public IImageEncoder GetImageFormat(string value)
{
switch (value.ToLower())
{
case "jpg":
return new JpegEncoder();
case "jpeg":
return new PngEncoder();
case "gif":
return new GifEncoder();
case "bmp":
return new BmpEncoder();
case "tiff":
return new TiffEncoder();
case "png":
default:
return new PngEncoder();
}
}
#endif
}
}

3 changes: 2 additions & 1 deletion QRCoderConsole/QRCoderConsole.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
<!-- <UseWindowsForms Condition="'$(TargetFramework)' == 'net5.0-windows' or '$(TargetFramework)' == 'net5.0-windows'">true</UseWindowsForms>-->
<UseWPF Condition="'$(TargetFramework)' == 'net5.0-windows' or '$(TargetFramework)' == 'net6.0-windows'">true</UseWPF>
<DefineConstants Condition="'$(TargetFramework)' == 'net5.0-windows'">$(DefineConstants);NET5_0_WINDOWS</DefineConstants>
<DefineConstants Condition="'$(TargetFramework)' == 'net6.0-windows'">$(DefineConstants);NET6_0_WINDOWS</DefineConstants>
<DefineConstants Condition="'$(TargetFramework)' == 'net6.0-windows'">$(DefineConstants);NET6_0_WINDOWS</DefineConstants>
<DefineConstants Condition=" $(TargetFramework.StartsWith('net5.0')) == true or $(TargetFramework.StartsWith('net6.0')) == true or '$(TargetFramework)' == 'netstandard2.0' ">$(DefineConstants);IMAGE_SHARP</DefineConstants>
<Externalconsole>true</Externalconsole>
<OutputType>Exe</OutputType>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
Expand Down
2 changes: 2 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ Besides the normal QRCode class (which is shown in the example above) for creati
* [SvgQRCode](https://github.com/codebude/QRCoder/wiki/Advanced-usage---QR-Code-renderers#26-svgqrcode-renderer-in-detail)
* [UnityQRCode](https://github.com/codebude/QRCoder/wiki/Advanced-usage---QR-Code-renderers#27-unityqrcode-renderer-in-detail) (_via [QRCoder.Unity](https://www.nuget.org/packages/QRCoder.Unity)_)
* [XamlQRCode](https://github.com/codebude/QRCoder/wiki/Advanced-usage---QR-Code-renderers#28-xamlqrcode-renderer-in-detail) (_via [QRCoder.Xaml](https://www.nuget.org/packages/QRCoder.Xaml)_)
* ImageSharp.QRCode (repeats logic of QRCode but uses ImageSharp under the hood)
* ImageSharp.Base64QRCode (repeats logic of Base64QRCode but uses ImageSharp under the hood)

*Note: Please be aware that not all renderers are available on all target frameworks. Please check the [compatibility table](https://github.com/codebude/QRCoder/wiki/Advanced-usage---QR-Code-renderers#2-overview-of-the-different-renderers) in our wiki, to see if a specific renderer is available on your favourite target framework.*

Expand Down

0 comments on commit 2fb0819

Please sign in to comment.