diff --git a/QRCoder/QRCode.cs b/QRCoder/QRCode.cs index 082b086e..e5b121f8 100644 --- a/QRCoder/QRCode.cs +++ b/QRCoder/QRCode.cs @@ -53,34 +53,7 @@ public Bitmap GetGraphic(int pixelsPerModule, string darkColorHtmlHex, string li /// Indicates if quiet zones around the QR code should be drawn. /// Returns the QR code graphic as a bitmap. public Bitmap GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, bool drawQuietZones = true) - { - var size = (QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : 8)) * pixelsPerModule; - var offset = drawQuietZones ? 0 : 4 * pixelsPerModule; - - var bmp = new Bitmap(size, size); - using (var gfx = Graphics.FromImage(bmp)) - using (var lightBrush = new SolidBrush(lightColor)) - using (var darkBrush = new SolidBrush(darkColor)) - { - gfx.FillRectangle(lightBrush, new Rectangle(0, 0, bmp.Width, bmp.Height)); - for (var x = 0; x < size + offset; x += pixelsPerModule) - { - for (var y = 0; y < size + offset; y += pixelsPerModule) - { - var module = QrCodeData.ModuleMatrix[(y + pixelsPerModule) / pixelsPerModule - 1][(x + pixelsPerModule) / pixelsPerModule - 1]; - - if (module) - { - gfx.FillRectangle(darkBrush, new Rectangle(x - offset, y - offset, pixelsPerModule, pixelsPerModule)); - } - } - } - - gfx.Save(); - } - - return bmp; - } + => GetGraphic(pixelsPerModule, darkColor, lightColor, null, 0, 0, drawQuietZones, null); /// /// Creates a colored bitmap image of the QR code with an optional icon in the center. @@ -107,20 +80,24 @@ public Bitmap GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, { gfx.InterpolationMode = InterpolationMode.HighQualityBicubic; gfx.CompositingQuality = CompositingQuality.HighQuality; + gfx.CompositingMode = CompositingMode.SourceCopy; + + // Fill background with light color gfx.Clear(lightColor); + var drawIconFlag = icon != null && iconSizePercent > 0 && iconSizePercent <= 100; - for (var x = 0; x < size + offset; x += pixelsPerModule) + // Create path for all dark modules using RLE + using (var darkPath = CreatePathFromModules(pixelsPerModule, offset, drawQuietZones)) { - for (var y = 0; y < size + offset; y += pixelsPerModule) - { - var moduleBrush = QrCodeData.ModuleMatrix[(y + pixelsPerModule) / pixelsPerModule - 1][(x + pixelsPerModule) / pixelsPerModule - 1] ? darkBrush : lightBrush; - gfx.FillRectangle(moduleBrush, new Rectangle(x - offset, y - offset, pixelsPerModule, pixelsPerModule)); - } + gfx.FillPath(darkBrush, darkPath); } if (drawIconFlag) { + // Set compositing mode to SourceOver for icon rendering + gfx.CompositingMode = CompositingMode.SourceOver; + float iconDestWidth = iconSizePercent * bmp.Width / 100f; float iconDestHeight = drawIconFlag ? iconDestWidth * icon!.Height / icon.Width : 0; float iconX = (bmp.Width - iconDestWidth) / 2; @@ -143,6 +120,54 @@ public Bitmap GetGraphic(int pixelsPerModule, Color darkColor, Color lightColor, return bmp; } + /// + /// Creates a GraphicsPath containing rectangles for all dark modules in the QR code. + /// Uses Run-Length Encoding (RLE) to combine adjoining dark modules in each row into single rectangles. + /// + /// The number of pixels per module. + /// The offset to apply for quiet zones. + /// Whether quiet zones are being drawn. + /// A GraphicsPath containing all dark module rectangles. + private GraphicsPath CreatePathFromModules(int pixelsPerModule, int offset, bool drawQuietZones) + { + var path = new GraphicsPath(); + var matrix = QrCodeData.ModuleMatrix; + var size = matrix.Count; + var startIdx = drawQuietZones ? 0 : 4; + var endIdx = drawQuietZones ? size : size - 4; + + for (int y = startIdx; y < endIdx; y++) + { + int x = startIdx; + while (x < endIdx) + { + // Skip light modules + if (!matrix[y][x]) + { + x++; + continue; + } + + // Found a dark module - find the run length + int startX = x; + while (x < endIdx && matrix[y][x]) + { + x++; + } + + // Create a single rectangle for the entire run of dark modules + var rectX = startX * pixelsPerModule - offset; + var rectY = y * pixelsPerModule - offset; + var rectWidth = (x - startX) * pixelsPerModule; + var rectHeight = pixelsPerModule; + + path.AddRectangle(new Rectangle(rectX, rectY, rectWidth, rectHeight)); + } + } + + return path; + } + /// /// Creates a rounded rectangle path for drawing. ///