Changeset View
Standalone View
src/widgets/previewjob.cpp
Show First 20 Lines • Show All 55 Lines • ▼ Show 20 Line(s) | |||||
56 | #include <qmimedatabase.h> | 56 | #include <qmimedatabase.h> | ||
57 | #include <qstandardpaths.h> | 57 | #include <qstandardpaths.h> | ||
58 | #include <KMountPoint> | 58 | #include <KMountPoint> | ||
59 | 59 | | |||
60 | #include <algorithm> | 60 | #include <algorithm> | ||
61 | 61 | | |||
62 | #include "job_p.h" | 62 | #include "job_p.h" | ||
63 | 63 | | |||
64 | namespace { | ||||
65 | static qreal s_defaultDevicePixelRatio = 1.0; | ||||
66 | } | ||||
67 | | ||||
64 | namespace KIO | 68 | namespace KIO | ||
65 | { | 69 | { | ||
66 | struct PreviewItem; | 70 | struct PreviewItem; | ||
67 | } | 71 | } | ||
68 | using namespace KIO; | 72 | using namespace KIO; | ||
69 | 73 | | |||
70 | struct KIO::PreviewItem { | 74 | struct KIO::PreviewItem { | ||
71 | KFileItem item; | 75 | KFileItem item; | ||
▲ Show 20 Lines • Show All 73 Lines • ▼ Show 20 Line(s) | 81 | public: | |||
145 | // of extent x extent x 4 (32 bit image) on first need. | 149 | // of extent x extent x 4 (32 bit image) on first need. | ||
146 | int shmid; | 150 | int shmid; | ||
147 | // And the data area | 151 | // And the data area | ||
148 | uchar *shmaddr; | 152 | uchar *shmaddr; | ||
149 | // Root of thumbnail cache | 153 | // Root of thumbnail cache | ||
150 | QString thumbRoot; | 154 | QString thumbRoot; | ||
151 | // List of encrypted mount points for checking if we should save thumbnail | 155 | // List of encrypted mount points for checking if we should save thumbnail | ||
152 | KMountPoint::List encryptedMountsList; | 156 | KMountPoint::List encryptedMountsList; | ||
157 | qreal devicePixelRatio = s_defaultDevicePixelRatio; | ||||
153 | 158 | | |||
154 | void getOrCreateThumbnail(); | 159 | void getOrCreateThumbnail(); | ||
155 | bool statResultThumbnail(); | 160 | bool statResultThumbnail(); | ||
156 | void createThumbnail(const QString &); | 161 | void createThumbnail(const QString &); | ||
157 | void cleanupTempFile(); | 162 | void cleanupTempFile(); | ||
158 | void determineNextFile(); | 163 | void determineNextFile(); | ||
159 | void emitPreview(const QImage &thumb); | 164 | void emitPreview(const QImage &thumb); | ||
160 | 165 | | |||
161 | void startPreview(); | 166 | void startPreview(); | ||
162 | void slotThumbData(KIO::Job *, const QByteArray &); | 167 | void slotThumbData(KIO::Job *, const QByteArray &); | ||
163 | 168 | | |||
164 | Q_DECLARE_PUBLIC(PreviewJob) | 169 | Q_DECLARE_PUBLIC(PreviewJob) | ||
165 | }; | 170 | }; | ||
166 | 171 | | |||
172 | | ||||
173 | void PreviewJob::setDefaultDevicePixelRatio(qreal devicePixerRatio) { | ||||
dfaure: coding style: '{' on separate line | |||||
174 | s_defaultDevicePixelRatio = devicePixerRatio; | ||||
175 | } | ||||
176 | | ||||
167 | PreviewJob::PreviewJob(const KFileItemList &items, int width, int height, | 177 | PreviewJob::PreviewJob(const KFileItemList &items, int width, int height, | ||
168 | int iconSize, int iconAlpha, bool scale, bool save, | 178 | int iconSize, int iconAlpha, bool scale, bool save, | ||
169 | const QStringList *enabledPlugins) | 179 | const QStringList *enabledPlugins) | ||
170 | : KIO::Job(*new PreviewJobPrivate(items, QSize(width, height ? height : width))) | 180 | : KIO::Job(*new PreviewJobPrivate(items, QSize(width, height ? height : width))) | ||
171 | { | 181 | { | ||
172 | Q_D(PreviewJob); | 182 | Q_D(PreviewJob); | ||
173 | d->enabledPlugins = enabledPlugins ? *enabledPlugins : availablePlugins(); | 183 | d->enabledPlugins = enabledPlugins ? *enabledPlugins : availablePlugins(); | ||
174 | d->iconSize = iconSize; | 184 | d->iconSize = iconSize; | ||
▲ Show 20 Lines • Show All 206 Lines • ▼ Show 20 Line(s) | 280 | { | |||
381 | 391 | | |||
382 | if (bNeedCache) { | 392 | if (bNeedCache) { | ||
383 | if (width <= 128 && height <= 128) { | 393 | if (width <= 128 && height <= 128) { | ||
384 | cacheWidth = cacheHeight = 128; | 394 | cacheWidth = cacheHeight = 128; | ||
385 | } else { | 395 | } else { | ||
386 | cacheWidth = cacheHeight = 256; | 396 | cacheWidth = cacheHeight = 256; | ||
387 | } | 397 | } | ||
388 | thumbPath = thumbRoot + QLatin1String(cacheWidth == 128 ? "normal/" : "large/"); | 398 | thumbPath = thumbRoot + QLatin1String(cacheWidth == 128 ? "normal/" : "large/"); | ||
389 | if (!QDir(thumbPath).exists()) { | 399 | if (!QDir(thumbPath).exists()) { | ||
Is this @2x suffix standardized? What happens if I'm using 125% scaling, generate some previews, and then switch back to 100% (no scaling?) ngraham: Is this @2x suffix standardized? What happens if I'm using 125% scaling, generate some previews… | |||||
What about if I'm using a 250% scale factor? Maybe there should be an @3x folder too. ngraham: What about if I'm using a 250% scale factor? Maybe there should be an `@3x` folder too. | |||||
meven: or just trunate devicePixelRatio to int | |||||
390 | if (QDir().mkpath(thumbPath)) { // Qt5 TODO: mkpath(dirPath, permissions) | 400 | if (QDir().mkpath(thumbPath)) { // Qt5 TODO: mkpath(dirPath, permissions) | ||
391 | QFile f(thumbPath); | 401 | QFile f(thumbPath); | ||
392 | f.setPermissions(QFile::ReadUser | QFile::WriteUser | QFile::ExeUser); // 0700 | 402 | f.setPermissions(QFile::ReadUser | QFile::WriteUser | QFile::ExeUser); // 0700 | ||
393 | } | 403 | } | ||
394 | } | 404 | } | ||
395 | } else { | 405 | } else { | ||
396 | bSave = false; | 406 | bSave = false; | ||
397 | } | 407 | } | ||
Show All 24 Lines | 431 | { | |||
422 | d_func()->sequenceIndex = index; | 432 | d_func()->sequenceIndex = index; | ||
423 | } | 433 | } | ||
424 | 434 | | |||
425 | int KIO::PreviewJob::sequenceIndex() const | 435 | int KIO::PreviewJob::sequenceIndex() const | ||
426 | { | 436 | { | ||
427 | return d_func()->sequenceIndex; | 437 | return d_func()->sequenceIndex; | ||
428 | } | 438 | } | ||
429 | 439 | | |||
440 | void KIO::PreviewJob::setDevicePixelRatio(qreal dpr) | ||||
441 | { | ||||
442 | d_func()->devicePixelRatio = dpr; | ||||
Maybe make this static, so that apps have to do it once per app sort of like we do with Qt::AA_UseHighDpiPixmaps, rather than by KPreviewJob. meven: Maybe make this static, so that apps have to do it once per app sort of like we do with `Qt… | |||||
Not a fan. You can have different dpi per screen. broulik: Not a fan. You can have different dpi per screen.
Maybe instead we should have a `QWindow*`… | |||||
What you imply is incorrect. In X, well there is only one scale anyway. meven: > Not a fan. You can have different dpi per screen.
What you imply is incorrect.
In Wayland… | |||||
443 | } | ||||
444 | | ||||
430 | void PreviewJob::setIgnoreMaximumSize(bool ignoreSize) | 445 | void PreviewJob::setIgnoreMaximumSize(bool ignoreSize) | ||
431 | { | 446 | { | ||
432 | d_func()->ignoreMaximumSize = ignoreSize; | 447 | d_func()->ignoreMaximumSize = ignoreSize; | ||
433 | } | 448 | } | ||
434 | 449 | | |||
435 | void PreviewJobPrivate::cleanupTempFile() | 450 | void PreviewJobPrivate::cleanupTempFile() | ||
436 | { | 451 | { | ||
437 | if (!tempName.isEmpty()) { | 452 | if (!tempName.isEmpty()) { | ||
▲ Show 20 Lines • Show All 121 Lines • ▼ Show 20 Line(s) | 559 | { | |||
559 | if (!thumb.load(thumbPath + thumbName)) { | 574 | if (!thumb.load(thumbPath + thumbName)) { | ||
560 | return false; | 575 | return false; | ||
561 | } | 576 | } | ||
562 | 577 | | |||
563 | if (thumb.text(QStringLiteral("Thumb::URI")) != QString::fromUtf8(origName) || | 578 | if (thumb.text(QStringLiteral("Thumb::URI")) != QString::fromUtf8(origName) || | ||
564 | thumb.text(QStringLiteral("Thumb::MTime")).toLongLong() != tOrig.toSecsSinceEpoch()) { | 579 | thumb.text(QStringLiteral("Thumb::MTime")).toLongLong() != tOrig.toSecsSinceEpoch()) { | ||
565 | return false; | 580 | return false; | ||
566 | } | 581 | } | ||
582 | if (thumb.textKeys().contains(QStringLiteral("Thumb::DevicePixelRatio"))) { | ||||
583 | qreal dpr = thumb.text(QStringLiteral("Thumb::DevicePixelRatio")).toDouble(); | ||||
584 | thumb.setDevicePixelRatio(dpr); | ||||
585 | } | ||||
567 | 586 | | |||
568 | QString thumbnailerVersion = currentItem.plugin->property(QStringLiteral("ThumbnailerVersion"), QVariant::String).toString(); | 587 | QString thumbnailerVersion = currentItem.plugin->property(QStringLiteral("ThumbnailerVersion"), QVariant::String).toString(); | ||
569 | 588 | | |||
elvisangelaccio: Missing `const` | |||||
570 | if (!thumbnailerVersion.isEmpty() && thumb.text(QStringLiteral("Software")).startsWith(QLatin1String("KDE Thumbnail Generator"))) { | 589 | if (!thumbnailerVersion.isEmpty() && thumb.text(QStringLiteral("Software")).startsWith(QLatin1String("KDE Thumbnail Generator"))) { | ||
571 | //Check if the version matches | 590 | //Check if the version matches | ||
572 | //The software string should read "KDE Thumbnail Generator pluginName (vX)" | 591 | //The software string should read "KDE Thumbnail Generator pluginName (vX)" | ||
573 | QString softwareString = thumb.text(QStringLiteral("Software")).remove(QStringLiteral("KDE Thumbnail Generator")).trimmed(); | 592 | QString softwareString = thumb.text(QStringLiteral("Software")).remove(QStringLiteral("KDE Thumbnail Generator")).trimmed(); | ||
574 | if (softwareString.isEmpty()) { | 593 | if (softwareString.isEmpty()) { | ||
575 | // The thumbnail has been created with an older version, recreating | 594 | // The thumbnail has been created with an older version, recreating | ||
576 | return false; | 595 | return false; | ||
577 | } | 596 | } | ||
▲ Show 20 Lines • Show All 76 Lines • ▼ Show 20 Line(s) | 664 | { | |||
654 | bool save = bSave && currentItem.plugin->property(QStringLiteral("CacheThumbnail")).toBool() && !sequenceIndex; | 673 | bool save = bSave && currentItem.plugin->property(QStringLiteral("CacheThumbnail")).toBool() && !sequenceIndex; | ||
655 | job->addMetaData(QStringLiteral("mimeType"), currentItem.item.mimetype()); | 674 | job->addMetaData(QStringLiteral("mimeType"), currentItem.item.mimetype()); | ||
656 | job->addMetaData(QStringLiteral("width"), QString().setNum(save ? cacheWidth : width)); | 675 | job->addMetaData(QStringLiteral("width"), QString().setNum(save ? cacheWidth : width)); | ||
657 | job->addMetaData(QStringLiteral("height"), QString().setNum(save ? cacheHeight : height)); | 676 | job->addMetaData(QStringLiteral("height"), QString().setNum(save ? cacheHeight : height)); | ||
658 | job->addMetaData(QStringLiteral("iconSize"), QString().setNum(save ? 64 : iconSize)); | 677 | job->addMetaData(QStringLiteral("iconSize"), QString().setNum(save ? 64 : iconSize)); | ||
659 | job->addMetaData(QStringLiteral("iconAlpha"), QString().setNum(iconAlpha)); | 678 | job->addMetaData(QStringLiteral("iconAlpha"), QString().setNum(iconAlpha)); | ||
660 | job->addMetaData(QStringLiteral("plugin"), currentItem.plugin->library()); | 679 | job->addMetaData(QStringLiteral("plugin"), currentItem.plugin->library()); | ||
661 | job->addMetaData(QStringLiteral("enabledPlugins"), enabledPlugins.join(QLatin1Char(','))); | 680 | job->addMetaData(QStringLiteral("enabledPlugins"), enabledPlugins.join(QLatin1Char(','))); | ||
681 | job->addMetaData(QStringLiteral("devicePixelRatio"), QString::number(devicePixelRatio)); | ||||
662 | if (sequenceIndex) { | 682 | if (sequenceIndex) { | ||
663 | job->addMetaData(QStringLiteral("sequence-index"), QString().setNum(sequenceIndex)); | 683 | job->addMetaData(QStringLiteral("sequence-index"), QString().setNum(sequenceIndex)); | ||
664 | } | 684 | } | ||
665 | 685 | | |||
666 | #if WITH_SHM | 686 | #if WITH_SHM | ||
667 | if (shmid == -1) { | 687 | if (shmid == -1) { | ||
668 | if (shmaddr) { | 688 | if (shmaddr) { | ||
669 | shmdt((char *)shmaddr); | 689 | shmdt((char *)shmaddr); | ||
670 | shmctl(shmid, IPC_RMID, nullptr); | 690 | shmctl(shmid, IPC_RMID, nullptr); | ||
671 | } | 691 | } | ||
672 | shmid = shmget(IPC_PRIVATE, cacheWidth * cacheHeight * 4, IPC_CREAT | 0600); | 692 | shmid = shmget(IPC_PRIVATE, cacheWidth * cacheHeight * 4 * devicePixelRatio * devicePixelRatio, IPC_CREAT | 0600); | ||
mixing floating point and integers for a memory-allocation like function is a real bad idea. bruns: mixing floating point and integers for a memory-allocation like function is a real bad idea. | |||||
meven: I am thinking about making devicePixelRatio an int. | |||||
673 | if (shmid != -1) { | 693 | if (shmid != -1) { | ||
674 | shmaddr = (uchar *)(shmat(shmid, nullptr, SHM_RDONLY)); | 694 | shmaddr = (uchar *)(shmat(shmid, nullptr, SHM_RDONLY)); | ||
675 | if (shmaddr == (uchar *) - 1) { | 695 | if (shmaddr == (uchar *) - 1) { | ||
676 | shmctl(shmid, IPC_RMID, nullptr); | 696 | shmctl(shmid, IPC_RMID, nullptr); | ||
677 | shmaddr = nullptr; | 697 | shmaddr = nullptr; | ||
678 | shmid = -1; | 698 | shmid = -1; | ||
679 | } | 699 | } | ||
680 | } else { | 700 | } else { | ||
Show All 16 Lines | 713 | bool save = bSave && | |||
697 | !currentItem.item.url().adjusted(QUrl::RemoveFilename).toLocalFile().startsWith(thumbRoot)); | 717 | !currentItem.item.url().adjusted(QUrl::RemoveFilename).toLocalFile().startsWith(thumbRoot)); | ||
698 | QImage thumb; | 718 | QImage thumb; | ||
699 | #if WITH_SHM | 719 | #if WITH_SHM | ||
700 | if (shmaddr) { | 720 | if (shmaddr) { | ||
701 | // Keep this in sync with kdebase/kioslave/thumbnail.cpp | 721 | // Keep this in sync with kdebase/kioslave/thumbnail.cpp | ||
702 | QDataStream str(data); | 722 | QDataStream str(data); | ||
703 | int width, height; | 723 | int width, height; | ||
704 | quint8 iFormat; | 724 | quint8 iFormat; | ||
725 | qreal imgDevicePixelRatio = 1; | ||||
726 | // TODO KF6: add a version number as first parameter | ||||
705 | str >> width >> height >> iFormat; | 727 | str >> width >> height >> iFormat; | ||
728 | if (iFormat & 0x80) { | ||||
This here also breaks compatibility. Add a KF6 TODO to start the serialization with a version number. Meanwhile a hack is needed, like if (iFormat & 0x1000) { iFormat &= 0xFFF; str >> imgDevicePixelRatio; } dfaure: This here also breaks compatibility. Add a KF6 TODO to start the serialization with a version… | |||||
I am not sure the values you gave for the hack bitmask are correct, iFormat is quint8, so how come it contain 0x1000 meven: I am not sure the values you gave for the hack bitmask are correct, iFormat is quint8, so how… | |||||
729 | // HACK to deduce if imgDevicePixelRatio is present | ||||
730 | iFormat &= 0x7f; | ||||
731 | str >> imgDevicePixelRatio; | ||||
732 | } | ||||
706 | QImage::Format format = static_cast<QImage::Format>(iFormat); | 733 | QImage::Format format = static_cast<QImage::Format>(iFormat); | ||
707 | thumb = QImage(shmaddr, width, height, format).copy(); | 734 | thumb = QImage(shmaddr, width, height, format).copy(); | ||
735 | thumb.setDevicePixelRatio(imgDevicePixelRatio); | ||||
708 | } else | 736 | } else | ||
709 | #endif | 737 | #endif | ||
710 | thumb.loadFromData(data); | 738 | thumb.loadFromData(data); | ||
711 | 739 | | |||
712 | if (thumb.isNull()) { | 740 | if (thumb.isNull()) { | ||
713 | QDataStream s(data); | 741 | QDataStream s(data); | ||
714 | s >> thumb; | 742 | s >> thumb; | ||
715 | } | 743 | } | ||
716 | 744 | | |||
717 | if (save) { | 745 | if (save) { | ||
718 | thumb.setText(QStringLiteral("Thumb::URI"), QString::fromUtf8(origName)); | 746 | thumb.setText(QStringLiteral("Thumb::URI"), QString::fromUtf8(origName)); | ||
719 | thumb.setText(QStringLiteral("Thumb::MTime"), QString::number(tOrig.toSecsSinceEpoch())); | 747 | thumb.setText(QStringLiteral("Thumb::MTime"), QString::number(tOrig.toSecsSinceEpoch())); | ||
720 | thumb.setText(QStringLiteral("Thumb::Size"), number(currentItem.item.size())); | 748 | thumb.setText(QStringLiteral("Thumb::Size"), number(currentItem.item.size())); | ||
721 | thumb.setText(QStringLiteral("Thumb::Mimetype"), currentItem.item.mimetype()); | 749 | thumb.setText(QStringLiteral("Thumb::Mimetype"), currentItem.item.mimetype()); | ||
750 | thumb.setText(QStringLiteral("Thumb::DevicePixelRatio"), QString::number(thumb.devicePixelRatio())); | ||||
meven: Maybe add it optionally, when different from 1 | |||||
meven: Not needed. | |||||
722 | QString thumbnailerVersion = currentItem.plugin->property(QStringLiteral("ThumbnailerVersion"), QVariant::String).toString(); | 751 | QString thumbnailerVersion = currentItem.plugin->property(QStringLiteral("ThumbnailerVersion"), QVariant::String).toString(); | ||
723 | QString signature = QLatin1String("KDE Thumbnail Generator ") + currentItem.plugin->name(); | 752 | QString signature = QLatin1String("KDE Thumbnail Generator ") + currentItem.plugin->name(); | ||
724 | if (!thumbnailerVersion.isEmpty()) { | 753 | if (!thumbnailerVersion.isEmpty()) { | ||
725 | signature.append(QLatin1String(" (v") + thumbnailerVersion + QLatin1Char(')')); | 754 | signature.append(QLatin1String(" (v") + thumbnailerVersion + QLatin1Char(')')); | ||
726 | } | 755 | } | ||
727 | thumb.setText(QStringLiteral("Software"), signature); | 756 | thumb.setText(QStringLiteral("Software"), signature); | ||
728 | QSaveFile saveFile(thumbPath + thumbName); | 757 | QSaveFile saveFile(thumbPath + thumbName); | ||
729 | if (saveFile.open(QIODevice::WriteOnly)) { | 758 | if (saveFile.open(QIODevice::WriteOnly)) { | ||
730 | if (thumb.save(&saveFile, "PNG")) { | 759 | if (thumb.save(&saveFile, "PNG")) { | ||
731 | saveFile.commit(); | 760 | saveFile.commit(); | ||
732 | } | 761 | } | ||
733 | } | 762 | } | ||
734 | } | 763 | } | ||
735 | emitPreview(thumb); | 764 | emitPreview(thumb); | ||
736 | succeeded = true; | 765 | succeeded = true; | ||
737 | } | 766 | } | ||
738 | 767 | | |||
739 | void PreviewJobPrivate::emitPreview(const QImage &thumb) | 768 | void PreviewJobPrivate::emitPreview(const QImage &thumb) | ||
740 | { | 769 | { | ||
741 | Q_Q(PreviewJob); | 770 | Q_Q(PreviewJob); | ||
742 | QPixmap pix; | 771 | QPixmap pix; | ||
743 | if (thumb.width() > width || thumb.height() > height) { | 772 | if (thumb.width() > width * devicePixelRatio || thumb.height() > height * devicePixelRatio) { | ||
744 | pix = QPixmap::fromImage(thumb.scaled(QSize(width, height), Qt::KeepAspectRatio, Qt::SmoothTransformation)); | 773 | pix = QPixmap::fromImage(thumb.scaled(QSize(width * devicePixelRatio, height * devicePixelRatio), Qt::KeepAspectRatio, Qt::SmoothTransformation)); | ||
745 | } else { | 774 | } else { | ||
746 | pix = QPixmap::fromImage(thumb); | 775 | pix = QPixmap::fromImage(thumb); | ||
747 | } | 776 | } | ||
777 | pix.setDevicePixelRatio(thumb.devicePixelRatioF()); | ||||
748 | emit q->gotPreview(currentItem.item, pix); | 778 | emit q->gotPreview(currentItem.item, pix); | ||
749 | } | 779 | } | ||
750 | 780 | | |||
751 | QStringList PreviewJob::availablePlugins() | 781 | QStringList PreviewJob::availablePlugins() | ||
752 | { | 782 | { | ||
753 | QStringList result; | 783 | QStringList result; | ||
754 | const KService::List plugins = KServiceTypeTrader::self()->query(QStringLiteral("ThumbCreator")); | 784 | const KService::List plugins = KServiceTypeTrader::self()->query(QStringLiteral("ThumbCreator")); | ||
755 | for (const KService::Ptr &plugin : plugins) { | 785 | for (const KService::Ptr &plugin : plugins) { | ||
▲ Show 20 Lines • Show All 65 Lines • Show Last 20 Lines |
coding style: '{' on separate line