Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ImageSharp renderers for .net 6.0, 5.0 and standart 2.0 #460

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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