File indexing completed on 2025-04-27 03:58:09

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2007-07-20
0007  * Description : Loader for thumbnails
0008  *
0009  * SPDX-FileCopyrightText: 2003-2005 by Renchi Raju <renchi dot raju at gmail dot com>
0010  * SPDX-FileCopyrightText: 2003-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0011  * SPDX-FileCopyrightText: 2006-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
0012  *
0013  * SPDX-License-Identifier: GPL-2.0-or-later
0014  *
0015  * ============================================================ */
0016 
0017 // C++ includes
0018 
0019 #include <cstdlib>
0020 #include <cstdio>
0021 #include <cstring>
0022 
0023 // Qt includes
0024 
0025 #include <QImage>
0026 #include <QDir>
0027 #include <QStandardPaths>
0028 #include <QCryptographicHash>
0029 #include <QUrl>
0030 
0031 // C ANSI includes
0032 
0033 extern "C"
0034 {
0035 #ifndef Q_CC_MSVC
0036 #   include <unistd.h>
0037 #endif
0038 #include <sys/stat.h>
0039 #include <sys/types.h>
0040 
0041 #ifndef Q_OS_WIN32
0042 #   include <sys/ipc.h>
0043 #   include <sys/shm.h>
0044 #endif
0045 
0046 #include <time.h>
0047 #include <png.h>
0048 }
0049 
0050 // Local includes
0051 
0052 #include "thumbnailcreator_p.h"
0053 
0054 // Definitions
0055 
0056 #define PNG_BYTES_TO_CHECK 4
0057 
0058 namespace Digikam
0059 {
0060 
0061 // --- Static methods: Generate the thumbnail path according to FreeDesktop spec ---
0062 
0063 QString ThumbnailCreator::normalThumbnailDir()
0064 {
0065     return (QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) +
0066                                              QLatin1String("/thumbnails/normal/"));
0067 }
0068 
0069 QString ThumbnailCreator::largeThumbnailDir()
0070 {
0071     return (QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) +
0072                                              QLatin1String("/thumbnails/large/"));
0073 }
0074 
0075 QString ThumbnailCreator::thumbnailPath(const QString& filePath, const QString& basePath)
0076 {
0077     return thumbnailPathFromUri(thumbnailUri(filePath), basePath);
0078 }
0079 
0080 QString ThumbnailCreator::thumbnailUri(const QString& filePath)
0081 {
0082     return QUrl::fromLocalFile(filePath).url();
0083 }
0084 
0085 QString ThumbnailCreator::thumbnailPathFromUri(const QString& uri, const QString& basePath)
0086 {
0087     QCryptographicHash md5(QCryptographicHash::Md5);
0088     md5.addData(QFile::encodeName(uri).constData());
0089 
0090     return (basePath                                                                      +
0091             QString::fromUtf8(QFile::encodeName(QString::fromUtf8(md5.result().toHex()))) +
0092             QLatin1String(".png"));
0093 }
0094 
0095 // --- non-static methods ---
0096 
0097 void ThumbnailCreator::initThumbnailDirs()
0098 {
0099     d->smallThumbPath = normalThumbnailDir();
0100     d->bigThumbPath   = largeThumbnailDir();
0101 
0102     if (!QDir(d->smallThumbPath).exists())
0103     {
0104         if (QDir().mkpath(d->smallThumbPath))
0105         {
0106             QFile f(d->smallThumbPath);
0107             f.setPermissions(QFile::ReadUser | QFile::WriteUser | QFile::ExeUser); // 0700
0108         }
0109     }
0110 
0111     if (!QDir(d->bigThumbPath).exists())
0112     {
0113         if (QDir().mkpath(d->bigThumbPath))
0114         {
0115             QFile f(d->bigThumbPath);
0116             f.setPermissions(QFile::ReadUser | QFile::WriteUser | QFile::ExeUser); // 0700
0117         }
0118     }
0119 }
0120 
0121 QString ThumbnailCreator::thumbnailPath(const QString& filePath) const
0122 {
0123     QString basePath = (d->storageSize() == 128) ? d->smallThumbPath : d->bigThumbPath;
0124 
0125     return thumbnailPath(filePath, basePath);
0126 }
0127 
0128 // --- Basic PNG loading ---
0129 
0130 QImage ThumbnailCreator::loadPNG(const QString& path) const
0131 {
0132     png_uint_32  w32, h32;
0133     int          w, h;
0134     bool         has_alpha = 0;
0135     png_structp  png_ptr   = nullptr;
0136     png_infop    info_ptr  = nullptr;
0137     int          bit_depth, color_type, interlace_type;
0138     QImage       qimage;
0139 
0140 #ifdef Q_OS_WIN
0141 
0142     FILE* const f = _wfopen((const wchar_t*)path.utf16(), L"rb");
0143 
0144 #else
0145 
0146     FILE* const f = fopen(path.toUtf8().constData(), "rb");
0147 
0148 #endif
0149 
0150     if (!f)
0151     {
0152         return qimage;
0153     }
0154 
0155     unsigned char buf[PNG_BYTES_TO_CHECK];
0156 
0157     size_t itemsRead = fread(buf, 1, PNG_BYTES_TO_CHECK, f);
0158 
0159 #if PNG_LIBPNG_VER >= 10400
0160 
0161     if ((itemsRead != PNG_BYTES_TO_CHECK) || png_sig_cmp(buf, 0, PNG_BYTES_TO_CHECK))
0162 
0163 #else
0164 
0165     if ((itemsRead != PNG_BYTES_TO_CHECK) || !png_check_sig(buf, PNG_BYTES_TO_CHECK))
0166 
0167 #endif
0168     {
0169         fclose(f);
0170 
0171         return qimage;
0172     }
0173 
0174     rewind(f);
0175 
0176     png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
0177 
0178     if (!png_ptr)
0179     {
0180         fclose(f);
0181 
0182         return qimage;
0183     }
0184 
0185     info_ptr = png_create_info_struct(png_ptr);
0186 
0187     if (!info_ptr)
0188     {
0189         png_destroy_read_struct(&png_ptr, nullptr, nullptr);
0190         fclose(f);
0191 
0192         return qimage;
0193     }
0194 
0195 #if PNG_LIBPNG_VER >= 10400
0196 
0197     if (setjmp(png_jmpbuf(png_ptr)))
0198 
0199 #else
0200 
0201     if (setjmp(png_ptr->jmpbuf))
0202 
0203 #endif
0204     {
0205         png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
0206         fclose(f);
0207 
0208         return qimage;
0209     }
0210 
0211     png_init_io(png_ptr, f);
0212     png_read_info(png_ptr, info_ptr);
0213     png_get_IHDR(png_ptr, info_ptr, (png_uint_32*) (&w32),
0214                  (png_uint_32*) (&h32), &bit_depth, &color_type,
0215                  &interlace_type, nullptr, nullptr);
0216 
0217     bool  has_grey = 0;
0218     w              = w32;
0219     h              = h32;
0220     qimage         = QImage(w, h, QImage::Format_ARGB32);
0221 
0222     if (color_type == PNG_COLOR_TYPE_PALETTE)
0223     {
0224         png_set_expand(png_ptr);
0225     }
0226 
0227 #if PNG_LIBPNG_VER >= 10400
0228 
0229     png_byte info_color_type = png_get_color_type(png_ptr, info_ptr);
0230 
0231 #else
0232 
0233     png_byte info_color_type = info_ptr->color_type;
0234 
0235 #endif
0236 
0237     if (info_color_type == PNG_COLOR_TYPE_RGB_ALPHA)
0238     {
0239         has_alpha = 1;
0240     }
0241 
0242     if (info_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
0243     {
0244         has_alpha = 1;
0245         has_grey  = 1;
0246     }
0247 
0248     if (info_color_type == PNG_COLOR_TYPE_GRAY)
0249     {
0250         has_grey = 1;
0251     }
0252 
0253     unsigned char** lines = nullptr;
0254     int             i;
0255 
0256     if (has_alpha)
0257     {
0258         png_set_expand(png_ptr);
0259     }
0260 
0261     if (QSysInfo::ByteOrder == QSysInfo::LittleEndian)           // Intel
0262     {
0263         png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
0264         png_set_bgr(png_ptr);
0265     }
0266     else                                                         // PPC
0267     {
0268         png_set_swap_alpha(png_ptr);
0269         png_set_filler(png_ptr, 0xff, PNG_FILLER_BEFORE);
0270     }
0271 
0272     // 16bit color -> 8bit color
0273 
0274     if (bit_depth == 16)
0275     {
0276         png_set_strip_16(png_ptr);
0277     }
0278 
0279     // pack all pixels to byte boundaries
0280 
0281     png_set_packing(png_ptr);
0282 
0283     if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
0284     {
0285         png_set_expand(png_ptr);
0286     }
0287 
0288     lines = (unsigned char**)malloc(h * sizeof(unsigned char*));
0289 
0290     if (!lines)
0291     {
0292         png_read_end(png_ptr, info_ptr);
0293         png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) nullptr);
0294         fclose(f);
0295 
0296         return qimage;
0297     }
0298 
0299     if (has_grey)
0300     {
0301         png_set_gray_to_rgb(png_ptr);
0302 
0303         if (png_get_bit_depth(png_ptr, info_ptr) < 8)
0304 
0305 #if PNG_LIBPNG_VER >= 10400
0306 
0307             png_set_expand_gray_1_2_4_to_8(png_ptr);
0308 #else
0309 
0310             png_set_gray_1_2_4_to_8(png_ptr);
0311 
0312 #endif
0313 
0314     }
0315 
0316     int sizeOfUint = sizeof(unsigned int);
0317 
0318     for (i = 0 ; i < h ; ++i)
0319     {
0320         lines[i] = ((unsigned char*)(qimage.bits())) + (i * w * sizeOfUint);
0321     }
0322 
0323     png_read_image(png_ptr, lines);
0324     free(lines);
0325 
0326     png_textp text_ptr;
0327     int num_text = 0;
0328     png_get_text(png_ptr,info_ptr, &text_ptr, &num_text);
0329 
0330     while (num_text--)
0331     {
0332         qimage.setText(QString::fromUtf8(text_ptr->key), QString::fromUtf8(text_ptr->text));
0333         ++text_ptr;
0334     }
0335 
0336     png_read_end(png_ptr, info_ptr);
0337     png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) nullptr);
0338     fclose(f);
0339 
0340     return qimage;
0341 }
0342 
0343 } // namespace Digikam