| 9 | #include "Logging.h" |
| 10 | |
| 11 | void QrUtils::drawQrCode(const GfxRenderer& renderer, const Rect& bounds, const std::string& textPayload) { |
| 12 | // Dynamically calculate the QR code version based on text length |
| 13 | // Version 4 holds ~114 bytes, Version 10 ~395, Version 20 ~1066, up to 40 |
| 14 | // qrcode.h max version is 40. |
| 15 | // Formula: approx version = size / 26 + 1 (very rough estimate, better to find best fit) |
| 16 | size_t len = textPayload.length(); |
| 17 | |
| 18 | // Truncate to max QR capacity at a UTF-8 safe boundary to avoid splitting multi-byte sequences |
| 19 | static constexpr size_t MAX_QR_CAPACITY = 2953; // Version 40, ECC_LOW, byte mode |
| 20 | std::string truncated; |
| 21 | const char* payload = textPayload.c_str(); |
| 22 | if (len > MAX_QR_CAPACITY) { |
| 23 | len = utf8SafeTruncateBuffer(textPayload.c_str(), static_cast<int>(MAX_QR_CAPACITY)); |
| 24 | truncated = textPayload.substr(0, len); |
| 25 | payload = truncated.c_str(); |
| 26 | } |
| 27 | |
| 28 | int version = 4; |
| 29 | if (len > 114) version = 10; |
| 30 | if (len > 395) version = 20; |
| 31 | if (len > 1066) version = 30; |
| 32 | if (len > 2110) version = 40; |
| 33 | |
| 34 | // Make sure we have a large enough buffer on the heap to avoid blowing the stack |
| 35 | uint32_t bufferSize = qrcode_getBufferSize(version); |
| 36 | auto qrcodeBytes = std::make_unique<uint8_t[]>(bufferSize); |
| 37 | |
| 38 | QRCode qrcode; |
| 39 | // Initialize the QR code. We use ECC_LOW for max capacity. |
| 40 | int8_t res = qrcode_initText(&qrcode, qrcodeBytes.get(), version, ECC_LOW, payload); |
| 41 | |
| 42 | if (res == 0) { |
| 43 | // Determine the optimal pixel size. |
| 44 | const int maxDim = std::min(bounds.width, bounds.height); |
| 45 | |
| 46 | int px = maxDim / qrcode.size; |
| 47 | if (px < 1) px = 1; |
| 48 | |
| 49 | // Calculate centering X and Y |
| 50 | const int qrDisplaySize = qrcode.size * px; |
| 51 | const int xOff = bounds.x + (bounds.width - qrDisplaySize) / 2; |
| 52 | const int yOff = bounds.y + (bounds.height - qrDisplaySize) / 2; |
| 53 | |
| 54 | // Draw the QR Code |
| 55 | for (uint8_t cy = 0; cy < qrcode.size; cy++) { |
| 56 | for (uint8_t cx = 0; cx < qrcode.size; cx++) { |
| 57 | if (qrcode_getModule(&qrcode, cx, cy)) { |
| 58 | renderer.fillRect(xOff + px * cx, yOff + px * cy, px, px, true); |
| 59 | } |
| 60 | } |
| 61 | } |
| 62 | } else { |
| 63 | // If it fails (e.g. text too large), log an error |
| 64 | LOG_ERR("QR", "Text too large for QR Code version %d", version); |
| 65 | } |
| 66 | } |
nothing calls this directly
no test coverage detected