Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion QRCoder.Core.Tests/Renderers/ArtQRCodeRendererTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
var gen = new QRCodeGenerator();
var data = gen.CreateQrCode("This is a quick test! 123#?", QRCodeGenerator.ECCLevel.H);
var finder = new SKBitmap(15, 15);
var bmp = new ArtQRCode(data).GetGraphic(10, SKColors.Black, SKColors.White, SKColors.Transparent, finderPatternImage: finder);

Check warning on line 34 in QRCoder.Core.Tests/Renderers/ArtQRCodeRendererTests.cs

View workflow job for this annotation

GitHub Actions / Build QRCoder.Core (Windows)

'ArtQRCode.GetGraphic(int, SKColor, SKColor, SKColor, SKBitmap, double, bool, ArtQRCode.QuietZoneStyle, ArtQRCode.BackgroundImageStyle, SKBitmap)' is obsolete: 'Use GetGraphic(ArtQRCodeGraphicOptions) instead.'

var result = HelperFunctions.BitmapToHash(bmp);
result.ShouldBe("442648a1087f78955773c261b45665c9");
Expand Down Expand Up @@ -60,7 +60,7 @@

var result = HelperFunctions.BitmapToHash(bmp);

result.ShouldBe("b9ecef2ee7e769d17f5e00914c7452bb");
result.ShouldBe("0ee19045007db3c29f2fc75f33906e49");
}

[Fact]
Expand Down
52 changes: 32 additions & 20 deletions QRCoder.Core/Renderers/ArtQRCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -197,11 +197,23 @@ private SKBitmap RenderGraphicCore(int pixelsPerModule, SKColor darkSKColor, SKC
if (backgroundImage != null)
{
if (backgroundImageStyle == BackgroundImageStyle.Fill)
graphics.DrawBitmap(Resize(backgroundImage, size), 0, 0);
{
using (var resizedImage = Resize(backgroundImage, size))
{
if (resizedImage != null)
graphics.DrawBitmap(resizedImage, 0, 0);
graphics.Flush();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Redundant graphics.Flush() calls inside background image rendering blocks

The graphics.Flush() calls at lines 205 and 215 are called immediately after drawing the background image, but before the module dots are rendered. There is already a graphics.Flush() at line 249 after all rendering (modules + finder patterns) is complete. The intermediate flushes are unnecessary — SKCanvas.Flush() forces pending draw operations to be submitted, but SkiaSharp doesn't require intermediate flushes between draw calls on the same canvas. These add minor overhead without benefit.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

}
}
else if (backgroundImageStyle == BackgroundImageStyle.DataAreaOnly)
{
var bgOffset = 4 - offset;
graphics.DrawBitmap(Resize(backgroundImage, size - (2 * bgOffset * pixelsPerModule)), 0 + (bgOffset * pixelsPerModule), (bgOffset * pixelsPerModule));
using (var resizedImage = Resize(backgroundImage, size - (2 * bgOffset * pixelsPerModule)))
{
if (resizedImage != null)
graphics.DrawBitmap(resizedImage, 0 + (bgOffset * pixelsPerModule), (bgOffset * pixelsPerModule));
graphics.Flush();
}
Comment on lines +201 to +216

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Old code also leaked the scaledImage bitmap — fixed by this PR

In the old Resize method, var scaledImage = new SKBitmap(scaledWidth, scaledHeight) was allocated but never disposed. The new code replaces this with image.Resize(...) wrapped in a using block at line 349, properly disposing the intermediate bitmap. Similarly, the callers in RenderGraphicCore (lines 201, 211) now wrap the Resize() return value in using blocks, fixing a resource leak where the resized bitmap was previously passed directly to DrawBitmap and never disposed.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

}
}

Expand Down Expand Up @@ -316,34 +328,34 @@ private static bool IsPartOfFinderPattern(int x, int y, int numModules, int offs
/// <summary>
/// Resize to a square bitmap, but maintain the aspect ratio by padding transparently.
/// </summary>
/// <param name="image"></param>
/// <param name="newSize"></param>
/// <returns>Resized image as bitmap</returns>
private SKBitmap Resize(SKBitmap image, int newSize)
/// <param name="image">Source image to resize.</param>
/// <param name="newSize">Target side length of the square output.</param>
/// <returns>Resized image as bitmap, or null if the source image is invalid.</returns>
private static SKBitmap Resize(SKBitmap image, int newSize)
{
if (image == null) return null;
if (image == null || image.Width == 0 || image.Height == 0 || newSize <= 0)
return null;

float scale = Math.Min((float)newSize / image.Width, (float)newSize / image.Height);
var scaledWidth = (int)(image.Width * scale);
var scaledHeight = (int)(image.Height * scale);
var scaledWidth = Math.Max(1, (int)(image.Width * scale));
var scaledHeight = Math.Max(1, (int)(image.Height * scale));
var offsetX = (newSize - scaledWidth) / 2;
var offsetY = (newSize - scaledHeight) / 2;

var scaledImage = new SKBitmap(scaledWidth, scaledHeight);

var bm = new SKBitmap(newSize, newSize);

using (var graphics = new SKCanvas(bm))
using (var scaledImage = image.Resize(new SKSizeI(scaledWidth, scaledHeight), new SKSamplingOptions(SKFilterMode.Linear)))
{
using (var brush = new SKPaint { Color = SKColors.Transparent, })
{
graphics.DrawRect(new SKRect(0, 0, newSize, newSize), brush);
brush.IsAntialias = true;
if (scaledImage == null)
return null;

graphics.DrawBitmap(scaledImage, new SKRect(offsetX, offsetY, offsetX + scaledWidth, offsetY + scaledHeight));
var bm = new SKBitmap(newSize, newSize);
using (var graphics = new SKCanvas(bm))
{
graphics.Clear(SKColors.Transparent);
graphics.DrawBitmap(scaledImage, offsetX, offsetY);
graphics.Flush();
}
return bm;
}
return bm;
}

/// <summary>
Expand Down
Loading