diff --git a/autotests/aztec/encoding/aztec-complete-big.png b/autotests/aztec/encoding/aztec-complete-big.png index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@dimensions(), Prison::AbstractBarcode::TwoDimensions); } + + void testSize() + { + std::unique_ptr barcode(Prison::createBarcode(Prison::Aztec)); + QVERIFY(barcode); + QVERIFY(!barcode->minimumSize().isValid()); + barcode->setData(QStringLiteral("UNIT TEST")); + QCOMPARE(barcode->minimumSize(), QSize(60, 60)); + QCOMPARE(barcode->toImage(barcode->minimumSize()).size(), QSize(60, 60)); + QCOMPARE(barcode->toImage({1, 1}).isNull(), true); + } }; QTEST_APPLESS_MAIN(AztecBarcodeTest) diff --git a/autotests/code128barcodetest.cpp b/autotests/code128barcodetest.cpp --- a/autotests/code128barcodetest.cpp +++ b/autotests/code128barcodetest.cpp @@ -100,6 +100,17 @@ QVERIFY(barcode); QCOMPARE(barcode->dimensions(), Prison::AbstractBarcode::OneDimension); } + + void testSize() + { + std::unique_ptr barcode(Prison::createBarcode(Prison::Code128)); + QVERIFY(barcode); + QVERIFY(!barcode->minimumSize().isValid()); + barcode->setData(QStringLiteral("UNIT TEST")); + QCOMPARE(barcode->minimumSize(), QSize(154, 10)); + QCOMPARE(barcode->toImage(barcode->minimumSize()).size(), QSize(154, 10)); + QCOMPARE(barcode->toImage({1, 1}).isNull(), true); + } }; QTEST_APPLESS_MAIN(Code128BarcodeTest) diff --git a/src/lib/abstractbarcode.h b/src/lib/abstractbarcode.h --- a/src/lib/abstractbarcode.h +++ b/src/lib/abstractbarcode.h @@ -55,14 +55,12 @@ * Creates a image with a barcode on * @return QImage with a barcode on, trying to match the requested \param size * - * The image function is cached and painted on demand. - * - * if one of the dimensions of @param size is smaller than the matching dimension in \ref minimumSize, + * If one of the dimensions of @param size is smaller than the matching dimension in \ref minimumSize, * a null QImage will be returned */ QImage toImage(const QSizeF& size) ; /** - * Note! minimalSize() doesn't work as expected if this is not painting on something. + * The minimal size of this barcode. * @return the minimal size for this barcode. */ QSizeF minimumSize() const; @@ -112,9 +110,10 @@ void setMinimumSize(const QSizeF& minimumSize); /** * Doing the actual painting of the image - * @param size requested size of the miage + * @param size unused - will be removed in KF6 * @return image with barcode, or null image */ + // TODO KF6: remove the size argument virtual QImage paintImage(const QSizeF& size) = 0; private: class Private; diff --git a/src/lib/abstractbarcode.cpp b/src/lib/abstractbarcode.cpp --- a/src/lib/abstractbarcode.cpp +++ b/src/lib/abstractbarcode.cpp @@ -5,6 +5,8 @@ */ #include "abstractbarcode.h" + +#include #include #include #include @@ -16,22 +18,30 @@ class AbstractBarcode::Private { public: QString m_data; - QSizeF m_size; QImage m_cache; - QColor m_foreground; - QColor m_background; + QColor m_foreground = Qt::black; + QColor m_background = Qt::white; QSizeF m_minimum_size; AbstractBarcode::Dimensions m_dimension = AbstractBarcode::NoDimensions; AbstractBarcode* q; - bool sizeTooSmall(const QSizeF& size) { + + bool sizeTooSmall(const QSizeF& size) const { if(m_minimum_size.width() > size.width()) { return true; } else if(m_minimum_size.height() > size.height()) { return true; } return false; } - Private(AbstractBarcode* barcode) : m_foreground(Qt::black), m_background(Qt::white), m_minimum_size(10,10), q(barcode) { } + + void recompute() + { + if (m_cache.isNull() && !m_data.isEmpty()) { + m_cache = q->paintImage({}); + } + } + + Private(AbstractBarcode* barcode) : q(barcode) { } }; /** * @endcond @@ -54,35 +64,57 @@ } -QImage AbstractBarcode::toImage(const QSizeF& size) { - if(d->sizeTooSmall(size)) { - d->m_cache = QImage(); +QImage AbstractBarcode::toImage(const QSizeF& size) +{ + d->recompute(); + if (d->m_cache.isNull() || d->sizeTooSmall(size)) { return QImage(); } - if(d->m_cache.isNull() || size != d->m_size) { - d->m_size = size; - d->m_cache = paintImage(size); - return d->m_cache; + + // scale to the requested size, using only full integer factors to keep the code readable + int scaleX = std::max(1, size.width() / d->m_minimum_size.width()); + int scaleY = std::max(1, size.height() / d->m_minimum_size.height()); + if (dimensions() == TwoDimensions) { + scaleX = scaleY = std::min(scaleX, scaleY); } - return d->m_cache; + + QImage out(d->m_minimum_size.width() * scaleX, d->m_minimum_size.height() * scaleY, d->m_cache.format()); + QPainter p(&out); + p.setRenderHint(QPainter::SmoothPixmapTransform, false); + p.drawImage(out.rect(), d->m_cache, d->m_cache.rect()); + return out; } void AbstractBarcode::setData(const QString& data) { d->m_data=data; d->m_cache=QImage(); - d->m_size=QSize(); - d->m_minimum_size=QSizeF(10,10); + d->m_minimum_size = QSize(); } -QSizeF AbstractBarcode::minimumSize() const { - return d->m_minimum_size; +QSizeF AbstractBarcode::minimumSize() const +{ + d->recompute(); + + // ### backward compatibility: this is applying minimum size behavior that the specific + // implementations were doing prior to 5.69. This is eventually to be dropped. + if (!d->m_minimum_size.isValid()) { + return {}; + } + switch (d->m_dimension) { + case NoDimensions: + return {}; + case OneDimension: + return QSizeF(d->m_minimum_size.width(), std::max(d->m_minimum_size.height(), 10.0)); + case TwoDimensions: + return d->m_minimum_size * 4; + } + + return d->m_minimum_size; } -void AbstractBarcode::setMinimumSize(const QSizeF& minimumSize) { +void AbstractBarcode::setMinimumSize(const QSizeF& minimumSize) +{ d->m_minimum_size = minimumSize; - if(minimumSize.width() > d->m_size.width() || minimumSize.height() > d->m_size.height()) { - d->m_cache = QImage(); - } } const QColor& AbstractBarcode::backgroundColor() const { diff --git a/src/lib/aztecbarcode.h b/src/lib/aztecbarcode.h --- a/src/lib/aztecbarcode.h +++ b/src/lib/aztecbarcode.h @@ -34,12 +34,12 @@ void paintFullGrid(QImage *img) const; void paintFullData(QImage *img, const BitVector &data, int layerCount) const; void paintFullModeMessage(QImage *img, const BitVector &modeData) const; - QImage cropAndScaleFull(QImage *img, int layerCount, int size); + QImage cropAndScaleFull(QImage *img, int layerCount); void paintCompactGrid(QImage *img) const; void paintCompactData(QImage *img, const BitVector &data, int layerCount) const; void paintCompactModeMessage(QImage *img, const BitVector &modeData) const; - QImage cropAndScaleCompact(QImage *img, int layerCount, int size); + QImage cropAndScaleCompact(QImage *img, int layerCount); }; } diff --git a/src/lib/aztecbarcode.cpp b/src/lib/aztecbarcode.cpp --- a/src/lib/aztecbarcode.cpp +++ b/src/lib/aztecbarcode.cpp @@ -63,6 +63,7 @@ QImage AztecBarcode::paintImage(const QSizeF& size) { + Q_UNUSED(size); const auto inputData = aztecEncode(data().toLatin1()); int layerCount = 0; @@ -142,14 +143,14 @@ paintCompactGrid(&img); paintCompactData(&img, encodedData, layerCount); paintCompactModeMessage(&img, modeMsg); - return cropAndScaleCompact(&img, layerCount, std::min(size.width(), size.height())); + return cropAndScaleCompact(&img, layerCount); } else { QImage img(FullMaxSize, FullMaxSize, QImage::Format_RGB32); img.fill(backgroundColor()); paintFullGrid(&img); paintFullData(&img, encodedData, layerCount); paintFullModeMessage(&img, modeMsg); - return cropAndScaleFull(&img, layerCount, std::min(size.width(), size.height())); + return cropAndScaleFull(&img, layerCount); } } @@ -679,14 +680,13 @@ } } -QImage AztecBarcode::cropAndScaleFull(QImage *img, int layerCount, int size) +QImage AztecBarcode::cropAndScaleFull(QImage *img, int layerCount) { const auto offset = aztecFullLayerOffset[layerCount - 1]; const auto minSize = FullMaxSize - 2 * offset; - setMinimumSize(QSizeF(minSize, minSize) * 4); // *4 taken from what QR does - const int scale = std::max(1, size / minSize); + setMinimumSize(QSizeF(minSize, minSize)); - QImage out(minSize * scale, minSize * scale, img->format()); + QImage out(minSize, minSize, img->format()); QPainter p(&out); p.setRenderHint(QPainter::SmoothPixmapTransform, false); const auto srcRect = img->rect().adjusted(offset, offset, -offset, -offset); @@ -769,14 +769,13 @@ } } -QImage AztecBarcode::cropAndScaleCompact(QImage *img, int layerCount, int size) +QImage AztecBarcode::cropAndScaleCompact(QImage *img, int layerCount) { const auto offset = aztecCompactLayerOffset[layerCount - 1]; const auto minSize = CompactMaxSize - 2 * offset; - setMinimumSize(QSizeF(minSize, minSize) * 4); // *4 taken from what QR does - const int scale = std::max(1, size / minSize); + setMinimumSize(QSizeF(minSize, minSize)); - QImage out(minSize * scale, minSize * scale, img->format()); + QImage out(minSize, minSize, img->format()); QPainter p(&out); p.setRenderHint(QPainter::SmoothPixmapTransform, false); const auto srcRect = img->rect().adjusted(offset, offset, -offset, -offset); diff --git a/src/lib/code128barcode.cpp b/src/lib/code128barcode.cpp --- a/src/lib/code128barcode.cpp +++ b/src/lib/code128barcode.cpp @@ -43,23 +43,19 @@ QImage Code128Barcode::paintImage(const QSizeF& size) { - if (size.height() < 1) - return {}; + Q_UNUSED(size); const auto bits = encode(data().toLatin1()); const auto width = bits.size() + 2 * QuietZone; - setMinimumSize(QSizeF(width, 10)); + setMinimumSize(QSizeF(width, 1)); - const auto moduleSize = size.width() / width; - if (moduleSize < 1) // too small for this - return {}; - - QImage img(moduleSize * width, size.height(), QImage::Format_ARGB32); + QImage img(width, 1, QImage::Format_ARGB32); img.fill(backgroundColor()); QPainter p(&img); for (int i = 0; i < bits.size(); ++i) { - if (bits.at(i)) - p.fillRect(QRectF((QuietZone + i) * moduleSize, 0, moduleSize, img.height()), foregroundColor()); + if (bits.at(i)) { + img.setPixel(QuietZone + i, 0, foregroundColor().rgb()); + } } return img; diff --git a/src/lib/code39barcode.cpp b/src/lib/code39barcode.cpp --- a/src/lib/code39barcode.cpp +++ b/src/lib/code39barcode.cpp @@ -72,8 +72,9 @@ Code39Barcode::Code39Barcode() : AbstractBarcode(AbstractBarcode::OneDimension) {} Code39Barcode::~Code39Barcode() = default; -QImage Code39Barcode::paintImage(const QSizeF& size) { - if(size.height() == 0.0) return QImage(); +QImage Code39Barcode::paintImage(const QSizeF& size) +{ + Q_UNUSED(size); QList barcode; // convert text into sequences of wide/narrow bars { @@ -103,21 +104,14 @@ *) wide * largeWidth + narrow * smallWidth <= size.width() - the barcode has to fit within the given size */ - const int w = size.width(); const int wide = barcode.count(true); const int narrow = barcode.count(false); - // maximize wide bar width - int largeWidth = 2*w / (2*wide + narrow); - // then maximize narrow bar width - int smallWidth = (w - largeWidth*wide) / narrow; + // wide bar width + const int largeWidth = 2; + // narrow bar width + const int smallWidth = 1; // if the requested size was too small return a null image - setMinimumSize(QSize(2* wide + narrow, 10)); - if(largeWidth<2) { - return QImage(); - } - if(smallWidth<1) { - return QImage(); - } + setMinimumSize(QSize(2* wide + narrow, 1)); Q_ASSERT(largeWidth > smallWidth); // one line of the result image @@ -132,11 +126,8 @@ } // build the complete barcode - QImage ret(line.size(), size.height(), QImage::Format_ARGB32); - // just repeat the line to make the image - for(int y = 0 ; y < ret.height() ; y++) { - memcpy(ret.scanLine(y), line.data(), line.size() * sizeof(QRgb)); - } + QImage ret(line.size(), 1, QImage::Format_ARGB32); + memcpy(ret.scanLine(0), line.data(), line.size() * sizeof(QRgb)); return ret; } diff --git a/src/lib/code93barcode.cpp b/src/lib/code93barcode.cpp --- a/src/lib/code93barcode.cpp +++ b/src/lib/code93barcode.cpp @@ -229,9 +229,7 @@ Code93Barcode::~Code93Barcode() = default; QImage Code93Barcode::paintImage(const QSizeF& size) { - if(size.height() == 0.0) { - return QImage(); - } + Q_UNUSED(size); QList barcode; // convert text into sequences of fg/bg bars { @@ -260,12 +258,8 @@ barcode += true; } - // try to fill the requested size - const int barWidth = int(size.width() / barcode.size()); - setMinimumSize(QSize(barcode.size(), 10)); - if(barWidth < 1 ) { // can't go below 1 pixel - return QImage(); - } + const int barWidth = 1; + setMinimumSize(QSize(barcode.size(), 1)); // build one line of the result image QVector line; @@ -278,10 +272,7 @@ } // build the complete barcode - QImage ret(line.size(), size.height(), QImage::Format_ARGB32); - // just repeat the line to make the image - for(int y = 0 ; y < ret.height() ; y++) { - memcpy(ret.scanLine(y), line.data(), line.size() * sizeof(QRgb)); - } + QImage ret(line.size(), 1, QImage::Format_ARGB32); + memcpy(ret.scanLine(0), line.data(), line.size() * sizeof(QRgb)); return ret; } diff --git a/src/lib/datamatrixbarcode.cpp b/src/lib/datamatrixbarcode.cpp --- a/src/lib/datamatrixbarcode.cpp +++ b/src/lib/datamatrixbarcode.cpp @@ -12,16 +12,17 @@ DataMatrixBarcode::DataMatrixBarcode() : AbstractBarcode(AbstractBarcode::TwoDimensions) {} DataMatrixBarcode::~DataMatrixBarcode() = default; -QImage DataMatrixBarcode::paintImage(const QSizeF& size) { - const int width = qRound(qMin(size.width(),size.height())); - if(data().size()==0 || width == 0 || data().size() > 1200) { +QImage DataMatrixBarcode::paintImage(const QSizeF& size) +{ + Q_UNUSED(size); + if (data().size() > 1200) { return QImage(); } DmtxEncode * enc = dmtxEncodeCreate(); dmtxEncodeSetProp( enc, DmtxPropPixelPacking, DmtxPack32bppRGBX ); - dmtxEncodeSetProp( enc, DmtxPropWidth, width ); - dmtxEncodeSetProp( enc, DmtxPropHeight, width ); + dmtxEncodeSetProp( enc, DmtxPropModuleSize, 1); + dmtxEncodeSetProp( enc, DmtxPropMarginSize, 2); QByteArray trimmedData(data().trimmed().toUtf8()); DmtxPassFail result = dmtxEncodeDataMatrix(enc, trimmedData.length(), @@ -72,9 +73,6 @@ delete[] img; } } - if(!ret.isNull() && ret.width() < width) { - ret = ret.scaled(width,width); - } dmtxEncodeDestroy(&enc); return ret; } diff --git a/src/lib/qrcodebarcode.cpp b/src/lib/qrcodebarcode.cpp --- a/src/lib/qrcodebarcode.cpp +++ b/src/lib/qrcodebarcode.cpp @@ -14,10 +14,7 @@ QRCodeBarcode::~QRCodeBarcode() = default; QImage QRCodeBarcode::paintImage(const QSizeF& size) { - const int width = qRound(qMin(size.width(),size.height())); - if(data().size()==0 || width==0) { - return QImage(); - } + Q_UNUSED(size); const QByteArray trimmedData(data().trimmed().toUtf8()); QRcode* code = QRcode_encodeString8bit(trimmedData.constData(), 0, QR_ECLEVEL_Q); if(!code) { @@ -64,10 +61,10 @@ } } } - QImage tmp(img,code->width+2*margin,code->width+2*margin,QImage::Format_ARGB32); - setMinimumSize(QSizeF(tmp.width()*4,tmp.height()*4)); - QImage ret = tmp.convertToFormat(QImage::Format_ARGB32).scaled(qMax(tmp.width()*4,width),qMax(tmp.height()*4,width)); //4 is determined by trial and error. + + const auto result = QImage(img, code->width+2*margin, code->width+2*margin, QImage::Format_ARGB32).copy(); // deep copy as we are going to delete img + setMinimumSize(QSizeF(result.width(), result.height())); delete[] img; QRcode_free(code); - return ret; + return result; }