Skip to content

Conversation

@Shane32
Copy link
Owner

@Shane32 Shane32 commented Oct 8, 2025

Continues efforts by @TimothyMakkison in #626 . This PR draws from other recent optimizations, where it creates a path and then fills the path at once.

Before

Method Mean Error StdDev Allocated
RenderQRCodeSmall 233.8 us 1.78 us 1.58 us 312 B
RenderQRCodeMedium 689.7 us 7.62 us 7.13 us 313 B
RenderQRCodeBig 10,423.5 us 91.34 us 80.97 us 329 B
RenderQRCodeHuge 128,431.5 us 1,670.22 us 1,562.33 us 580 B

After

Method Mean Error StdDev Allocated
RenderQRCodeSmall 175.0 us 0.93 us 0.82 us 336 B
RenderQRCodeMedium 506.0 us 1.69 us 1.32 us 336 B
RenderQRCodeBig 7,148.9 us 77.72 us 72.70 us 347 B
RenderQRCodeHuge 118,753.4 us 1,109.70 us 1,038.02 us 493 B

Summary by CodeRabbit

  • Bug Fixes

    • Ensures background is filled before drawing, preventing transparency artifacts.
    • Corrects icon compositing for more consistent overlay on the QR code.
    • Reduces visual artifacts by adjusting render order and blending.
  • Refactor

    • Optimized QR code rendering by batching dark module drawing, improving performance and smoothing output.
    • Separated module drawing from icon rendering for clearer rendering flow and reliability.

@Shane32 Shane32 added this to the 1.7.0 milestone Oct 8, 2025
@Shane32 Shane32 requested a review from gfoidl October 8, 2025 17:37
@Shane32 Shane32 self-assigned this Oct 8, 2025
@Shane32 Shane32 added the performance Performance related enhancements or benchmarks label Oct 8, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 8, 2025

📝 Walkthrough

Walkthrough

Refactors QR code rendering in QRCoder/QRCode.cs to build and fill a single GraphicsPath of dark modules using row-wise run-length encoding, adds explicit background fill and compositing mode switches, and defers icon drawing until after module rendering. Introduces CreatePathFromModules helper for path construction with quiet-zone offsets.

Changes

Cohort / File(s) Summary of Changes
Bitmap rendering pipeline refactor
QRCoder/QRCode.cs
Replaces per-module rectangle fills with RLE-built GraphicsPath filled once; adds CreatePathFromModules; performs background fill first; switches CompositingMode between SourceCopy and SourceOver; draws optional icon after modules with adjusted compositing; updates comments/structure accordingly.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant QR as QRCode.Render
  participant G as Graphics
  participant P as GraphicsPath
  participant I as Icon/Image (optional)

  QR->>G: Create bitmap/graphics
  QR->>G: Set CompositingMode=SourceCopy
  QR->>G: Fill background (light modules)
  QR->>QR: CreatePathFromModules(matrix, pixelsPerModule, quietZone)
  QR->>P: Build path via row-wise RLE rectangles
  QR->>G: FillPath(P) for dark modules
  alt Icon specified
    QR->>G: Set CompositingMode=SourceOver
    QR->>G: Draw icon centered
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • gfoidl

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title clearly states that the pull request focuses on optimizing the performance of the QRCoder.GetGraphic method, which aligns with the introduction of a path-based drawing approach and benchmark improvements, making it concise and directly related to the main change.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch qrcoderenderer

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 47d2c2d and 74e4cf4.

📒 Files selected for processing (1)
  • QRCoder/QRCode.cs (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
QRCoder/QRCode.cs (3)
QRCoder/SvgQRCode.cs (7)
  • GetGraphic (30-33)
  • GetGraphic (40-44)
  • GetGraphic (56-62)
  • GetGraphic (74-80)
  • GetGraphic (90-91)
  • GetGraphic (103-104)
  • GetGraphic (116-280)
QRCoder/Base64QRCode.cs (3)
  • GetGraphic (36-37)
  • GetGraphic (48-49)
  • GetGraphic (60-104)
QRCoder/PdfByteQRCode.cs (1)
  • CreatePathFromModules (180-217)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Test .NET Core 2.1
  • GitHub Check: Test .NET Framework 4.6.2
  • GitHub Check: Test .NET 5.0 Windows
  • GitHub Check: additional-tests
  • GitHub Check: Test .NET 6.0 Windows
🔇 Additional comments (5)
QRCoder/QRCode.cs (5)

56-56: LGTM!

The forwarding to the main overload with explicit default parameters is correct and maintains backward compatibility.


83-86: Compositing mode optimization with transparency trade-off.

Setting CompositingMode.SourceCopy improves performance by skipping alpha blending during module rendering. This is appropriate for typical opaque colors, but semi-transparent dark colors will render as fully opaque. Given the performance focus and that most QR codes use opaque colors, this trade-off is reasonable.


90-94: Excellent path-based rendering optimization!

Replacing individual module draws with a single path fill operation delivers the measured ~25% performance improvement. The using statement ensures proper GraphicsPath disposal even if exceptions occur.


98-100: Proper compositing mode for icon transparency.

Switching to CompositingMode.SourceOver before icon rendering ensures that transparent pixels blend correctly with the QR code beneath, maintaining the expected visual behavior.


123-169: Well-implemented RLE path construction.

The run-length encoding approach correctly:

  • Combines adjacent dark modules into single rectangles per row
  • Handles quiet zone inclusion/exclusion via startIdx/endIdx boundaries
  • Applies proper pixel offset calculations (startX * pixelsPerModule - offset)
  • Follows the same RLE pattern as SvgQRCode.cs and PdfByteQRCode.cs

The algorithm efficiently reduces the number of rectangles in the path, which directly contributes to the observed performance gains.

Based on relevant code snippets


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance Performance related enhancements or benchmarks

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants