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