How EFQRCode Embeds Images Within QR Codes: SVG Composition and Masking Techniques

EFQRCode embeds images by converting them to base64-encoded PNGs, injecting them as SVG <image> elements, and optionally applying SVG masks to preserve QR code scannability when transparent pixels are allowed.

EFQRCode is a Swift library for generating customizable QR codes. Unlike simple overlay approaches that can obstruct critical QR patterns, it treats embedded images as first-class SVG layers composited with the QR code matrix, ensuring the final output remains valid and scannable.

Image Configuration with EFStyleImageParamsImage

The embedding process begins with the EFStyleImageParamsImage structure, which encapsulates the image data and styling options. Defined in Source/Styles/EFQRCodeStyleImage.swift (lines 96-108), this class stores the raw CGImage, a scaling mode (EFImageMode), opacity, and a transparency flag.

let img = EFStyleImageParamsImage(
    image: myCGImage,
    mode: .scaleAspectFill,
    alpha: 0.9,
    allowTransparent: false
)

The allowTransparent parameter is critical: when set to true, the library will generate an SVG mask to ensure that transparent portions of the embedded image do not obscure the underlying QR code modules.

Rasterization and Base64 Encoding

Before SVG injection, the image must be clipped to the QR code canvas dimensions and converted to a format suitable for embedding. This occurs in EFStyleParamImage.write within Source/Styles/EFQRCodeStyle.swift (lines 63-71).

The method first applies the selected EFImageMode to scale and clip the image to the canvas ratio:

let imageCliped = try mode.imageForContent(
    ofImage: image,
    inCanvasOfRatio: rect.size
)

Then, the clipped image is converted to PNG data and base64-encoded to create a data URL for the SVG <image> element:

let pngBase64 = try imageCliped.pngBase64EncodedString()
return "<image ... xlink:href=\"\(pngBase64)\" .../>"

This approach ensures the final QR code is self-contained, requiring no external image references.

SVG Composition and Masking Logic

The final composition occurs in EFQRCodeStyleImage.writeQRCode (Source/Styles/EFQRCodeStyleImage.swift, lines 32-60 and 82-88). This method orchestrates the rendering order and applies masks when necessary.

Rendering Order Based on Transparency

When allowTransparent is true, the library renders the QR code modules first (excluding alignment, timing, and position patterns), then injects the image fragment. It wraps the image in an SVG mask that punches holes through the QR code modules where the image has transparent pixels, ensuring those modules remain visible for scanning.

if let image = params.image {
    // ... draw modules when allowTransparent is true ...
    let line = try image.image.write(
        id: id,
        rect: CGRect(x: 0, y: 0, width: nCount, height: nCount),
        opacity: image.alpha,
        mode: image.mode
    )
    imageLineIndex = pointList.count
    pointList.append(line)  // image SVG fragment
    id += 1
}

Mask Application

If transparency is enabled, the code constructs a mask around the image fragment (lines 82-88):

if let imageLineIndex = imageLineIndex {
    imageMask += "</mask></defs>"
    let oldImageLine = pointList[imageLineIndex]
    pointList[imageLineIndex] =
        "\(imageMask)<g x=\"0\" y=\"0\" width=\"\(nCount)\" height=\"\(nCount)\" mask=\"url(#hole)\">\(oldImageLine)</g>"
}

When allowTransparent is false, the QR code modules are drawn first and the image is simply drawn on top without masking, covering the underlying modules completely.

Practical Implementation Examples

Static Image Watermark

This example demonstrates embedding a static logo into a QR code using the EFStyleImageParamsImage configuration:

import EFQRCode
import CoreGraphics

// Load a CGImage from a PNG file
let logoCG: CGImage = try CGImageSourceCreateWithURL(
    URL(fileURLWithPath: "/path/to/logo.png") as CFURL,
    nil)!.createImage(at: 0)!

// Configure image parameters with scaleAspectFill and no transparency
let imageParams = EFStyleImageParamsImage(
    image: EFStyleParamImage.static(image: logoCG),
    mode: .scaleAspectFill,
    alpha: 0.9,
    allowTransparent: false)

// Build the complete style
let style = EFQRCodeStyle.image(
    EFStyleImageParams(
        icon: nil,
        backdrop: EFStyleParamBackdrop(),
        align: EFStyleImageParamsAlign(),
        timing: EFStyleImageParamsTiming(),
        position: EFStyleImageParamsPosition(),
        data: EFStyleImageParamsData(),
        image: imageParams))

