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 defaultDevicePixelRatio) | ||||
dfaure: coding style: '{' on separate line | |||||
174 | { | ||||
175 | s_defaultDevicePixelRatio = defaultDevicePixelRatio; | ||||
176 | } | ||||
177 | | ||||
167 | PreviewJob::PreviewJob(const KFileItemList &items, int width, int height, | 178 | PreviewJob::PreviewJob(const KFileItemList &items, int width, int height, | ||
168 | int iconSize, int iconAlpha, bool scale, bool save, | 179 | int iconSize, int iconAlpha, bool scale, bool save, | ||
169 | const QStringList *enabledPlugins) | 180 | const QStringList *enabledPlugins) | ||
170 | : KIO::Job(*new PreviewJobPrivate(items, QSize(width, height ? height : width))) | 181 | : KIO::Job(*new PreviewJobPrivate(items, QSize(width, height ? height : width))) | ||
171 | { | 182 | { | ||
172 | Q_D(PreviewJob); | 183 | Q_D(PreviewJob); | ||
173 | d->enabledPlugins = enabledPlugins ? *enabledPlugins : availablePlugins(); | 184 | d->enabledPlugins = enabledPlugins ? *enabledPlugins : availablePlugins(); | ||
174 | d->iconSize = iconSize; | 185 | d->iconSize = iconSize; | ||
▲ Show 20 Lines • Show All 206 Lines • ▼ Show 20 Line(s) | 281 | { | |||
381 | 392 | | |||
382 | if (bNeedCache) { | 393 | if (bNeedCache) { | ||
383 | if (width <= 128 && height <= 128) { | 394 | if (width <= 128 && height <= 128) { | ||
384 | cacheWidth = cacheHeight = 128; | 395 | cacheWidth = cacheHeight = 128; | ||
385 | } else { | 396 | } else { | ||
386 | cacheWidth = cacheHeight = 256; | 397 | cacheWidth = cacheHeight = 256; | ||
387 | } | 398 | } | ||
388 | thumbPath = thumbRoot + QLatin1String(cacheWidth == 128 ? "normal/" : "large/"); | 399 | thumbPath = thumbRoot + QLatin1String(cacheWidth == 128 ? "normal/" : "large/"); | ||
389 | if (!QDir(thumbPath).exists()) { | 400 | 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) | 401 | if (QDir().mkpath(thumbPath)) { // Qt5 TODO: mkpath(dirPath, permissions) | ||
391 | QFile f(thumbPath); | 402 | QFile f(thumbPath); | ||
392 | f.setPermissions(QFile::ReadUser | QFile::WriteUser | QFile::ExeUser); // 0700 | 403 | f.setPermissions(QFile::ReadUser | QFile::WriteUser | QFile::ExeUser); // 0700 | ||
393 | } | 404 | } | ||
394 | } | 405 | } | ||
395 | } else { | 406 | } else { | ||
396 | bSave = false; | 407 | bSave = false; | ||
397 | } | 408 | } | ||
Show All 24 Lines | 432 | { | |||
422 | d_func()->sequenceIndex = index; | 433 | d_func()->sequenceIndex = index; | ||
423 | } | 434 | } | ||
424 | 435 | | |||
425 | int KIO::PreviewJob::sequenceIndex() const | 436 | int KIO::PreviewJob::sequenceIndex() const | ||
426 | { | 437 | { | ||
427 | return d_func()->sequenceIndex; | 438 | return d_func()->sequenceIndex; | ||
428 | } | 439 | } | ||
429 | 440 | | |||
441 | void KIO::PreviewJob::setDevicePixelRatio(qreal dpr) | ||||
442 | { | ||||
443 | 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… | |||||
444 | } | ||||
445 | | ||||
430 | void PreviewJob::setIgnoreMaximumSize(bool ignoreSize) | 446 | void PreviewJob::setIgnoreMaximumSize(bool ignoreSize) | ||
431 | { | 447 | { | ||
432 | d_func()->ignoreMaximumSize = ignoreSize; | 448 | d_func()->ignoreMaximumSize = ignoreSize; | ||
433 | } | 449 | } | ||
434 | 450 | | |||
435 | void PreviewJobPrivate::cleanupTempFile() | 451 | void PreviewJobPrivate::cleanupTempFile() | ||
436 | { | 452 | { | ||
437 | if (!tempName.isEmpty()) { | 453 | if (!tempName.isEmpty()) { | ||
▲ Show 20 Lines • Show All 121 Lines • ▼ Show 20 Line(s) | 560 | { | |||
559 | if (!thumb.load(thumbPath + thumbName)) { | 575 | if (!thumb.load(thumbPath + thumbName)) { | ||
560 | return false; | 576 | return false; | ||
561 | } | 577 | } | ||
562 | 578 | | |||
563 | if (thumb.text(QStringLiteral("Thumb::URI")) != QString::fromUtf8(origName) || | 579 | if (thumb.text(QStringLiteral("Thumb::URI")) != QString::fromUtf8(origName) || | ||
564 | thumb.text(QStringLiteral("Thumb::MTime")).toLongLong() != tOrig.toSecsSinceEpoch()) { | 580 | thumb.text(QStringLiteral("Thumb::MTime")).toLongLong() != tOrig.toSecsSinceEpoch()) { | ||
565 | return false; | 581 | return false; | ||
566 | } | 582 | } | ||
583 | if (thumb.textKeys().contains(QStringLiteral("Thumb::DevicePixelRatio"))) { | ||||
584 | qreal dpr = thumb.text(QStringLiteral("Thumb::DevicePixelRatio")).toDouble(); | ||||
585 | thumb.setDevicePixelRatio(dpr); | ||||
586 | } | ||||
567 | 587 | | |||
568 | QString thumbnailerVersion = currentItem.plugin->property(QStringLiteral("ThumbnailerVersion"), QVariant::String).toString(); | 588 | QString thumbnailerVersion = currentItem.plugin->property(QStringLiteral("ThumbnailerVersion"), QVariant::String).toString(); | ||
569 | 589 | | |||
elvisangelaccio: Missing `const` | |||||
570 | if (!thumbnailerVersion.isEmpty() && thumb.text(QStringLiteral("Software")).startsWith(QLatin1String("KDE Thumbnail Generator"))) { | 590 | if (!thumbnailerVersion.isEmpty() && thumb.text(QStringLiteral("Software")).startsWith(QLatin1String("KDE Thumbnail Generator"))) { | ||
571 | //Check if the version matches | 591 | //Check if the version matches | ||
572 | //The software string should read "KDE Thumbnail Generator pluginName (vX)" | 592 | //The software string should read "KDE Thumbnail Generator pluginName (vX)" | ||
573 | QString softwareString = thumb.text(QStringLiteral("Software")).remove(QStringLiteral("KDE Thumbnail Generator")).trimmed(); | 593 | QString softwareString = thumb.text(QStringLiteral("Software")).remove(QStringLiteral("KDE Thumbnail Generator")).trimmed(); | ||
574 | if (softwareString.isEmpty()) { | 594 | if (softwareString.isEmpty()) { | ||
575 | // The thumbnail has been created with an older version, recreating | 595 | // The thumbnail has been created with an older version, recreating | ||
576 | return false; | 596 | return false; | ||
577 | } | 597 | } | ||
▲ Show 20 Lines • Show All 76 Lines • ▼ Show 20 Line(s) | 665 | { | |||
654 | bool save = bSave && currentItem.plugin->property(QStringLiteral("CacheThumbnail")).toBool() && !sequenceIndex; | 674 | bool save = bSave && currentItem.plugin->property(QStringLiteral("CacheThumbnail")).toBool() && !sequenceIndex; | ||
655 | job->addMetaData(QStringLiteral("mimeType"), currentItem.item.mimetype()); | 675 | job->addMetaData(QStringLiteral("mimeType"), currentItem.item.mimetype()); | ||
656 | job->addMetaData(QStringLiteral("width"), QString().setNum(save ? cacheWidth : width)); | 676 | job->addMetaData(QStringLiteral("width"), QString().setNum(save ? cacheWidth : width)); | ||
657 | job->addMetaData(QStringLiteral("height"), QString().setNum(save ? cacheHeight : height)); | 677 | job->addMetaData(QStringLiteral("height"), QString().setNum(save ? cacheHeight : height)); | ||
658 | job->addMetaData(QStringLiteral("iconSize"), QString().setNum(save ? 64 : iconSize)); | 678 | job->addMetaData(QStringLiteral("iconSize"), QString().setNum(save ? 64 : iconSize)); | ||
659 | job->addMetaData(QStringLiteral("iconAlpha"), QString().setNum(iconAlpha)); | 679 | job->addMetaData(QStringLiteral("iconAlpha"), QString().setNum(iconAlpha)); | ||
660 | job->addMetaData(QStringLiteral("plugin"), currentItem.plugin->library()); | 680 | job->addMetaData(QStringLiteral("plugin"), currentItem.plugin->library()); | ||
661 | job->addMetaData(QStringLiteral("enabledPlugins"), enabledPlugins.join(QLatin1Char(','))); | 681 | job->addMetaData(QStringLiteral("enabledPlugins"), enabledPlugins.join(QLatin1Char(','))); | ||
682 | job->addMetaData(QStringLiteral("devicePixelRatio"), QString::number(devicePixelRatio)); | ||||
662 | if (sequenceIndex) { | 683 | if (sequenceIndex) { | ||
663 | job->addMetaData(QStringLiteral("sequence-index"), QString().setNum(sequenceIndex)); | 684 | job->addMetaData(QStringLiteral("sequence-index"), QString().setNum(sequenceIndex)); | ||
664 | } | 685 | } | ||
665 | 686 | | |||
666 | #if WITH_SHM | 687 | #if WITH_SHM | ||
667 | if (shmid == -1) { | 688 | if (shmid == -1) { | ||
668 | if (shmaddr) { | 689 | if (shmaddr) { | ||
669 | shmdt((char *)shmaddr); | 690 | shmdt((char *)shmaddr); | ||
670 | shmctl(shmid, IPC_RMID, nullptr); | 691 | shmctl(shmid, IPC_RMID, nullptr); | ||
671 | } | 692 | } | ||
672 | shmid = shmget(IPC_PRIVATE, cacheWidth * cacheHeight * 4, IPC_CREAT | 0600); | 693 | 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) { | 694 | if (shmid != -1) { | ||
674 | shmaddr = (uchar *)(shmat(shmid, nullptr, SHM_RDONLY)); | 695 | shmaddr = (uchar *)(shmat(shmid, nullptr, SHM_RDONLY)); | ||
675 | if (shmaddr == (uchar *) - 1) { | 696 | if (shmaddr == (uchar *) - 1) { | ||
676 | shmctl(shmid, IPC_RMID, nullptr); | 697 | shmctl(shmid, IPC_RMID, nullptr); | ||
677 | shmaddr = nullptr; | 698 | shmaddr = nullptr; | ||
678 | shmid = -1; | 699 | shmid = -1; | ||
679 | } | 700 | } | ||
680 | } else { | 701 | } else { | ||
Show All 16 Lines | 714 | bool save = bSave && | |||
697 | !currentItem.item.url().adjusted(QUrl::RemoveFilename).toLocalFile().startsWith(thumbRoot)); | 718 | !currentItem.item.url().adjusted(QUrl::RemoveFilename).toLocalFile().startsWith(thumbRoot)); | ||
698 | QImage thumb; | 719 | QImage thumb; | ||
699 | #if WITH_SHM | 720 | #if WITH_SHM | ||
700 | if (shmaddr) { | 721 | if (shmaddr) { | ||
701 | // Keep this in sync with kdebase/kioslave/thumbnail.cpp | 722 | // Keep this in sync with kdebase/kioslave/thumbnail.cpp | ||
702 | QDataStream str(data); | 723 | QDataStream str(data); | ||
703 | int width, height; | 724 | int width, height; | ||
704 | quint8 iFormat; | 725 | quint8 iFormat; | ||
726 | qreal imgDevicePixelRatio = 1; | ||||
727 | // TODO KF6: add a version number as first parameter | ||||
705 | str >> width >> height >> iFormat; | 728 | str >> width >> height >> iFormat; | ||
729 | 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… | |||||
730 | // HACK to deduce if imgDevicePixelRatio is present | ||||
731 | iFormat &= 0x7f; | ||||
732 | str >> imgDevicePixelRatio; | ||||
733 | } | ||||
706 | QImage::Format format = static_cast<QImage::Format>(iFormat); | 734 | QImage::Format format = static_cast<QImage::Format>(iFormat); | ||
707 | thumb = QImage(shmaddr, width, height, format).copy(); | 735 | thumb = QImage(shmaddr, width, height, format).copy(); | ||
736 | thumb.setDevicePixelRatio(imgDevicePixelRatio); | ||||
708 | } else | 737 | } else | ||
709 | #endif | 738 | #endif | ||
710 | thumb.loadFromData(data); | 739 | thumb.loadFromData(data); | ||
711 | 740 | | |||
712 | if (thumb.isNull()) { | 741 | if (thumb.isNull()) { | ||
713 | QDataStream s(data); | 742 | QDataStream s(data); | ||
714 | s >> thumb; | 743 | s >> thumb; | ||
715 | } | 744 | } | ||
716 | 745 | | |||
717 | if (save) { | 746 | if (save) { | ||
718 | thumb.setText(QStringLiteral("Thumb::URI"), QString::fromUtf8(origName)); | 747 | thumb.setText(QStringLiteral("Thumb::URI"), QString::fromUtf8(origName)); | ||
719 | thumb.setText(QStringLiteral("Thumb::MTime"), QString::number(tOrig.toSecsSinceEpoch())); | 748 | thumb.setText(QStringLiteral("Thumb::MTime"), QString::number(tOrig.toSecsSinceEpoch())); | ||
720 | thumb.setText(QStringLiteral("Thumb::Size"), number(currentItem.item.size())); | 749 | thumb.setText(QStringLiteral("Thumb::Size"), number(currentItem.item.size())); | ||
721 | thumb.setText(QStringLiteral("Thumb::Mimetype"), currentItem.item.mimetype()); | 750 | thumb.setText(QStringLiteral("Thumb::Mimetype"), currentItem.item.mimetype()); | ||
751 | 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(); | 752 | QString thumbnailerVersion = currentItem.plugin->property(QStringLiteral("ThumbnailerVersion"), QVariant::String).toString(); | ||
723 | QString signature = QLatin1String("KDE Thumbnail Generator ") + currentItem.plugin->name(); | 753 | QString signature = QLatin1String("KDE Thumbnail Generator ") + currentItem.plugin->name(); | ||
724 | if (!thumbnailerVersion.isEmpty()) { | 754 | if (!thumbnailerVersion.isEmpty()) { | ||
725 | signature.append(QLatin1String(" (v") + thumbnailerVersion + QLatin1Char(')')); | 755 | signature.append(QLatin1String(" (v") + thumbnailerVersion + QLatin1Char(')')); | ||
726 | } | 756 | } | ||
727 | thumb.setText(QStringLiteral("Software"), signature); | 757 | thumb.setText(QStringLiteral("Software"), signature); | ||
728 | QSaveFile saveFile(thumbPath + thumbName); | 758 | QSaveFile saveFile(thumbPath + thumbName); | ||
729 | if (saveFile.open(QIODevice::WriteOnly)) { | 759 | if (saveFile.open(QIODevice::WriteOnly)) { | ||
730 | if (thumb.save(&saveFile, "PNG")) { | 760 | if (thumb.save(&saveFile, "PNG")) { | ||
731 | saveFile.commit(); | 761 | saveFile.commit(); | ||
732 | } | 762 | } | ||
733 | } | 763 | } | ||
734 | } | 764 | } | ||
735 | emitPreview(thumb); | 765 | emitPreview(thumb); | ||
736 | succeeded = true; | 766 | succeeded = true; | ||
737 | } | 767 | } | ||
738 | 768 | | |||
739 | void PreviewJobPrivate::emitPreview(const QImage &thumb) | 769 | void PreviewJobPrivate::emitPreview(const QImage &thumb) | ||
740 | { | 770 | { | ||
741 | Q_Q(PreviewJob); | 771 | Q_Q(PreviewJob); | ||
742 | QPixmap pix; | 772 | QPixmap pix; | ||
743 | if (thumb.width() > width || thumb.height() > height) { | 773 | if (thumb.width() > width * thumb.devicePixelRatio() || thumb.height() > height * thumb.devicePixelRatio()) { | ||
744 | pix = QPixmap::fromImage(thumb.scaled(QSize(width, height), Qt::KeepAspectRatio, Qt::SmoothTransformation)); | 774 | pix = QPixmap::fromImage(thumb.scaled(QSize(width * thumb.devicePixelRatio(), height * thumb.devicePixelRatio()), Qt::KeepAspectRatio, Qt::SmoothTransformation)); | ||
745 | } else { | 775 | } else { | ||
746 | pix = QPixmap::fromImage(thumb); | 776 | pix = QPixmap::fromImage(thumb); | ||
747 | } | 777 | } | ||
778 | pix.setDevicePixelRatio(thumb.devicePixelRatio()); | ||||
748 | emit q->gotPreview(currentItem.item, pix); | 779 | emit q->gotPreview(currentItem.item, pix); | ||
749 | } | 780 | } | ||
750 | 781 | | |||
751 | QStringList PreviewJob::availablePlugins() | 782 | QStringList PreviewJob::availablePlugins() | ||
752 | { | 783 | { | ||
753 | QStringList result; | 784 | QStringList result; | ||
754 | const KService::List plugins = KServiceTypeTrader::self()->query(QStringLiteral("ThumbCreator")); | 785 | const KService::List plugins = KServiceTypeTrader::self()->query(QStringLiteral("ThumbCreator")); | ||
755 | for (const KService::Ptr &plugin : plugins) { | 786 | for (const KService::Ptr &plugin : plugins) { | ||
▲ Show 20 Lines • Show All 65 Lines • Show Last 20 Lines |
coding style: '{' on separate line