File indexing completed on 2024-05-12 04:01:32

0001 /*
0002     SPDX-FileCopyrightText: 2010-2016 Sune Vuorela <sune@vuorela.dk>
0003 
0004     SPDX-License-Identifier: MIT
0005 */
0006 
0007 #include "qrcodebarcode_p.h"
0008 #include <qrencode.h>
0009 
0010 #include <memory>
0011 
0012 using namespace Prison;
0013 
0014 using QRcode_ptr = std::unique_ptr<QRcode, decltype(&QRcode_free)>;
0015 using QRinput_ptr = std::unique_ptr<QRinput, decltype(&QRinput_free)>;
0016 
0017 QRCodeBarcode::QRCodeBarcode()
0018     : AbstractBarcodePrivate(Barcode::TwoDimensions)
0019 {
0020 }
0021 QRCodeBarcode::~QRCodeBarcode() = default;
0022 
0023 static void qrEncodeString(QRcode_ptr &code, const QByteArray &data)
0024 {
0025     // try decreasing ECC levels, in case the higher levels result in overflowing the maximum content size
0026     for (auto ecc : {QR_ECLEVEL_Q, QR_ECLEVEL_M, QR_ECLEVEL_L}) {
0027         code.reset(QRcode_encodeString(data.constData(), 0, ecc, QR_MODE_8, true));
0028         if (code) {
0029             break;
0030         }
0031     }
0032 }
0033 
0034 QImage QRCodeBarcode::paintImage()
0035 {
0036     QRcode_ptr code(nullptr, &QRcode_free);
0037     QRinput_ptr input(nullptr, &QRinput_free);
0038     if (m_data.typeId() == QMetaType::QString) {
0039         const QByteArray trimmedData(m_data.toString().trimmed().toUtf8());
0040         qrEncodeString(code, trimmedData);
0041     } else {
0042         const auto b = m_data.toByteArray();
0043         const auto isReallyBinary = std::any_of(b.begin(), b.end(), [](unsigned char c) {
0044             return std::iscntrl(c) && !std::isspace(c);
0045         });
0046         // prefer encodeString whenever possible, as that selects the more efficient encoding
0047         // automatically, otherwise we end up needlessly in the binary encoding unconditionally
0048         if (isReallyBinary) {
0049             input.reset(QRinput_new());
0050             QRinput_append(input.get(), QR_MODE_8, b.size(), reinterpret_cast<const uint8_t *>(b.constData()));
0051             code.reset(QRcode_encodeInput(input.get()));
0052         } else {
0053             qrEncodeString(code, b);
0054         }
0055     }
0056 
0057     if (!code) {
0058         return QImage();
0059     }
0060     const int margin = 4;
0061     /*32 bit colors, 8 bit pr byte*/
0062     uchar *img = new uchar[4 * sizeof(char *) * (2 * margin + code->width) * (2 * margin * +code->width)];
0063     uchar *p = img;
0064     QByteArray background;
0065     background.resize(4);
0066     background[3] = qAlpha(m_background.rgba());
0067     background[2] = qRed(m_background.rgba());
0068     background[1] = qGreen(m_background.rgba());
0069     background[0] = qBlue(m_background.rgba());
0070     QByteArray foreground;
0071     foreground.resize(4);
0072     foreground[3] = qAlpha(m_foreground.rgba());
0073     foreground[2] = qRed(m_foreground.rgba());
0074     foreground[1] = qGreen(m_foreground.rgba());
0075     foreground[0] = qBlue(m_foreground.rgba());
0076     for (int row = 0; row < code->width + 2 * margin; row++) {
0077         for (int col = 0; col < code->width + 2 * margin; col++) {
0078             if (row < margin || row >= (code->width + margin) || col < margin || col >= (code->width + margin)) {
0079                 /*4 bytes for color*/
0080                 for (int i = 0; i < 4; i++) {
0081                     *p = background[i];
0082                     p++;
0083                 }
0084             } else {
0085                 int c = (row - margin) * code->width + (col - margin);
0086                 /*it is bit 1 that is the interesting bit for us from libqrencode*/
0087                 if (code->data[c] & 1) {
0088                     /*4 bytes for color*/
0089                     for (int i = 0; i < 4; i++) {
0090                         *p = foreground[i];
0091                         p++;
0092                     }
0093                 } else {
0094                     /*4 bytes for color*/
0095                     for (int i = 0; i < 4; i++) {
0096                         *p = background[i];
0097                         p++;
0098                     }
0099                 }
0100             }
0101         }
0102     }
0103 
0104     const auto result =
0105         QImage(img, code->width + 2 * margin, code->width + 2 * margin, QImage::Format_ARGB32).copy(); // deep copy as we are going to delete img
0106     delete[] img;
0107     return result;
0108 }