// Generate the QR code
let generator = try EFQRCode.Generator("https://example.com", style: style)
let pngData = try generator.toPNGData(width: 500)

// Save to disk
try pngData.write(to: URL(fileURLWithPath: "/tmp/qrcode.png"))

Animated Watermark with Transparency

This advanced example embeds an animated GIF as a watermark while preserving QR code scannability through transparent pixel masking:

import EFQRCode
import CoreGraphics

// Assume frames and delays are loaded from an animated source
let frames: [CGImage] = [...]  // Array of CGImage frames
let delays: [CGFloat] = [...]  // Frame durations in seconds

// Create an animated image parameter
let animatedWatermark = EFStyleParamImage.animated(
    images: frames,
    imageDelays: delays)

// Configure with transparency enabled to preserve QR readability
let imgParams = EFStyleImageParamsImage(
    image: animatedWatermark,
    mode: .scaleAspectFit,
    alpha: 1.0,
    allowTransparent: true)

// Assemble the style
let style = EFQRCodeStyle.image(
    EFStyleImageParams(
        icon: nil,
        backdrop: EFStyleParamBackdrop(),
        align: EFStyleImageParamsAlign(),
        timing: EFStyleImageParamsTiming(),
        position: EFStyleImageParamsPosition(),
        data: EFStyleImageParamsData(),
        image: imgParams))

// Generate animated GIF output
let generator = try EFQRCode.Generator("Animated QR", style: style)
let gifData = try generator.toGIFData(width: 400)

// Save the result
try gifData.write(to: URL(fileURLWithPath: "/tmp/animated_qr.gif"))

When allowTransparent is set to true, the library ensures that transparent portions of the animated frames do not obscure the underlying QR code modules, maintaining scannability throughout the animation sequence.

Summary

  • EFQRCode treats embedded images as SVG layers rather than simple overlays, ensuring the output remains a valid, scannable QR code.
  • The EFStyleImageParamsImage structure configures the image source, scaling mode, opacity, and transparency handling (Source/Styles/EFQRCodeStyleImage.swift, lines 96-108).
  • EFStyleParamImage.write handles rasterization, clipping to canvas dimensions, and base64 PNG encoding for SVG embedding (Source/Styles/EFQRCodeStyle.swift, lines 63-71).
  • EFQRCodeStyleImage.writeQRCode orchestrates the final composition, rendering QR modules first when transparency is enabled, then injecting the image fragment with an optional SVG mask to preserve module visibility (Source/Styles/EFQRCodeStyleImage.swift, lines 32-60, 82-88).
  • The allowTransparent flag determines whether the image covers modules directly or uses masking to ensure scannability through transparent image pixels.

Frequently Asked Questions

What image formats does EFQRCode support for embedding?

EFQRCode accepts any CGImage or UIImage on iOS/macOS platforms. The library internally converts these to PNG format for embedding within the SVG output. This means you can use PNG, JPEG, or any other format that Core Graphics can decode, provided you convert it to a CGImage first. The final output is always a base64-encoded PNG embedded directly in the SVG.

How does transparent pixel handling affect QR code readability?

The allowTransparent parameter in EFStyleImageParamsImage controls this behavior. When set to false, the image simply overlays the QR code modules, potentially obscuring them. When set to true, EFQRCode generates an SVG mask that punches holes through the QR code modules where the image has transparent pixels, ensuring those modules remain visible for scanners. This preserves scannability even when using logos with transparent backgrounds.

Can I use animated GIFs as watermarks in EFQRCode?

Yes, EFQRCode supports animated watermarks through the EFStyleParamImage.animated initializer. You provide an array of CGImage frames and corresponding delay times. When generating GIF output via toGIFData, the library composites each frame of the animated watermark onto each frame of the QR code animation. To maintain scannability throughout the animation, enable allowTransparent: true so the mask updates per-frame.

Does embedding an image reduce the QR code's error correction capability?

Embedding an image does not inherently reduce the QR code's error correction level, which is determined during the encoding phase. However, covering modules with opaque image pixels effectively removes those modules from the scannable pattern. EFQRCode mitigates this by using SVG masks when allowTransparent is enabled, ensuring that even under transparent image areas, the underlying modules remain visible. For best results, use high error correction levels (H or Q) when embedding large opaque images.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →