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 - Database thumbnail storage 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 #include "thumbnailcreator_p.h" 0018 0019 namespace Digikam 0020 { 0021 0022 void ThumbnailCreator::storeInDatabase(const ThumbnailInfo& info, const ThumbnailImage& image) const 0023 { 0024 ThumbsDbInfo dbInfo; 0025 0026 // We rely on loadThumbsDbInfo() being called before, so we do not need to look up 0027 // by filepath of uniqueHash to find out if a thumb need to be replaced. 0028 0029 dbInfo.id = d->dbIdForReplacement; 0030 d->dbIdForReplacement = -1; 0031 dbInfo.type = DatabaseThumbnail::PGF; 0032 dbInfo.modificationDate = info.modificationDate; 0033 dbInfo.orientationHint = image.exifOrientation; 0034 0035 if (dbInfo.type == DatabaseThumbnail::PGF) 0036 { 0037 // NOTE: see bug #233094: using PGF compression level 4 there. Do not use a value > 4, 0038 // else image is blurred due to down-sampling. 0039 0040 if (!PGFUtils::writePGFImageData(image.qimage, dbInfo.data, 4)) 0041 { 0042 qCWarning(DIGIKAM_GENERAL_LOG) << "Cannot save PGF thumb in DB"; 0043 return; 0044 } 0045 } 0046 else if (dbInfo.type == DatabaseThumbnail::JPEG) 0047 { 0048 QBuffer buffer(&dbInfo.data); 0049 buffer.open(QIODevice::WriteOnly); 0050 image.qimage.save(&buffer, "JPEG", 90); // Here we will use JPEG quality = 90 to reduce artifacts. 0051 buffer.close(); 0052 0053 if (dbInfo.data.isNull()) 0054 { 0055 qCWarning(DIGIKAM_GENERAL_LOG) << "Cannot save JPEG thumb in DB"; 0056 return; 0057 } 0058 } 0059 else if (dbInfo.type == DatabaseThumbnail::JPEG2000) 0060 { 0061 QBuffer buffer(&dbInfo.data); 0062 buffer.open(QIODevice::WriteOnly); 0063 image.qimage.save(&buffer, "JP2"); 0064 buffer.close(); 0065 0066 if (dbInfo.data.isNull()) 0067 { 0068 qCWarning(DIGIKAM_GENERAL_LOG) << "Cannot save JPEG2000 thumb in DB"; 0069 return; 0070 } 0071 } 0072 else if (dbInfo.type == DatabaseThumbnail::PNG) 0073 { 0074 QBuffer buffer(&dbInfo.data); 0075 buffer.open(QIODevice::WriteOnly); 0076 image.qimage.save(&buffer, "PNG", 0); 0077 buffer.close(); 0078 0079 if (dbInfo.data.isNull()) 0080 { 0081 qCWarning(DIGIKAM_GENERAL_LOG) << "Cannot save PNG thumb in DB"; 0082 return; 0083 } 0084 } 0085 0086 ThumbsDbAccess access; 0087 BdEngineBackend::QueryState lastQueryState = BdEngineBackend::QueryState(BdEngineBackend::ConnectionError); 0088 0089 while (lastQueryState == BdEngineBackend::ConnectionError) 0090 { 0091 lastQueryState = access.backend()->beginTransaction(); 0092 0093 if (BdEngineBackend::NoErrors != lastQueryState) 0094 { 0095 continue; 0096 } 0097 0098 // Insert thumbnail data 0099 0100 if (dbInfo.id == -1) 0101 { 0102 QVariant id; 0103 lastQueryState = access.db()->insertThumbnail(dbInfo, &id); 0104 0105 if (BdEngineBackend::NoErrors != lastQueryState) 0106 { 0107 continue; 0108 } 0109 else 0110 { 0111 dbInfo.id = id.toInt(); 0112 } 0113 } 0114 else 0115 { 0116 lastQueryState = access.db()->replaceThumbnail(dbInfo); 0117 0118 if (BdEngineBackend::NoErrors != lastQueryState) 0119 { 0120 continue; 0121 } 0122 } 0123 0124 // Insert lookup data used to locate thumbnail data 0125 0126 if (!info.customIdentifier.isNull()) 0127 { 0128 lastQueryState = access.db()->insertCustomIdentifier(info.customIdentifier, dbInfo.id); 0129 0130 if (BdEngineBackend::NoErrors != lastQueryState) 0131 { 0132 continue; 0133 } 0134 } 0135 else 0136 { 0137 if (!info.uniqueHash.isNull()) 0138 { 0139 lastQueryState = access.db()->insertUniqueHash(info.uniqueHash, info.fileSize, dbInfo.id); 0140 0141 if (BdEngineBackend::NoErrors != lastQueryState) 0142 { 0143 continue; 0144 } 0145 } 0146 0147 if (!info.filePath.isNull()) 0148 { 0149 lastQueryState = access.db()->insertFilePath(info.filePath, dbInfo.id); 0150 0151 if (BdEngineBackend::NoErrors != lastQueryState) 0152 { 0153 continue; 0154 } 0155 } 0156 } 0157 0158 lastQueryState = access.backend()->commitTransaction(); 0159 0160 if (BdEngineBackend::NoErrors != lastQueryState) 0161 { 0162 continue; 0163 } 0164 } 0165 } 0166 0167 ThumbsDbInfo ThumbnailCreator::loadThumbsDbInfo(const ThumbnailInfo& info) const 0168 { 0169 ThumbsDbAccess access; 0170 ThumbsDbInfo dbInfo; 0171 0172 // Custom identifier takes precedence 0173 0174 if (!info.customIdentifier.isEmpty()) 0175 { 0176 dbInfo = access.db()->findByCustomIdentifier(info.customIdentifier); 0177 } 0178 else 0179 { 0180 if (!info.uniqueHash.isEmpty()) 0181 { 0182 dbInfo = access.db()->findByHash(info.uniqueHash, info.fileSize); 0183 } 0184 0185 if (dbInfo.data.isNull() && !info.filePath.isEmpty()) 0186 { 0187 dbInfo = access.db()->findByFilePath(info.filePath, info.uniqueHash); 0188 } 0189 } 0190 0191 // Store for use in storeInDatabase() 0192 0193 d->dbIdForReplacement = dbInfo.id; 0194 0195 return dbInfo; 0196 } 0197 0198 bool ThumbnailCreator::isInDatabase(const ThumbnailInfo& info) const 0199 { 0200 ThumbsDbInfo dbInfo = loadThumbsDbInfo(info); 0201 0202 if (dbInfo.data.isNull()) 0203 { 0204 return false; 0205 } 0206 0207 // Check modification date 0208 0209 if (dbInfo.modificationDate < info.modificationDate) 0210 { 0211 return false; 0212 } 0213 0214 return true; 0215 } 0216 0217 ThumbnailImage ThumbnailCreator::loadFromDatabase(const ThumbnailInfo& info) const 0218 { 0219 ThumbsDbInfo dbInfo = loadThumbsDbInfo(info); 0220 ThumbnailImage image; 0221 0222 if (dbInfo.data.isNull()) 0223 { 0224 return ThumbnailImage(); 0225 } 0226 0227 // Check modification date 0228 0229 if (dbInfo.modificationDate < info.modificationDate) 0230 { 0231 return ThumbnailImage(); 0232 } 0233 0234 // Read QImage from data blob 0235 0236 if (dbInfo.type == DatabaseThumbnail::PGF) 0237 { 0238 if (!PGFUtils::readPGFImageData(dbInfo.data, image.qimage)) 0239 { 0240 qCWarning(DIGIKAM_GENERAL_LOG) << "Cannot load PGF thumb from DB"; 0241 return ThumbnailImage(); 0242 } 0243 } 0244 else if (dbInfo.type == DatabaseThumbnail::JPEG) 0245 { 0246 QBuffer buffer(&dbInfo.data); 0247 buffer.open(QIODevice::ReadOnly); 0248 image.qimage.load(&buffer, "JPEG"); 0249 buffer.close(); 0250 0251 if (dbInfo.data.isNull()) 0252 { 0253 qCWarning(DIGIKAM_GENERAL_LOG) << "Cannot load JPEG thumb from DB"; 0254 return ThumbnailImage(); 0255 } 0256 } 0257 else if (dbInfo.type == DatabaseThumbnail::JPEG2000) 0258 { 0259 QBuffer buffer(&dbInfo.data); 0260 buffer.open(QIODevice::ReadOnly); 0261 image.qimage.load(&buffer, "JP2"); 0262 buffer.close(); 0263 0264 if (dbInfo.data.isNull()) 0265 { 0266 qCWarning(DIGIKAM_GENERAL_LOG) << "Cannot load JPEG2000 thumb from DB"; 0267 return ThumbnailImage(); 0268 } 0269 } 0270 else if (dbInfo.type == DatabaseThumbnail::PNG) 0271 { 0272 QBuffer buffer(&dbInfo.data); 0273 buffer.open(QIODevice::ReadOnly); 0274 image.qimage.load(&buffer, "PNG"); 0275 buffer.close(); 0276 0277 if (dbInfo.data.isNull()) 0278 { 0279 qCWarning(DIGIKAM_GENERAL_LOG) << "Cannot load PNG thumb from DB"; 0280 return ThumbnailImage(); 0281 } 0282 } 0283 0284 // Give priority to main database's rotation flag 0285 // NOTE: Breaks rotation of RAWs which do not contain JPEG previews 0286 0287 image.exifOrientation = info.orientationHint; 0288 0289 if ((image.exifOrientation == DMetadata::ORIENTATION_UNSPECIFIED) && 0290 !info.filePath.isEmpty() && 0291 LoadSaveThread::infoProvider()) 0292 { 0293 image.exifOrientation = LoadSaveThread::infoProvider()->orientationHint(info.filePath); 0294 } 0295 0296 if (image.exifOrientation == DMetadata::ORIENTATION_UNSPECIFIED) 0297 { 0298 image.exifOrientation = dbInfo.orientationHint; 0299 } 0300 0301 return image; 0302 } 0303 0304 void ThumbnailCreator::deleteFromDatabase(const ThumbnailInfo& info) const 0305 { 0306 ThumbsDbAccess access; 0307 BdEngineBackend::QueryState lastQueryState = BdEngineBackend::QueryState(BdEngineBackend::ConnectionError); 0308 0309 while (BdEngineBackend::ConnectionError == lastQueryState) 0310 { 0311 lastQueryState = access.backend()->beginTransaction(); 0312 0313 if (BdEngineBackend::NoErrors != lastQueryState) 0314 { 0315 continue; 0316 } 0317 0318 if (!info.uniqueHash.isNull()) 0319 { 0320 lastQueryState = access.db()->removeByUniqueHash(info.uniqueHash, info.fileSize); 0321 0322 if (BdEngineBackend::NoErrors != lastQueryState) 0323 { 0324 continue; 0325 } 0326 } 0327 0328 if (!info.filePath.isNull()) 0329 { 0330 lastQueryState = access.db()->removeByFilePath(info.filePath); 0331 0332 if (BdEngineBackend::NoErrors != lastQueryState) 0333 { 0334 continue; 0335 } 0336 } 0337 0338 lastQueryState = access.backend()->commitTransaction(); 0339 0340 if (BdEngineBackend::NoErrors != lastQueryState) 0341 { 0342 continue; 0343 } 0344 } 0345 } 0346 0347 } // namespace Digikam