QR-code rendering: BGE.QrCode - #174
Merged
Merged
Conversation
- Ports Project Nayuki's QR Code generator (via the paramount-engineering BrightScript port, both MIT) to plain BrighterScript classes/functions under src/source/utils/qrcode/ - versions 1-40, all 4 ECC levels, no SceneGraph. Attribution in LICENSE-THIRD-PARTY.md. - Public entry point: BGE.QrCode.draw(renderer, x, y, code, width, darkRgba=Black, lightRgba=White) as boolean - encodes and rasterizes in one call, matching Renderer.draw*'s bounds-check/draw-call-count conventions. - BGE.QrCode.qrEncodeText/qrEncodeBinary/qrEncodeSegments expose the encoder directly (returns a BGE.QrCode.QrCode with getModule(x,y)) for anyone who wants to draw a code some other way. - Not a Renderer.drawQRCode() method: a confirmed toolchain limitation in this project's exact brighterscript/bslint/rooibos-roku version combo means a pre-existing file can't reference a symbol from a brand-new file (reproduced on a fresh clone; bisected away namespace/class-name collision and custom-type coupling as causes - see project dev notes). Renderer.bs only gained two trivial, itself-unaffected additions (getDraw2d(), incrementDrawCalls()) so BGE.QrCode.draw() can call into it - the reverse direction, which works fine. - examples/controller's MainRoom now draws the connection URL as a QR code (onDrawEnd) alongside the existing text. New docs/qr-codes.md guide; docs/controller-input.md links to it. - Verified correctness independently of the engine: extracted the actual module grid via a throwaway test, reconstructed a clean PNG, and decoded it with zbarimg - exact round-trip match on the encoded URL. Confirmed rendering on real hardware via rokubot (screenshot in docs/images/); the on-device JPEG screenshot itself is too compressed for a scanner to read at that size, unrelated to the encoder. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…e off-by-one, uncaught throw, per-frame re-encoding - qrSpliceArray had two off-by-one bugs (inherited from the upstream reference port) comparing against arr.Count()-1 where arr.Count() was needed: the clamp/negative-index handling, and the relocate-loop condition that stopped one element short of the end. The latter silently misordered getAlignmentPatternPositions() for QR version 7+ (the first version needing 2+ insertions), placing an alignment pattern on top of the bottom-left finder pattern instead of its correct position - a real, confirmed correctness bug (regression test added, verified failing before the fix). - qrSliceArray had the same class of off-by-one resolving a negative start/finish (dormant - no current caller passes one, but it's public documented API). - BGE.QrCode.draw() now catches the uncaught throw qrEncodeSegments() raises when text doesn't fit any QR version, returning false instead of crashing the render loop - matching every other failure path in draw() already returning false rather than throwing. - examples/controller no longer re-encodes the (unchanging) connection URL every frame - encodes once in onCreate, caches the QrCode object, and only re-runs the per-module blit (qrDrawModules) in onDrawEnd. Also derives the info label's position from the QR width instead of an unrelated magic number. - Added missing doc comments on newQrMode/qrFloor/qrCeil/qrMin/qrMax. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Already credited in LICENSE-THIRD-PARTY.md and QrConstants.bs's file header; adding it to docs/qr-codes.md too since that's the page a consumer actually reads. Also fixes the guide's 'drawing one every frame' sample to show the cache-once pattern (encode in onCreate, re-blit via qrDrawModules per frame) instead of the naive re-encode that examples/controller no longer does either. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
# Conflicts: # examples/controller/src/source/Rooms/MainRoom.bs
The test suite has grown enough (860+ specs, after merging the controller-protocol-v2 work and adding this PR's QR-code suite) that a CI runner can take noticeably longer than a local dev machine to finish - confirmed by two consecutive CI failures that each hung at a different point in the run (varying by normal runner-speed jitter, not a deterministic hang), both comfortably under the new budget locally. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #168.
What
BGE.QrCode- a plain-BrighterScript QR code generator/renderer, no SceneGraph. Ports Project Nayuki's QR Code generator via the paramount-engineering BrightScript port (both MIT, attribution inLICENSE-THIRD-PARTY.md) into real BrighterScript classes/functions.One call: encodes
codeand rasterizes it ontorenderer's draw surface as awidthxwidthsquare, black-on-white by default (darkRgba/lightRgbaoverride).qrEncodeText/qrEncodeBinary/qrEncodeSegmentsexpose the encoder directly (BGE.QrCode.QrCode.getModule(x, y)) if you want to draw a code some other way.Screenshot (real hardware, via rokubot)
examples/controller'sMainRoomnow draws the connection URL as a QR code (onDrawEnd) alongside the existing text label.Why not
Renderer.drawQRCode()Ran into and confirmed (bisected on a fresh clone) a real toolchain limitation: in this project's exact
brighterscript/@rokucommunity/bslint/rooibos-rokuversion combo, a pre-existing file can't reference a symbol from a brand-new file - tested and ruled out namespace/class name collision and custom-class-type coupling as the cause per teammate suggestions, it's specifically the existing→new direction, in general. The reverse (new file calling an existing class's public method) works fine, soBGE.QrCode.draw()takes theRendererinstance and calls two trivial new (same-file-only) public methods on it -getDraw2d()/incrementDrawCalls()- rather thanRenderergaining adrawQRCode()that reaches into the new files.Correctness verification
Beyond the 15 new Rooibos tests (structural invariants - version growth, determinism, forced mask, ECC boosting, finder-pattern placement, draw-call counting): extracted the actual module grid via a throwaway test print, reconstructed a clean PNG from it, and decoded it with
zbarimg- exact round-trip match on the encoded URL. The on-device screenshot above is too JPEG-compressed for a scanner to read at that size (confirmed unrelated to the encoder itself).Testing
npm run check: 850 passed, lint clean.examples/controllervalidates clean and confirmed running on real hardware.🤖 Generated with Claude Code