diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ include(FeatureSummary) set(QT_MIN_VERSION "5.11.0") -set(KF5_MIN_VERSION "5.66.0") +set(KF5_MIN_VERSION "5.71.0") find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS DBus Network Widgets Svg) find_package(Qt5Test ${QT_MIN_VERSION} CONFIG QUIET) diff --git a/thumbnail/djvucreator.h b/thumbnail/djvucreator.h --- a/thumbnail/djvucreator.h +++ b/thumbnail/djvucreator.h @@ -23,11 +23,11 @@ #include -class DjVuCreator : public ThumbCreator +class DjVuCreator : public ThumbCreatorV3 { public: DjVuCreator() {} - bool create(const QString &path, int, int, QImage &img) override; + bool createV3(const QString &path, int, int, QImage &img, qreal devicePixelRatio) override; Flags flags() const override; }; diff --git a/thumbnail/djvucreator.cpp b/thumbnail/djvucreator.cpp --- a/thumbnail/djvucreator.cpp +++ b/thumbnail/djvucreator.cpp @@ -47,7 +47,7 @@ } } -bool DjVuCreator::create(const QString &path, int width, int height, QImage &img) +bool DjVuCreator::createV3(const QString &path, int width, int height, QImage &img, qreal devicePixelRatio) { int output[2]; QByteArray data(1024, 'k'); @@ -58,7 +58,7 @@ const char* argv[8]; QByteArray sizearg, fnamearg; - sizearg = QByteArray::number(width) + 'x' + QByteArray::number(height); + sizearg = QByteArray::number(width * devicePixelRatio) + 'x' + QByteArray::number(height * devicePixelRatio); fnamearg = QFile::encodeName( path ); argv[0] = "ddjvu"; argv[1] = "-page"; @@ -121,6 +121,17 @@ close(output[0]); int l = img.loadFromData( data ); + if (!img.isNull()) { + + const int maxWidth = static_cast(width * devicePixelRatio); + const int maxHeight = static_cast(height * devicePixelRatio); + + const QSize imageSize = img.size(); + if (imageSize.isValid() && (imageSize.width() > maxWidth || imageSize.height() > maxHeight)) { + img = img.scaled(maxWidth, maxHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation); + } + img.setDevicePixelRatio(devicePixelRatio); + } return ok && l; } diff --git a/thumbnail/imagecreator.h b/thumbnail/imagecreator.h --- a/thumbnail/imagecreator.h +++ b/thumbnail/imagecreator.h @@ -24,11 +24,11 @@ #include -class ImageCreator : public ThumbCreator +class ImageCreator : public ThumbCreatorV3 { public: ImageCreator() {} - bool create(const QString &path, int, int, QImage &img) override; + bool createV3(const QString &path, int, int, QImage &img, qreal devicePixelRatio) override; Flags flags() const override; }; diff --git a/thumbnail/imagecreator.cpp b/thumbnail/imagecreator.cpp --- a/thumbnail/imagecreator.cpp +++ b/thumbnail/imagecreator.cpp @@ -30,14 +30,27 @@ } } -bool ImageCreator::create(const QString &path, int, int, QImage &img) +bool ImageCreator::createV3(const QString &path, int width, int height, QImage &img, qreal devicePixelRatio) { // create image preview QImageReader ir(path); ir.setDecideFormatFromContent(true); + + const int maxWidth = width * devicePixelRatio; + const int maxHeight = height * devicePixelRatio; + + QSize imageSize = ir.size(); + if (imageSize.isValid() && (imageSize.width() > maxWidth || imageSize.height() > maxHeight)) { + const QSize thumbnailSize = imageSize.scaled(maxWidth, maxHeight, Qt::KeepAspectRatio); + ir.setScaledSize(thumbnailSize); // fast downscaling + } + ir.setQuality(75); // set quality so that the jpeg handler will use a high quality downscaler + img = ir.read(); if (img.isNull()) return false; + + img.setDevicePixelRatio(devicePixelRatio); if (img.depth() != 32) img = img.convertToFormat(img.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32); return true; diff --git a/thumbnail/jpegcreator.h b/thumbnail/jpegcreator.h --- a/thumbnail/jpegcreator.h +++ b/thumbnail/jpegcreator.h @@ -24,11 +24,11 @@ class QTransform; -class JpegCreator : public ThumbCreator +class JpegCreator : public ThumbCreatorV3 { public: JpegCreator(); - bool create(const QString &path, int, int, QImage &img) override; + bool createV3(const QString &path, int, int, QImage &img, qreal devicePixelRatio) override; Flags flags() const override; QWidget *createConfigurationWidget() override; void writeConfiguration(const QWidget *configurationWidget) override; diff --git a/thumbnail/jpegcreator.cpp b/thumbnail/jpegcreator.cpp --- a/thumbnail/jpegcreator.cpp +++ b/thumbnail/jpegcreator.cpp @@ -38,22 +38,29 @@ { } -bool JpegCreator::create(const QString &path, int width, int height, QImage &image) +bool JpegCreator::createV3(const QString &path, int width, int height, QImage &image, qreal devicePixelRatio) { QImageReader imageReader(path, "jpeg"); + const int maxWidth = static_cast(width * devicePixelRatio); + const int maxHeight = static_cast(height * devicePixelRatio); + const QSize imageSize = imageReader.size(); - if (imageSize.isValid() && (imageSize.width() > width || imageSize.height() > height)) { - const QSize thumbnailSize = imageSize.scaled(width, height, Qt::KeepAspectRatio); + if (imageSize.isValid() && (imageSize.width() > maxWidth || imageSize.height() > maxHeight)) { + const QSize thumbnailSize = imageSize.scaled(maxWidth, maxHeight, Qt::KeepAspectRatio); imageReader.setScaledSize(thumbnailSize); // fast downscaling } imageReader.setQuality(75); // set quality so that the jpeg handler will use a high quality downscaler JpegCreatorSettings* settings = JpegCreatorSettings::self(); settings->load(); imageReader.setAutoTransform(settings->rotate()); - return imageReader.read(&image); + bool result = imageReader.read(&image); + if (result) { + image.setDevicePixelRatio(devicePixelRatio); + } + return result; } ThumbCreator::Flags JpegCreator::flags() const diff --git a/thumbnail/kritacreator.h b/thumbnail/kritacreator.h --- a/thumbnail/kritacreator.h +++ b/thumbnail/kritacreator.h @@ -28,13 +28,13 @@ /** * The Krita thumbnail creator can create thumbnails for krita and openraster images */ -class KritaCreator : public ThumbCreator +class KritaCreator : public ThumbCreatorV3 { public: KritaCreator(); ~KritaCreator() override; - bool create(const QString &path, int width, int height, QImage &image) override; + bool createV3(const QString &path, int width, int height, QImage &image, qreal devicePixelRatio) override; Flags flags() const override; }; diff --git a/thumbnail/kritacreator.cpp b/thumbnail/kritacreator.cpp --- a/thumbnail/kritacreator.cpp +++ b/thumbnail/kritacreator.cpp @@ -43,7 +43,7 @@ { } -bool KritaCreator::create(const QString &path, int width, int height, QImage &image) +bool KritaCreator::createV3(const QString &path, int width, int height, QImage &image, qreal devicePixelRatio) { // for now just rely on the rendered data inside the file, // do not load Krita code for rendering ourselves, as that currently (2.9) @@ -71,7 +71,7 @@ const KZipFileEntry* fileZipEntry = static_cast(entry); bool thumbLoaded = thumbnail.loadFromData(fileZipEntry->data(), "PNG"); if (thumbLoaded) { - biggerSizeNeeded = (thumbnail.width() < width || thumbnail.height() < height); + biggerSizeNeeded = (thumbnail.width() < width * devicePixelRatio || thumbnail.height() < height * devicePixelRatio); } if (biggerSizeNeeded || !thumbLoaded) { @@ -82,7 +82,8 @@ fileZipEntry = static_cast(entry); thumbLoaded = thumbnail.loadFromData(fileZipEntry->data(), "PNG"); if (thumbLoaded) { - thumbnail = thumbnail.scaled(width, height, Qt::KeepAspectRatio, Qt::SmoothTransformation); + thumbnail = thumbnail.scaled(width * devicePixelRatio, height * devicePixelRatio, Qt::KeepAspectRatio, Qt::SmoothTransformation); + image.setDevicePixelRatio(devicePixelRatio); } else { return false; } @@ -93,6 +94,7 @@ // (or better checkerboard?) image = QImage(thumbnail.size(), QImage::Format_RGB32); image.fill(QColor(Qt::white).rgb()); + image.setDevicePixelRatio(thumbnail.devicePixelRatio()); QPainter p(&image); p.drawImage(QPoint(0, 0), thumbnail); diff --git a/thumbnail/svgcreator.h b/thumbnail/svgcreator.h --- a/thumbnail/svgcreator.h +++ b/thumbnail/svgcreator.h @@ -22,11 +22,11 @@ #include -class SvgCreator : public ThumbCreator +class SvgCreator : public ThumbCreatorV3 { public: SvgCreator() {} - bool create(const QString &path, int w, int h, QImage &img) override; + bool createV3(const QString &path, int w, int h, QImage &img, qreal devicePixelRatio) override; Flags flags() const override; }; diff --git a/thumbnail/svgcreator.cpp b/thumbnail/svgcreator.cpp --- a/thumbnail/svgcreator.cpp +++ b/thumbnail/svgcreator.cpp @@ -31,25 +31,35 @@ } } -bool SvgCreator::create(const QString &path, int w, int h, QImage &img) +bool SvgCreator::createV3(const QString &path, int width, int height, QImage &img, qreal devicePixelRatio) { QSvgRenderer r(path); if ( !r.isValid() ) return false; // render using the correct ratio const double ratio = static_cast(r.defaultSize().height()) / static_cast(r.defaultSize().width()); - if (w < h) - h = qRound(ratio * w); + if (width < height) + height = qRound(ratio * width); else - w = qRound(h / ratio); + width = qRound(height / ratio); - QImage i(w, h, QImage::Format_ARGB32_Premultiplied); + QImage i(width * devicePixelRatio, height * devicePixelRatio, QImage::Format_ARGB32_Premultiplied); + i.setDevicePixelRatio(devicePixelRatio); i.fill(0); QPainter p(&i); r.render(&p); img = i; + + const int maxWidth = static_cast(width * devicePixelRatio); + const int maxHeight = static_cast(height * devicePixelRatio); + + const QSize imageSize = img.size(); + if (imageSize.isValid() && (imageSize.width() > maxWidth || imageSize.height() > maxHeight)) { + img = img.scaled(maxWidth, maxHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation); + } + return true; } diff --git a/thumbnail/textcreator.h b/thumbnail/textcreator.h --- a/thumbnail/textcreator.h +++ b/thumbnail/textcreator.h @@ -24,12 +24,12 @@ #include #include -class TextCreator : public ThumbCreator +class TextCreator : public ThumbCreatorV3 { public: TextCreator(); ~TextCreator() override; - bool create(const QString &path, int width, int height, QImage &img) override; + bool createV3(const QString &path, int width, int height, QImage &img, qreal devicePixelRatio) override; Flags flags() const override; private: diff --git a/thumbnail/textcreator.cpp b/thumbnail/textcreator.cpp --- a/thumbnail/textcreator.cpp +++ b/thumbnail/textcreator.cpp @@ -69,7 +69,7 @@ #endif } -bool TextCreator::create(const QString &path, int width, int height, QImage &img) +bool TextCreator::createV3(const QString &path, int width, int height, QImage &img, qreal devicePixelRatio) { // Desktop files, .directory files, and flatpakrefs aren't traditional // text files, so their icons should be shown instead @@ -84,27 +84,29 @@ // determine some sizes... // example: width: 60, height: 64 - QSize pixmapSize( width, height ); + QSize pixmapSize( width * devicePixelRatio, height * devicePixelRatio ); if (height * 3 > width * 4) - pixmapSize.setHeight( width * 4 / 3 ); + pixmapSize.setHeight( width * 4 / 3 * devicePixelRatio); else - pixmapSize.setWidth( height * 3 / 4 ); + pixmapSize.setWidth( height * 3 / 4 * devicePixelRatio); - if ( pixmapSize != m_pixmap.size() ) + if ( pixmapSize != m_pixmap.size() ) { m_pixmap = QPixmap( pixmapSize ); + m_pixmap.setDevicePixelRatio(devicePixelRatio); + } // one pixel for the rectangle, the rest. whitespace - int xborder = 1 + pixmapSize.width()/16; // minimum x-border - int yborder = 1 + pixmapSize.height()/16; // minimum y-border + int xborder = 1 + pixmapSize.width()/16 / devicePixelRatio; // minimum x-border + int yborder = 1 + pixmapSize.height()/16 / devicePixelRatio; // minimum y-border // this font is supposed to look good at small sizes QFont font = QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont); - font.setPixelSize( qMax(7, qMin( 10, ( pixmapSize.height() - 2 * yborder ) / 16 ) ) ); + font.setPixelSize( qMax(7, qMin( 10, ( static_cast(pixmapSize.height() / devicePixelRatio) - 2 * yborder ) / 16 ) ) ); QFontMetrics fm( font ); // calculate a better border so that the text is centered - const QSizeF canvasSize(pixmapSize.width() - 2 * xborder, pixmapSize.height() - 2 * yborder); + const QSizeF canvasSize(pixmapSize.width() / devicePixelRatio - 2 * xborder, pixmapSize.height() / devicePixelRatio - 2 * yborder); const int numLines = (int) (canvasSize.height() / fm.height()); // assumes an average line length of <= 120 chars diff --git a/thumbnail/thumbnail.h b/thumbnail/thumbnail.h --- a/thumbnail/thumbnail.h +++ b/thumbnail/thumbnail.h @@ -60,7 +60,7 @@ /** * Scales down the image \p img in a way that it fits into the - * given maximum width and height. + * given maximum width and height, takes into account the image devicePixelRatio */ void scaleDownImage(QImage& img, int maxWidth, int maxHeight); @@ -83,6 +83,7 @@ QSet m_propagationDirectories; QString m_thumbBasePath; qint64 m_maxFileSize; + qreal m_devicePixelRatio; }; #endif diff --git a/thumbnail/thumbnail.cpp b/thumbnail/thumbnail.cpp --- a/thumbnail/thumbnail.cpp +++ b/thumbnail/thumbnail.cpp @@ -140,6 +140,15 @@ return 0; } +bool createThumbnail(ThumbCreator *creator, const QString &path, int width, int height, QImage &img, qreal devicePixelRatio = 1.0) +{ + const auto thumbv3 = dynamic_cast(creator); + if (thumbv3) { + return thumbv3->createV3(path, width, height, img, devicePixelRatio); + } else { + return creator->create(path, width, height, img); + } +} ThumbnailProtocol::ThumbnailProtocol(const QByteArray &pool, const QByteArray &app) : SlaveBase("thumbnail", pool, app), @@ -201,6 +210,7 @@ m_width = metaData("width").toInt(); m_height = metaData("height").toInt(); int iconSize = metaData("iconSize").toInt(); + m_devicePixelRatio = metaData("devicePixelRatio").toInt(); if (m_width < 0 || m_height < 0) { error(KIO::ERR_INTERNAL, i18n("No or invalid size specified.")); @@ -263,14 +273,15 @@ if(sequenceCreator) sequenceCreator->setSequenceIndex(sequenceIndex()); - if (!creator->create(url.path(), m_width, m_height, img)) { + if (!createThumbnail(creator, url.path(), m_width, m_height, img, m_devicePixelRatio)) { error(KIO::ERR_INTERNAL, i18n("Cannot create thumbnail for %1", url.path())); return; } flags = creator->flags(); } } + qreal imgDpr = img.devicePixelRatioF(); scaleDownImage(img, m_width, m_height); if (flags & ThumbCreator::DrawFrame) { @@ -342,21 +353,18 @@ error(KIO::ERR_INTERNAL, i18n("Failed to attach to shared memory segment %1", shmid)); return; } - if (img.width() * img.height() > m_width * m_height) { + if (img.width() / m_devicePixelRatio * img.height() / m_devicePixelRatio > m_width * m_height) { error(KIO::ERR_INTERNAL, i18n("Image is too big for the shared memory segment")); shmdt((char*)shmaddr); return; } if( img.format() != QImage::Format_ARGB32 ) { // KIO::PreviewJob and this code below completely ignores colortable :-/, img = img.convertToFormat(QImage::Format_ARGB32); // so make sure there is none } - // Keep in sync with kdelibs/kio/kio/previewjob.cpp - stream << img.width() << img.height() << quint8(img.format()); -#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) + // Keep in sync with kio/src/previewjob.cpp + quint8 format = img.format() | 0x80; + stream << img.width() << img.height() << format << imgDpr; memcpy(shmaddr, img.bits(), img.sizeInBytes()); -#else - memcpy(shmaddr, img.bits(), img.byteCount()); -#endif shmdt((char*)shmaddr); mimeType("application/octet-stream"); data(imgData); @@ -409,25 +417,23 @@ // Scale the image down so it matches the aspect ratio float scaling = 1.0; - if ((image.size().width() > imageTargetSize.width()) && (imageTargetSize.width() != 0)) { - scaling = float(imageTargetSize.width()) / float(image.size().width()); + if ((image.width() > imageTargetSize.width() * image.devicePixelRatio()) && (imageTargetSize.width() != 0)) { + scaling = float(imageTargetSize.width()) / float(image.width()); } - QImage frame(imageTargetSize + QSize(frameWidth * 2, frameWidth * 2), - QImage::Format_ARGB32); - frame.fill(0); - - float scaledFrameWidth = frameWidth / scaling; + float scaledFrameWidth = frameWidth / scaling / image.devicePixelRatio(); QTransform m; m.rotate(qrand() % 17 - 8); // Random rotation ±8° m.scale(scaling, scaling); - QRectF frameRect(QPointF(0, 0), QPointF(image.width() + scaledFrameWidth*2, image.height() + scaledFrameWidth*2)); + QRectF frameRect(QPointF(0, 0), QPointF(image.width() / image.devicePixelRatio() + scaledFrameWidth * 2, + image.height() / image.devicePixelRatio() + scaledFrameWidth * 2)); QRect r = m.mapRect(QRectF(frameRect)).toAlignedRect(); - QImage transformed(r.size(), QImage::Format_ARGB32); + QImage transformed(r.size() * image.devicePixelRatio(), QImage::Format_ARGB32); + transformed.setDevicePixelRatio(image.devicePixelRatio()); transformed.fill(0); QPainter p(&transformed); p.setRenderHint(QPainter::SmoothPixmapTransform); @@ -484,12 +490,13 @@ QString localFile = directory.path(); KFileItem item(QUrl::fromLocalFile(localFile)); - const int extent = qMin(m_width, m_height); + const int extent = qMin(m_width * m_devicePixelRatio, m_height * m_devicePixelRatio); QPixmap folder = QIcon::fromTheme(item.iconName()).pixmap(extent); + folder.setDevicePixelRatio(m_devicePixelRatio); // Scale up base icon to ensure overlays are rendered with // the best quality possible even for low-res custom folder icons - if (qMax(folder.width(), folder.height()) < extent) { + if (qMax(folder.width() * m_devicePixelRatio, folder.height() * m_devicePixelRatio) < extent) { folder = folder.scaled(extent, extent, Qt::KeepAspectRatio, Qt::SmoothTransformation); } @@ -501,7 +508,7 @@ const int leftMargin = folderWidth / 13; const int rightMargin = leftMargin; - const int segmentWidth = (folderWidth - leftMargin - rightMargin + spacing) / tiles - spacing; + const int segmentWidth = (folderWidth - leftMargin - rightMargin + spacing) / tiles - spacing; const int segmentHeight = (folderHeight - topMargin - bottomMargin + spacing) / tiles - spacing; if ((segmentWidth < 5) || (segmentHeight < 5)) { // the segment size is too small for a useful preview @@ -512,6 +519,7 @@ int skipValidItems = ((int)sequenceIndex()) * tiles * tiles; img = QImage(QSize(folderWidth, folderHeight), QImage::Format_ARGB32); + img.setDevicePixelRatio(m_devicePixelRatio); img.fill(0); QPainter p; @@ -559,7 +567,8 @@ continue; } - if (!drawSubThumbnail(p, dir.filePath(), segmentWidth, segmentHeight, xPos, yPos, frameWidth)) { + if (!drawSubThumbnail(p, dir.filePath(), segmentWidth, segmentHeight + , xPos / m_devicePixelRatio, yPos / m_devicePixelRatio, frameWidth)) { continue; } @@ -621,6 +630,7 @@ // If only for one file a thumbnail could be generated then paint an image with only one tile if (validThumbnails == 1) { QImage oneTileImg(folder.size(), QImage::Format_ARGB32); + oneTileImg.setDevicePixelRatio(m_devicePixelRatio); oneTileImg.fill(0); QPainter oneTilePainter(&oneTileImg); @@ -631,7 +641,7 @@ const int oneTileWidth = folderWidth - leftMargin - rightMargin; const int oneTileHeight = folderHeight - topMargin - bottomMargin; - drawSubThumbnail(oneTilePainter, hadFirstThumbnail, oneTileWidth, oneTileHeight, leftMargin, topMargin, frameWidth); + drawSubThumbnail(oneTilePainter, hadFirstThumbnail, oneTileWidth, oneTileHeight, leftMargin / m_devicePixelRatio, topMargin / m_devicePixelRatio, frameWidth); return oneTileImg; } @@ -696,7 +706,7 @@ if ((segmentWidth <= 256) && (segmentHeight <= 256)) { // check whether a cached version of the file is available for - // 128 x 128 or 256 x 256 pixels + // 128 x 128 or 256 x 256 pixels (the saved image can have a scaling factor applied) int cacheSize = 0; QCryptographicHash md5(QCryptographicHash::Md5); md5.addData(QFile::encodeName(fileUrl.toString())); @@ -709,47 +719,62 @@ QFile::setPermissions(basePath.absoluteFilePath("normal"), QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner); basePath.mkpath("large/"); QFile::setPermissions(basePath.absoluteFilePath("large"), QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner); + if (qFuzzyCompare(m_devicePixelRatio, 2)) { + basePath.mkpath("normal@2x/"); + QFile::setPermissions(basePath.absoluteFilePath("normal@2x"), QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner); + basePath.mkpath("large@2x/"); + QFile::setPermissions(basePath.absoluteFilePath("large@2x"), QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner); + } } QDir thumbPath(m_thumbBasePath); + QString postfix = qFuzzyCompare(m_devicePixelRatio, 2) ? QStringLiteral("@2x") : QStringLiteral(""); if ((segmentWidth <= 128) && (segmentHeight <= 128)) { cacheSize = 128; - thumbPath.cd("normal"); + thumbPath.cd("normal" + postfix); } else { cacheSize = 256; - thumbPath.cd("large"); + thumbPath.cd("large" + postfix); } if (!thumbnail.load(thumbPath.absoluteFilePath(thumbName))) { // no cached version is available, a new thumbnail must be created - QSaveFile thumbnailfile(thumbPath.absoluteFilePath(thumbName)); - bool savedCorrectly = false; - if (subCreator->create(filePath, cacheSize, cacheSize, thumbnail)) { + if (createThumbnail(subCreator, filePath, cacheSize, cacheSize, thumbnail, m_devicePixelRatio)) { + // Some thumnail creator do not respect the width height parameters scaleDownImage(thumbnail, cacheSize, cacheSize); // The thumbnail has been created successfully. Store the thumbnail - // to the cache for future access. + // to the cache for future access, with the scaling factor applied + QSaveFile thumbnailfile(thumbPath.absoluteFilePath(thumbName)); if (thumbnailfile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { - savedCorrectly = thumbnail.save(&thumbnailfile, "PNG"); + if (thumbnail.save(&thumbnailfile, "PNG")) { + thumbnailfile.commit(); + } } } else { return false; } - if(savedCorrectly) - { - thumbnailfile.commit(); + } else { + if (thumbnail.width() > cacheSize || thumbnail.height() > cacheSize) { + // the cached thumbnail uses a scaling factor, let's find it + const auto maxDim = qMax(thumbnail.width(), thumbnail.height()); + const auto imgDpr = maxDim / cacheSize; + thumbnail.setDevicePixelRatio(imgDpr); } } - } else if (!subCreator->create(filePath, segmentWidth, segmentHeight, thumbnail)) { + } else if (!createThumbnail(subCreator, filePath, segmentWidth / m_devicePixelRatio, segmentHeight / m_devicePixelRatio, thumbnail, m_devicePixelRatio)) { return false; } + // Make sure the image fits in the segments + // Some thumbnail creators do not respect the width / height parameters + scaleDownImage(thumbnail, segmentWidth / m_devicePixelRatio, segmentHeight / m_devicePixelRatio); return true; } void ThumbnailProtocol::scaleDownImage(QImage& img, int maxWidth, int maxHeight) { - if (img.width() > maxWidth || img.height() > maxHeight) { - img = img.scaled(maxWidth, maxHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation); + if (img.width() / img.devicePixelRatio() > maxWidth || img.height() / img.devicePixelRatio() > maxHeight) { + img = img.scaled(maxWidth * img.devicePixelRatio(), maxHeight * img.devicePixelRatio(), Qt::KeepAspectRatio, Qt::SmoothTransformation); } } @@ -759,22 +784,20 @@ if (!createSubThumbnail(subThumbnail, filePath, width, height)) { return false; } + scaleDownImage(subThumbnail, width, height); // Seed the random number generator so that it always returns the same result // for the same directory and sequence-item qsrand(qHash(filePath)); // Apply fake smooth scaling, as seen on several blogs - if (subThumbnail.width() > width * 4 || subThumbnail.height() > height * 4) { - subThumbnail = subThumbnail.scaled(width*4, height*4, Qt::KeepAspectRatio, Qt::FastTransformation); + if (subThumbnail.width() > width * 4 * subThumbnail.devicePixelRatio() || subThumbnail.height() > height * 4 * subThumbnail.devicePixelRatio()) { + subThumbnail = subThumbnail.scaled(width * 4 * subThumbnail.devicePixelRatio(), height * 4 * subThumbnail.devicePixelRatio(), Qt::KeepAspectRatio, Qt::FastTransformation); } - QSize targetSize(subThumbnail.size()); - targetSize.scale(width, height, Qt::KeepAspectRatio); - // center the image inside the segment boundaries - const QPoint centerPos(xPos + (width/ 2), yPos + (height / 2)); - drawPictureFrame(&p, centerPos, subThumbnail, frameWidth, targetSize); + const QPoint centerPos(xPos + (width/ 2 / m_devicePixelRatio), yPos + (height / 2 / m_devicePixelRatio)); + drawPictureFrame(&p, centerPos, subThumbnail, frameWidth, QSize(width, height)); return true; }