diff --git a/QRCoder/Base64QRCode.cs b/QRCoder/Base64QRCode.cs index 539af073..a35d9970 100644 --- a/QRCoder/Base64QRCode.cs +++ b/QRCoder/Base64QRCode.cs @@ -3,7 +3,6 @@ using System.Drawing; using System.Drawing.Imaging; using System.IO; -using static QRCoder.Base64QRCode; using static QRCoder.QRCodeGenerator; namespace QRCoder @@ -88,13 +87,6 @@ private string BitmapToBase64(Bitmap bmp, ImageType imgType) return base64; } - public enum ImageType - { - Gif, - Jpeg, - Png - } - } #if NET6_0_WINDOWS diff --git a/QRCoder/ImageSharp/Base64QRCode.cs b/QRCoder/ImageSharp/Base64QRCode.cs new file mode 100644 index 00000000..c58feb56 --- /dev/null +++ b/QRCoder/ImageSharp/Base64QRCode.cs @@ -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; + + /// + /// Constructor without params to be used in COM Objects connections + /// + 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); + } + } + } +} diff --git a/QRCoder/ImageSharp/QRCode.cs b/QRCoder/ImageSharp/QRCode.cs new file mode 100644 index 00000000..f3c5b25c --- /dev/null +++ b/QRCoder/ImageSharp/QRCode.cs @@ -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 + { + /// + /// Constructor without params to be used in COM Objects connections + /// + 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(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; + 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 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); + } + } + } +} diff --git a/QRCoder/ImageType.cs b/QRCoder/ImageType.cs new file mode 100644 index 00000000..72568dca --- /dev/null +++ b/QRCoder/ImageType.cs @@ -0,0 +1,9 @@ +namespace QRCoder +{ + public enum ImageType + { + Gif, + Jpeg, + Png + } +} diff --git a/QRCoder/QRCoder.csproj b/QRCoder/QRCoder.csproj index d67a7989..f055f64f 100644 --- a/QRCoder/QRCoder.csproj +++ b/QRCoder/QRCoder.csproj @@ -4,7 +4,7 @@ net35;net40;netstandard1.3;netstandard2.0;net5.0;net5.0-windows;net6.0;net6.0-windows false $(DefineConstants);NET5_0_WINDOWS - $(DefineConstants);NET6_0_WINDOWS + $(DefineConstants);NET6_0_WINDOWS false true @@ -48,9 +48,17 @@ - - - + + + + + + + + + + + $(MSBuildProgramFiles32)\Reference Assemblies\Microsoft\Framework\.NETFramework\v3.5\Profile\Client diff --git a/QRCoderConsole/Program.cs b/QRCoderConsole/Program.cs index e9af916a..130ce4d2 100644 --- a/QRCoderConsole/Program.cs +++ b/QRCoderConsole/Program.cs @@ -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; @@ -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)) @@ -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)) @@ -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()) @@ -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 } } diff --git a/QRCoderConsole/QRCoderConsole.csproj b/QRCoderConsole/QRCoderConsole.csproj index 94c07191..24fa14c8 100644 --- a/QRCoderConsole/QRCoderConsole.csproj +++ b/QRCoderConsole/QRCoderConsole.csproj @@ -5,7 +5,8 @@ true $(DefineConstants);NET5_0_WINDOWS - $(DefineConstants);NET6_0_WINDOWS + $(DefineConstants);NET6_0_WINDOWS + $(DefineConstants);IMAGE_SHARP true Exe false diff --git a/readme.md b/readme.md index 7ffdda2f..521364bc 100644 --- a/readme.md +++ b/readme.md @@ -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.*