#qrcode #generator #codes

fuqr

feeling unemployed qr code generator

2 releases (1 stable)

1.0.0 Oct 9, 2024
0.1.0 Sep 5, 2024

#232 in Images

MIT license

75KB
2K SLoC

fuqr

feeling unemployed qr code generator

Usage

let qr_code = generate("https://github.com/zhengkyl/fuqr", &QrOptions::new()).unwrap();

This is what QrOptions::new() looks like.

QrOptions {
    min_version: Version(1),
    strict_version: false,
    min_ecl: ECL::Low,
    strict_ecl: false,
    mode: None, // None = automatically determined
    mask: None, // None = automatically determined
}

generate() has two possible errors.

QrError::InvalidEncoding occurs if Mode::Numeric or Mode::Alphanumeric is specified and the input string contains invalid characters. None or Mode::Byte will not error.

QrError::ExceedsMaxCapacity is what it sounds like, but unless strict_version is set to true, this is very hard to trigger. The lower limit is exceeding 1273 characters with Mode::Byte and ECL::High. See capacity table for specifics.

QArt Codes

Based on Russ Cox's QArt codes with one improvement. The decoded message doesn't contain extra garbage data, because only the padding bits are manipulated. I first saw this improvement from https://github.com/xyzzy/qrpicture (actual code not available).

let version = Version::new(13);
let qr_width = version.0 * 4 + 17;
let pixel_weights = vec![WeightPixel::new(false, 0); qr_width * qr_width];
let qr_code = generate_qart(
    "https://github.com/zhengkyl/fuqr",
    &QrOptions::new().min_version(version),
    &pixel_weights
).unwrap();

generate_qart() has the same errors as generate() along with QartError::InvalidPixelWeights if the size of pixel_weights doesn't match the size of the QR code matrix.

Advanced Usage

// This returns None if input string exceeds max capacity
let data = Data::new(
    "https://github.com/zhengkyl/fuqr",
    Mode::Byte,
    Version(1), // minimum Version
    ECL::Low, // minimum ECL
).unwrap();
// OR
let data = Data::new_verbose(
    "https://github.com/zhengkyl/fuqr",
    Mode::Byte,
    Version(1),
    true, // strict Version
    ECL::Low,
    true, // strict ECL
).unwrap();

// Pass None to determine and use "best" mask
let qr_code = QrCode::new(data, Some(Mask::M1));

The encoding Mode must be specified and no errors are thrown if it's invalid. This is fine because it's probably always Mode::Byte.

The strict arguments force Version and ECL to not upgrade. There is no real usecase for this.

// data from above
let mask = Mask::M0;
let bit_info = BitInfo::new(data.mode, data.version, data.ecl, mask);

BitInfo is like QrCode, but it stores the role of each bit/pixel for the specified Mode, Version, ECL, Mask combination. Specifically for data pixels, it tracks whether it is a data, error correction, or remainder bit, as well as its error correction block and index within said block.

NOTE

  • MASK SCORING IS (probably) NOT IMPLEMENTED CORRECTLY
    • I haven't bothered fixing the code, because it's annoying and probably pointless.

Examples

All example code is WIP and in a very unpolished state.

/examples/bad_apple.rs

Animated QArt codes.

Naive (low scannability) Patterns + Low FPS
# place video at ./examples/bad_apple/bad_apple.mp4
# i'm using the 3:40 one

# generates frames in ./examples/bad_apple/frames
cargo run --example bad_apple

# combines frames into mp4
ffmpeg -framerate 5 -pattern_type glob -i 'examples/bad_apple/frames/*.png' -c:v libx264 -pix_fmt yuv420p -vf "scale=iw*10:ih*10:flags=neighbor" frames.mp4

# combines frames with audio
ffmpeg -i frames.mp4 -i examples/bad_apple/bad_apple.mp4 -map 0:v:0 -map 1:a:0 -c:v copy -c:a aac -shortest out.mp4

/examples/scale.rs

Scaling modules based on position.

Circle Stripes Waves
circle stripes waves

/examples/weave.rs

No need to stick to a boring pixel grid.

Thick Thin Diagonal
thick thin diagonal

/examples/layers.rs

Layering is neat, but it can seriously degrade scanning ability if done without care.

See Halftone QR Codes, Micrography QR Codes, Amazing QR for more thoughtful implementations with high scannability.

Background Minimalist Improved scannability
background thin diagonal

Misc bugs and experiments

Have Some More
bathroom diamonds mmm
mountains diamonds

Other

Benchmarks

My benchmarks seem to vary ~30% from run to run. My takeaway is that fuqr is kinda slow, but this is probably not the bottleneck.

Test Implementation Time Compared to fast_qr
V03H fuqr 71.225 - 73.230 µs ~1.01x slower
qrcode 541.65 - 569.61 µs ~7.8x slower
fast_qr 70.581 - 72.627 µs 1.0 (Fastest)
V10H fuqr 365.81 - 372.89 µs ~1.4x slower
qrcode 2.2897 - 2.3480 ms ~8.7x slower
fast_qr 262.86 - 270.61 µs 1.0 (Fastest)
V40H fuqr 3.0942 - 3.1684 ms ~1.3x slower
qrcode 21.502 - 21.952 ms ~8.8x slower
fast_qr 2.4293 - 2.4919 ms 1.0 (Fastest)

Dependencies

~0–1.2MB
~18K SLoC