diff --git a/QRCoder/PngByteQRCode.cs b/QRCoder/PngByteQRCode.cs
index 80d1766e..239287d7 100644
--- a/QRCoder/PngByteQRCode.cs
+++ b/QRCoder/PngByteQRCode.cs
@@ -20,13 +20,13 @@ public PngByteQRCode(QRCodeData data) : base(data)
///
/// Creates a black & white PNG of the QR code, using 1-bit grayscale.
///
- public byte[] GetGraphic(int pixelsPerModule)
+ public byte[] GetGraphic(int pixelsPerModule, bool drawQuietZones = true)
{
using (var png = new PngBuilder())
{
- var size = this.QrCodeData.ModuleMatrix.Count * pixelsPerModule;
+ var size = (this.QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8)) * pixelsPerModule;
png.WriteHeader(size, size, 1, PngBuilder.ColorType.Greyscale);
- png.WriteScanlines(this.DrawScanlines(pixelsPerModule));
+ png.WriteScanlines(this.DrawScanlines(pixelsPerModule, drawQuietZones));
png.WriteEnd();
return png.GetBytes();
}
@@ -35,14 +35,14 @@ public byte[] GetGraphic(int pixelsPerModule)
///
/// Creates 2-color PNG of the QR code, using 1-bit indexed color. Accepts 3-byte RGB colors for normal images and 4-byte RGBA-colors for transparent images.
///
- public byte[] GetGraphic(int pixelsPerModule, byte[] darkColorRgba, byte[] lightColorRgba)
+ public byte[] GetGraphic(int pixelsPerModule, byte[] darkColorRgba, byte[] lightColorRgba, bool drawQuietZones = true)
{
using (var png = new PngBuilder())
{
- var size = this.QrCodeData.ModuleMatrix.Count * pixelsPerModule;
+ var size = (this.QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8)) * pixelsPerModule;
png.WriteHeader(size, size, 1, PngBuilder.ColorType.Indexed);
png.WritePalette(darkColorRgba, lightColorRgba);
- png.WriteScanlines(this.DrawScanlines(pixelsPerModule));
+ png.WriteScanlines(this.DrawScanlines(pixelsPerModule, drawQuietZones));
png.WriteEnd();
return png.GetBytes();
}
@@ -51,22 +51,23 @@ public byte[] GetGraphic(int pixelsPerModule, byte[] darkColorRgba, byte[] light
///
/// Creates a bitmap where each pixel is represented by a single bit, dark = 0 and light = 1.
///
- private byte[] DrawScanlines(int pixelsPerModule)
+ private byte[] DrawScanlines(int pixelsPerModule, bool drawQuietZones)
{
var moduleMatrix = this.QrCodeData.ModuleMatrix;
- var matrixSize = moduleMatrix.Count;
+ var matrixSize = moduleMatrix.Count - (drawQuietZones ? 0 : 8);
+ var quietZoneOffset = (drawQuietZones ? 0 : 4);
var bytesPerScanline = (matrixSize * pixelsPerModule + 7) / 8 + 1; // A monochrome scanline is one byte for filter type then one bit per pixel.
var scanlines = new byte[bytesPerScanline * matrixSize * pixelsPerModule];
for (var y = 0; y < matrixSize; y++)
{
- var modules = moduleMatrix[y];
+ var modules = moduleMatrix[y+quietZoneOffset];
var scanlineOffset = y * pixelsPerModule * bytesPerScanline;
// Draw a scanline with the modules from the QR code.
for (var x = 0; x < matrixSize; x++)
{
- if (modules[x])
+ if (modules[x + quietZoneOffset])
{
continue;
}
@@ -319,22 +320,22 @@ private static uint Crc32(byte[] data, int index, int length)
public static class PngByteQRCodeHelper
{
- public static byte[] GetQRCode(string plainText, int pixelsPerModule, byte[] darkColorRgba, byte[] lightColorRgba, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1)
+ public static byte[] GetQRCode(string plainText, int pixelsPerModule, byte[] darkColorRgba, byte[] lightColorRgba, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false, EciMode eciMode = EciMode.Default, int requestedVersion = -1, bool drawQuietZones = true)
{
using (var qrGenerator = new QRCodeGenerator())
using (var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode, requestedVersion))
using (var qrCode = new PngByteQRCode(qrCodeData))
- return qrCode.GetGraphic(pixelsPerModule, darkColorRgba, lightColorRgba);
+ return qrCode.GetGraphic(pixelsPerModule, darkColorRgba, lightColorRgba, drawQuietZones);
}
- public static byte[] GetQRCode(string txt, QRCodeGenerator.ECCLevel eccLevel, int size)
+ public static byte[] GetQRCode(string txt, QRCodeGenerator.ECCLevel eccLevel, int size, bool drawQuietZones = true)
{
using (var qrGen = new QRCodeGenerator())
using (var qrCode = qrGen.CreateQrCode(txt, eccLevel))
using (var qrPng = new PngByteQRCode(qrCode))
- return qrPng.GetGraphic(size);
+ return qrPng.GetGraphic(size, drawQuietZones);
}
}
}
diff --git a/QRCoderTests/Helpers/HelperFunctions.cs b/QRCoderTests/Helpers/HelperFunctions.cs
index c67fce99..c04a0322 100644
--- a/QRCoderTests/Helpers/HelperFunctions.cs
+++ b/QRCoderTests/Helpers/HelperFunctions.cs
@@ -38,11 +38,16 @@ public static string BitmapToHash(Bitmap bmp)
}
return ByteArrayToHash(imgBytes);
}
+#endif
public static string ByteArrayToHash(byte[] data)
{
+#if !NETCOREAPP1_1
var md5 = new MD5CryptoServiceProvider();
var hash = md5.ComputeHash(data);
+#else
+ var hash = new SshNet.Security.Cryptography.MD5().ComputeHash(data);
+#endif
return BitConverter.ToString(hash).Replace("-", "").ToLower();
}
@@ -50,7 +55,5 @@ public static string StringToHash(string data)
{
return ByteArrayToHash(Encoding.UTF8.GetBytes(data));
}
-#endif
-
}
}
diff --git a/QRCoderTests/PngByteQRCodeRendererTests.cs b/QRCoderTests/PngByteQRCodeRendererTests.cs
new file mode 100644
index 00000000..7be21684
--- /dev/null
+++ b/QRCoderTests/PngByteQRCodeRendererTests.cs
@@ -0,0 +1,172 @@
+using Xunit;
+using QRCoder;
+using Shouldly;
+using QRCoderTests.Helpers.XUnitExtenstions;
+using QRCoderTests.Helpers;
+#if !NETCOREAPP1_1
+using System.Drawing;
+using System.IO;
+#endif
+
+namespace QRCoderTests
+{
+ /****************************************************************************************************
+ * Note: Test cases compare the outcome visually even if it's slower than a byte-wise compare.
+ * This is necessary, because the Deflate implementation differs on the different target
+ * platforms and thus the outcome, even if visually identical, differs. Thus only a visual
+ * test method makes sense. In addition bytewise differences shouldn't be important, if the
+ * visual outcome is identical and thus the qr code is identical/scannable.
+ ****************************************************************************************************/
+ public class PngByteQRCodeRendererTests
+ {
+
+
+ [Fact]
+ [Category("QRRenderer/PngByteQRCode")]
+ public void can_render_pngbyte_qrcode_blackwhite()
+ {
+ //Create QR code
+ var gen = new QRCodeGenerator();
+ var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L);
+ var pngCodeGfx = new PngByteQRCode(data).GetGraphic(5);
+
+#if NETCOREAPP1_1
+ var result = HelperFunctions.ByteArrayToHash(pngCodeGfx);
+ result.ShouldBe("1fc35c3bea6fad47427143ce716c83b8");
+#else
+ using (var mStream = new MemoryStream(pngCodeGfx))
+ {
+ var bmp = (Bitmap)Image.FromStream(mStream);
+ var result = HelperFunctions.BitmapToHash(bmp);
+ result.ShouldBe("18b19e6037cff06ae995d8d487b0e46e");
+ }
+#endif
+ }
+
+ [Fact]
+ [Category("QRRenderer/PngByteQRCode")]
+ public void can_render_pngbyte_qrcode_color()
+ {
+ //Create QR code
+ var gen = new QRCodeGenerator();
+ var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L);
+ var pngCodeGfx = new PngByteQRCode(data).GetGraphic(5, new byte[] { 255, 0, 0 }, new byte[] { 0, 0, 255 });
+
+#if NETCOREAPP1_1
+ var result = HelperFunctions.ByteArrayToHash(pngCodeGfx);
+ result.ShouldBe("0144b1d40aa6eeb6cb07df42822ea0a7");
+#else
+ using (var mStream = new MemoryStream(pngCodeGfx))
+ {
+ var bmp = (Bitmap)Image.FromStream(mStream);
+ var result = HelperFunctions.BitmapToHash(bmp);
+ result.ShouldBe("37ae73e90b66beac317b790be3db24cc");
+ }
+#endif
+ }
+
+
+ [Fact]
+ [Category("QRRenderer/PngByteQRCode")]
+ public void can_render_pngbyte_qrcode_color_with_alpha()
+ {
+ //Create QR code
+ var gen = new QRCodeGenerator();
+ var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L);
+ var pngCodeGfx = new PngByteQRCode(data).GetGraphic(5, new byte[] { 255, 255, 255, 127 }, new byte[] { 0, 0, 255 });
+
+#if NETCOREAPP1_1
+ var result = HelperFunctions.ByteArrayToHash(pngCodeGfx);
+ result.ShouldBe("627ce564fb5e17be42e4a85e907a17b5");
+#else
+ using (var mStream = new MemoryStream(pngCodeGfx))
+ {
+ var bmp = (Bitmap)Image.FromStream(mStream);
+ var result = HelperFunctions.BitmapToHash(bmp);
+ result.ShouldBe("c56c2a9535fd8e9a92a6ac9709d21e67");
+ }
+#endif
+ }
+
+ [Fact]
+ [Category("QRRenderer/PngByteQRCode")]
+ public void can_render_pngbyte_qrcode_color_without_quietzones()
+ {
+ //Create QR code
+ var gen = new QRCodeGenerator();
+ var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L);
+ var pngCodeGfx = new PngByteQRCode(data).GetGraphic(5, new byte[] { 255, 255, 255, 127 }, new byte[] { 0, 0, 255 }, false);
+
+#if NETCOREAPP1_1
+ var result = HelperFunctions.ByteArrayToHash(pngCodeGfx);
+ result.ShouldBe("07f760b3eb54901840b094d31e299713");
+#else
+ File.WriteAllBytes(@"C:\Temp\pngbyte_35.png", pngCodeGfx);
+ using (var mStream = new MemoryStream(pngCodeGfx))
+ {
+ var bmp = (Bitmap)Image.FromStream(mStream);
+ bmp.MakeTransparent(Color.Transparent);
+ var result = HelperFunctions.BitmapToHash(bmp);
+#if NET35_OR_GREATER || NET40_OR_GREATER
+ result.ShouldBe("75be11d582575617d2490c54b69e844e");
+#else
+ result.ShouldBe("fbbc8255ebf3e4f4a1d21f0dd15f76f8");
+#endif
+ }
+#endif
+ }
+
+ [Fact]
+ [Category("QRRenderer/PngByteQRCode")]
+ public void can_instantate_pngbyte_qrcode_parameterless()
+ {
+ var pngCode = new PngByteQRCode();
+ pngCode.ShouldNotBeNull();
+ pngCode.ShouldBeOfType();
+ }
+
+ [Fact]
+ [Category("QRRenderer/PngByteQRCode")]
+ public void can_render_pngbyte_qrcode_from_helper()
+ {
+ //Create QR code
+ var pngCodeGfx = PngByteQRCodeHelper.GetQRCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.L, 10);
+
+#if NETCOREAPP1_1
+ var result = HelperFunctions.ByteArrayToHash(pngCodeGfx);
+ result.ShouldBe("c562388f4f3cf13a299b469a3e3b852f");
+#else
+ using (var mStream = new MemoryStream(pngCodeGfx))
+ {
+ var bmp = (Bitmap)Image.FromStream(mStream);
+ var result = HelperFunctions.BitmapToHash(bmp);
+ result.ShouldBe("1978fb11ce26acf9b6cb7490b4c44ef2");
+ }
+#endif
+ }
+
+ [Fact]
+ [Category("QRRenderer/PngByteQRCode")]
+ public void can_render_pngbyte_qrcode_from_helper_2()
+ {
+ //Create QR code
+ var pngCodeGfx = PngByteQRCodeHelper.GetQRCode("This is a quick test! 123#?", 5, new byte[] { 255, 255, 255, 127 }, new byte[] { 0, 0, 255 }, QRCodeGenerator.ECCLevel.L);
+
+#if NETCOREAPP1_1
+ var result = HelperFunctions.ByteArrayToHash(pngCodeGfx);
+ result.ShouldBe("627ce564fb5e17be42e4a85e907a17b5");
+#else
+ using (var mStream = new MemoryStream(pngCodeGfx))
+ {
+ var bmp = (Bitmap)Image.FromStream(mStream);
+ var result = HelperFunctions.BitmapToHash(bmp);
+ result.ShouldBe("c56c2a9535fd8e9a92a6ac9709d21e67");
+ }
+#endif
+ }
+
+ }
+}
+
+
+
diff --git a/QRCoderTests/QRCoderTests.csproj b/QRCoderTests/QRCoderTests.csproj
index b14a2471..ce23de9d 100644
--- a/QRCoderTests/QRCoderTests.csproj
+++ b/QRCoderTests/QRCoderTests.csproj
@@ -36,6 +36,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+