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 }