From 2e9c94ffae2d684f293052f74b6d715d6471c695 Mon Sep 17 00:00:00 2001 From: Roman Telezhynskyi Date: Tue, 28 Sep 2021 15:19:02 +0300 Subject: [PATCH] Fix watermark size while scale sheet. Add watermark placeholder for cases when raster image of watermark will require bigger size than image cache can handle. --- src/app/puzzle/scene/vpgraphicstilegrid.cpp | 3 +- src/app/puzzle/share/resources/puzzleicon.qrc | 2 + .../puzzleicon/svg/watermark_placeholder.svg | 7 ++ .../svg/watermark_placeholder_grayscale.svg | 9 ++ src/app/puzzle/vpapplication.cpp | 3 + src/app/puzzle/vpmainwindow.cpp | 8 +- src/app/puzzle/vptilefactory.cpp | 98 +++++++++++++------ 7 files changed, 94 insertions(+), 36 deletions(-) create mode 100644 src/app/puzzle/share/resources/puzzleicon/svg/watermark_placeholder.svg create mode 100644 src/app/puzzle/share/resources/puzzleicon/svg/watermark_placeholder_grayscale.svg diff --git a/src/app/puzzle/scene/vpgraphicstilegrid.cpp b/src/app/puzzle/scene/vpgraphicstilegrid.cpp index 102951dad..56316094c 100644 --- a/src/app/puzzle/scene/vpgraphicstilegrid.cpp +++ b/src/app/puzzle/scene/vpgraphicstilegrid.cpp @@ -114,7 +114,8 @@ void VPGraphicsTileGrid::paint(QPainter *painter, const QStyleOptionGraphicsItem if (watermarkData.showImage && not watermarkData.path.isEmpty()) { VPTileFactory::PaintWatermarkImage(painter, img, watermarkData, - layout->LayoutSettings().WatermarkPath()); + layout->LayoutSettings().WatermarkPath(), + xScale, yScale); } if (watermarkData.showText && not watermarkData.text.isEmpty()) diff --git a/src/app/puzzle/share/resources/puzzleicon.qrc b/src/app/puzzle/share/resources/puzzleicon.qrc index 712fccda6..9625c1530 100644 --- a/src/app/puzzle/share/resources/puzzleicon.qrc +++ b/src/app/puzzle/share/resources/puzzleicon.qrc @@ -21,5 +21,7 @@ puzzleicon/32X32/vertical_grainline.png puzzleicon/32X32/vertical_grainline@2x.png puzzleicon/svg/no_watermark_image.svg + puzzleicon/svg/watermark_placeholder.svg + puzzleicon/svg/watermark_placeholder_grayscale.svg diff --git a/src/app/puzzle/share/resources/puzzleicon/svg/watermark_placeholder.svg b/src/app/puzzle/share/resources/puzzleicon/svg/watermark_placeholder.svg new file mode 100644 index 000000000..5300a4a45 --- /dev/null +++ b/src/app/puzzle/share/resources/puzzleicon/svg/watermark_placeholder.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/app/puzzle/share/resources/puzzleicon/svg/watermark_placeholder_grayscale.svg b/src/app/puzzle/share/resources/puzzleicon/svg/watermark_placeholder_grayscale.svg new file mode 100644 index 000000000..c360dc386 --- /dev/null +++ b/src/app/puzzle/share/resources/puzzleicon/svg/watermark_placeholder_grayscale.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/app/puzzle/vpapplication.cpp b/src/app/puzzle/vpapplication.cpp index 754ee78fa..d95807f2c 100644 --- a/src/app/puzzle/vpapplication.cpp +++ b/src/app/puzzle/vpapplication.cpp @@ -53,6 +53,7 @@ QT_WARNING_POP #include #include #include +#include //--------------------------------------------------------------------------------------------------------------------- inline void noisyFailureMsgHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) @@ -395,6 +396,8 @@ void VPApplication::InitOptions() qCDebug(pApp, "Command-line arguments: %s", qUtf8Printable(arguments().join(", "))); qCDebug(pApp, "Process ID: %s", qUtf8Printable(QString().setNum(applicationPid()))); + QPixmapCache::setCacheLimit(50 * 1024 /* 50 MB */); + LoadTranslation(QLocale().name());// By default the console version uses system locale VPCommandLine::Instance(); diff --git a/src/app/puzzle/vpmainwindow.cpp b/src/app/puzzle/vpmainwindow.cpp index fc1ce6a4d..ceea637fd 100644 --- a/src/app/puzzle/vpmainwindow.cpp +++ b/src/app/puzzle/vpmainwindow.cpp @@ -2827,16 +2827,12 @@ void VPMainWindow::DrawTilesScheme(QPrinter *printer, QPainter *painter, const V if (watermarkData.showImage && not watermarkData.path.isEmpty()) { VPTileFactory::PaintWatermarkImage(painter, target, watermarkData, - m_layout->LayoutSettings().WatermarkPath(), - m_layout->LayoutSettings().HorizontalScale(), - m_layout->LayoutSettings().VerticalScale()); + m_layout->LayoutSettings().WatermarkPath()); } if (watermarkData.showText && not watermarkData.text.isEmpty()) { - VPTileFactory::PaintWatermarkText(painter, target, watermarkData, - m_layout->LayoutSettings().HorizontalScale(), - m_layout->LayoutSettings().VerticalScale()); + VPTileFactory::PaintWatermarkText(painter, target, watermarkData); } } diff --git a/src/app/puzzle/vptilefactory.cpp b/src/app/puzzle/vptilefactory.cpp index 6093e13a7..12c3eebb3 100644 --- a/src/app/puzzle/vptilefactory.cpp +++ b/src/app/puzzle/vptilefactory.cpp @@ -35,11 +35,10 @@ auto Grayscale(QImage image) -> QImage auto WatermarkImageFromCache(const VWatermarkData &watermarkData, const QString &watermarkPath, qreal xScale, qreal yScale, QString &error) -> QPixmap { - const qreal opacity = watermarkData.opacity/100.; QPixmap pixmap; QString imagePath = AbsoluteMPath(watermarkPath, watermarkData.path); - QString imageCacheKey = QString("puzzle=path%1+opacity%2+rotation%3+grayscale%4+xscale%5+yxcale%6") - .arg(imagePath, QString::number(opacity), QString::number(watermarkData.imageRotation), + QString imageCacheKey = QString("puzzle=path%1+rotation%3+grayscale%4+xscale%5+yxcale%6") + .arg(imagePath, QString::number(watermarkData.imageRotation), watermarkData.grayscale ? trueStr : falseStr ).arg(xScale).arg(yScale); if (not QPixmapCache::find(imageCacheKey, &pixmap)) @@ -57,26 +56,17 @@ auto WatermarkImageFromCache(const VWatermarkData &watermarkData, const QString watermark = Grayscale(watermark); } - // Workaround for QGraphicsPixmapItem opacity problem. - // Opacity applied only if use a cached pixmap and only after first draw. First image always has opacity 1. - // Preparing an image manually allows to avoid the problem. - QSize scaledSize(qRound(watermark.width() * xScale), qRound(watermark.height() * yScale)); - QImage tmp(scaledSize, watermark.format()); - tmp = tmp.convertToFormat(QImage::Format_ARGB32); - tmp.fill(Qt::transparent); - - QPainter p(&tmp); - p.setOpacity(opacity); - QTransform t; - t.translate(tmp.width()/2., tmp.height()/2.); + t.scale(1 / xScale, 1 / yScale); + watermark = watermark.transformed(t); + + t = QTransform(); + t.translate(watermark.width()/2., watermark.height()/2.); t.rotate(-watermarkData.imageRotation); - t.translate(-tmp.width()/2., -tmp.height()/2.); - p.setTransform(t); + t.translate(-watermark.width()/2., -watermark.height()/2.); + watermark = watermark.transformed(t); - p.drawImage(QRectF(QPointF(), scaledSize), watermark); - - pixmap = QPixmap::fromImage(tmp); + pixmap = QPixmap::fromImage(watermark); QPixmapCache::insert(imageCacheKey, pixmap); } @@ -514,16 +504,12 @@ void VPTileFactory::DrawWatermark(QPainter *painter) if (m_watermarkData.showImage && not m_watermarkData.path.isEmpty()) { PaintWatermarkImage(painter, img, m_watermarkData, - layout->LayoutSettings().WatermarkPath(), - layout->LayoutSettings().HorizontalScale(), - layout->LayoutSettings().VerticalScale()); + layout->LayoutSettings().WatermarkPath()); } if (m_watermarkData.showText && not m_watermarkData.text.isEmpty()) { - PaintWatermarkText(painter, img, m_watermarkData, - layout->LayoutSettings().HorizontalScale(), - layout->LayoutSettings().VerticalScale()); + PaintWatermarkText(painter, img, m_watermarkData); } } } @@ -575,9 +561,9 @@ void VPTileFactory::PaintWatermarkImage(QPainter *painter, const QRectF &img, co { SCASSERT(painter != nullptr) - auto BrokenImage = [img, watermarkData, watermarkPath]() + const qreal opacity = watermarkData.opacity/100.; + auto BrokenImage = [img, watermarkData, watermarkPath, opacity]() { - const qreal opacity = watermarkData.opacity/100.; QPixmap watermark; QString imagePath = QString("puzzle=path%1+opacity%2_broken") .arg(AbsoluteMPath(watermarkPath, watermarkData.path), QString::number(opacity)); @@ -604,8 +590,57 @@ void VPTileFactory::PaintWatermarkImage(QPainter *painter, const QRectF &img, co return watermark; }; + QString imagePath = AbsoluteMPath(watermarkPath, watermarkData.path); + QFileInfo f(imagePath); + + QImageReader imageReader(imagePath); + QImage watermarkImage = imageReader.read(); + + if (watermarkImage.isNull()) + { + QPixmap watermarkPixmap = BrokenImage(); + + if (watermarkPixmap.width() < img.width() && watermarkPixmap.height() < img.height()) + { + QRect imagePosition(0, 0, watermarkPixmap.width(), watermarkPixmap.height()); + imagePosition.translate(img.center().toPoint() - imagePosition.center()); + + painter->drawPixmap(imagePosition, watermarkPixmap); + } + else + { + painter->drawPixmap(img.toRect(), watermarkPixmap); + } + return; + } + +#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) + qint64 fileSize = watermarkImage.byteCount(); +#else + qint64 fileSize = watermarkImage.sizeInBytes(); +#endif + qint64 pixelSize = fileSize / watermarkImage.height() / watermarkImage.width(); + QSize scaledSize(qRound(watermarkImage.width() / xScale), qRound(watermarkImage.height() / yScale)); + qint64 scaledImageSize = pixelSize*scaledSize.width()*scaledSize.height() / 1024; + int limit = QPixmapCache::cacheLimit(); + + if (scaledImageSize > limit && (xScale < 1 || yScale < 1)) + { + QScopedPointer svgRenderer(new QSvgRenderer()); + + painter->save(); + painter->setOpacity(opacity); + painter->restore(); + + QString grayscale = watermarkData.grayscale ? QStringLiteral("_grayscale") : QString(); + svgRenderer->load(QStringLiteral("://puzzleicon/svg/watermark_placeholder%1.svg").arg(grayscale)); + QRect imageRect(0, 0, qRound(watermarkImage.width() / xScale), qRound(watermarkImage.height() / yScale)); + imageRect.translate(img.center().toPoint() - imageRect.center()); + svgRenderer->render(painter, imageRect); + return; + } + QPixmap watermark; - QFileInfo f(watermarkData.path); if (f.suffix() == "png" || f.suffix() == "jpg" || f.suffix() == "jpeg" || f.suffix() == "bmp") { QString error; @@ -621,6 +656,9 @@ void VPTileFactory::PaintWatermarkImage(QPainter *painter, const QRectF &img, co watermark = BrokenImage(); } + painter->save(); + painter->setOpacity(watermarkData.opacity/100.); + if (watermark.width() < img.width() && watermark.height() < img.height()) { QRect imagePosition(0, 0, watermark.width(), watermark.height()); @@ -632,4 +670,6 @@ void VPTileFactory::PaintWatermarkImage(QPainter *painter, const QRectF &img, co { painter->drawPixmap(img.toRect(), watermark); } + + painter->restore(); }