File indexing completed on 2024-04-28 04:58:03

0001 /*  This file is part of the KDE libraries
0002     SPDX-FileCopyrightText: 2000 Carsten Pfeiffer <pfeiffer@kde.org>
0003     SPDX-FileCopyrightText: 2000 Malte Starostik <malte@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "imagecreator.h"
0009 
0010 #include <QImageReader>
0011 
0012 #include <KMemoryInfo>
0013 #include <KPluginFactory>
0014 
0015 K_PLUGIN_CLASS_WITH_JSON(ImageCreator, "imagethumbnail.json")
0016 
0017 ImageCreator::ImageCreator(QObject *parent, const QVariantList &args)
0018     : KIO::ThumbnailCreator(parent, args)
0019 {
0020 }
0021 
0022 #define MiB(bytes) ((bytes)*1024ll * 1024ll)
0023 #define GiB(bytes) (MiB(bytes) * 1024ll)
0024 
0025 // When the ram check is disabled or not available, this is the expected default value of free RAM
0026 #define DEFAULT_FREE_RAM GiB(2)
0027 
0028 // The maximum usable RAM is the free RAM is divided by this number:
0029 // if the calculated image size is greater than this value, the preview is skipped.
0030 #define RAM_DIVISOR 3
0031 
0032 // An image smaller than 64 MiB will be loaded even if the usable RAM check fails.
0033 #define MINIMUM_GUARANTEED_SIZE MiB(64)
0034 
0035 /**
0036  * @brief maximumThumbnailRam
0037  * Calculates the maximum RAM that can be used to generate the thumbnail.
0038  *
0039  * The value returned is a third of the available free RAM.
0040  */
0041 qint64 maximumThumbnailRam()
0042 {
0043     // read available RAM (physical free ram only)
0044     auto freeRam = DEFAULT_FREE_RAM;
0045 
0046     KMemoryInfo m;
0047     if (!m.isNull()) {
0048         freeRam = qint64(m.availablePhysical());
0049     }
0050 
0051     /*
0052      * NOTE 1: a minimal 64MiB image is always guaranteed (this small size should never cause OS thrashing).
0053      * NOTE 2: the freeRam is divided by 3 for the following reasons:
0054      *         - the image could be converted (e.g. when depth() != 32)
0055      *         - we don't want to use all free ram for a thumbnail :)
0056      */
0057     return std::max(MINIMUM_GUARANTEED_SIZE, freeRam / RAM_DIVISOR);
0058 }
0059 
0060 KIO::ThumbnailResult ImageCreator::create(const KIO::ThumbnailRequest &request)
0061 {
0062     // create image preview
0063     QImageReader ir(request.url().toLocalFile());
0064 
0065     /* The idea is to read the free ram and try to avoid OS trashing when the
0066      * image is too big:
0067      * - Qt 6: we can simply limit the maximum size that image reader can handle.
0068      * - Qt 5: the image plugin that allows big images should help us by implementing
0069      *         the QImageIOHandler::Size option (TIFF, PSB and XCF already have).
0070      */
0071     auto ram = maximumThumbnailRam();
0072 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0073     if (ir.supportsOption(QImageIOHandler::Size)) {
0074         auto size = ir.size();
0075         // euristic way: we always calculate the size supposing a 16-bits RGBA image
0076         if (size == QSize() || (8ll * size.width() * size.height() > ram)) {
0077             return KIO::ThumbnailResult::fail();
0078         }
0079     }
0080 #else
0081     QImageReader::setAllocationLimit(ram / 1024 / 1024);
0082 #endif
0083 
0084     ir.setAutoTransform(true);
0085     ir.setDecideFormatFromContent(true);
0086     if (ir.format() == QByteArray("raw")) {
0087         // make preview generation of raw files ~3 times faster (requires setDecideFormatFromContent(true))
0088         ir.setQuality(1);
0089     }
0090 
0091     QImage img;
0092     ir.read(&img);
0093 
0094     if (!img.isNull()) {
0095         return KIO::ThumbnailResult::pass(img);
0096     }
0097 
0098     return KIO::ThumbnailResult::fail();
0099 }
0100 
0101 #include "imagecreator.moc"
0102 #include "moc_imagecreator.cpp"