File indexing completed on 2025-01-05 03:59:42
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2004-02-14 0007 * Description : image data interface for image tools 0008 * 0009 * SPDX-FileCopyrightText: 2004-2005 by Renchi Raju <renchi dot raju at gmail dot com> 0010 * SPDX-FileCopyrightText: 2004-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0011 * 0012 * SPDX-License-Identifier: GPL-2.0-or-later 0013 * 0014 * ============================================================ */ 0015 0016 #include "imageiface.h" 0017 0018 // Qt includes 0019 0020 #include <QWidget> 0021 #include <QSize> 0022 #include <QBitmap> 0023 #include <QPainter> 0024 0025 // Local includes 0026 0027 #include "digikam_debug.h" 0028 #include "exposurecontainer.h" 0029 #include "iccmanager.h" 0030 #include "iccsettingscontainer.h" 0031 #include "icctransform.h" 0032 #include "editorcore.h" 0033 #include "dmetadata.h" 0034 #include "digikam_globals.h" 0035 0036 namespace Digikam 0037 { 0038 0039 class Q_DECL_HIDDEN ImageIface::Private 0040 { 0041 public: 0042 0043 explicit Private() 0044 : previewType (FullImage), 0045 originalWidth (0), 0046 originalHeight (0), 0047 originalBytesDepth (0), 0048 constrainWidth (0), 0049 constrainHeight (0), 0050 previewWidth (0), 0051 previewHeight (0), 0052 core (EditorCore::defaultInstance()) 0053 { 0054 } 0055 0056 QPixmap checkPixmap(); 0057 0058 uchar* previewImageData(); 0059 0060 public: 0061 0062 ImageIface::PreviewType previewType; 0063 0064 int originalWidth; 0065 int originalHeight; 0066 int originalBytesDepth; 0067 0068 int constrainWidth; 0069 int constrainHeight; 0070 0071 int previewWidth; 0072 int previewHeight; 0073 0074 QPixmap qcheck; 0075 0076 DImg previewImage; 0077 DImg targetPreviewImage; 0078 EditorCore* const core; 0079 }; 0080 0081 QPixmap ImageIface::Private::checkPixmap() 0082 { 0083 if (qcheck.isNull()) 0084 { 0085 qcheck = QPixmap(8, 8); 0086 0087 QPainter p; 0088 p.begin(&qcheck); 0089 p.fillRect(0, 0, 4, 4, QColor(144, 144, 144)); 0090 p.fillRect(4, 4, 4, 4, QColor(144, 144, 144)); 0091 p.fillRect(0, 4, 4, 4, QColor(100, 100, 100)); 0092 p.fillRect(4, 0, 4, 4, QColor(100, 100, 100)); 0093 p.end(); 0094 } 0095 0096 return qcheck; 0097 } 0098 0099 uchar* ImageIface::Private::previewImageData() 0100 { 0101 if (previewImage.isNull()) 0102 { 0103 DImg* im = nullptr; 0104 0105 if (previewType == FullImage) 0106 { 0107 im = core->getImg(); 0108 0109 if (!im || im->isNull()) 0110 { 0111 return nullptr; 0112 } 0113 } 0114 else // ImageSelection 0115 { 0116 im = new DImg(core->getImgSelection()); 0117 0118 if (!im) 0119 { 0120 return nullptr; 0121 } 0122 0123 if (im->isNull()) 0124 { 0125 delete im; 0126 return nullptr; 0127 } 0128 0129 im->setIccProfile(core->getEmbeddedICC()); 0130 } 0131 0132 QSize sz(im->width(), im->height()); 0133 sz.scale(constrainWidth, constrainHeight, Qt::KeepAspectRatio); 0134 0135 previewImage = im->smoothScale(sz.width(), sz.height()); 0136 previewWidth = previewImage.width(); 0137 previewHeight = previewImage.height(); 0138 0139 // only create another copy if needed, in setPreviewImage 0140 0141 targetPreviewImage = previewImage; 0142 0143 if (previewType == ImageSelection) 0144 { 0145 delete im; 0146 } 0147 } 0148 0149 DImg previewData = previewImage.copyImageData(); 0150 0151 return previewData.stripImageData(); 0152 } 0153 0154 // ------------------------------------------------------------------------------------------------------ 0155 0156 ImageIface::ImageIface(const QSize& size) 0157 : d(new Private) 0158 { 0159 d->constrainWidth = size.width(); 0160 d->constrainHeight = size.height(); 0161 d->originalWidth = d->core->origWidth(); 0162 d->originalHeight = d->core->origHeight(); 0163 d->originalBytesDepth = d->core->bytesDepth(); 0164 } 0165 0166 ImageIface::~ImageIface() 0167 { 0168 delete d; 0169 } 0170 0171 void ImageIface::setPreviewType(PreviewType type) 0172 { 0173 d->previewType = type; 0174 } 0175 0176 ImageIface::PreviewType ImageIface::previewType() const 0177 { 0178 return d->previewType; 0179 } 0180 0181 DImg *ImageIface::previewReference() 0182 { 0183 return &(d->previewImage); 0184 } 0185 0186 DColor ImageIface::colorInfoFromOriginal(const QPoint& point) const 0187 { 0188 if (!original() || original()->isNull()) 0189 { 0190 qCWarning(DIGIKAM_GENERAL_LOG) << "No image data available!"; 0191 return DColor(); 0192 } 0193 0194 if ((point.x() > originalSize().width()) || (point.y() > originalSize().height())) 0195 { 0196 qCWarning(DIGIKAM_GENERAL_LOG) << "Coordinate out of range!"; 0197 0198 return DColor(); 0199 } 0200 0201 return original()->getPixelColor(point.x(), point.y()); 0202 } 0203 0204 DColor ImageIface::colorInfoFromPreview(const QPoint& point) const 0205 { 0206 if (d->previewImage.isNull() || (point.x() > d->previewWidth) || (point.y() > d->previewHeight)) 0207 { 0208 qCWarning(DIGIKAM_GENERAL_LOG) << "Coordinate out of range or no image data available!"; 0209 0210 return DColor(); 0211 } 0212 0213 return d->previewImage.getPixelColor(point.x(), point.y()); 0214 } 0215 0216 DColor ImageIface::colorInfoFromTargetPreview(const QPoint& point) const 0217 { 0218 if (d->targetPreviewImage.isNull() || (point.x() > d->previewWidth) || (point.y() > d->previewHeight)) 0219 { 0220 qCWarning(DIGIKAM_GENERAL_LOG) << "Coordinate out of range or no image data available!"; 0221 0222 return DColor(); 0223 } 0224 0225 return d->targetPreviewImage.getPixelColor(point.x(), point.y()); 0226 } 0227 0228 DImg ImageIface::setPreviewSize(const QSize& size) const 0229 { 0230 d->previewImage.reset(); 0231 d->targetPreviewImage.reset(); 0232 0233 d->constrainWidth = size.width(); 0234 d->constrainHeight = size.height(); 0235 0236 return preview(); 0237 } 0238 0239 DImg ImageIface::preview() const 0240 { 0241 // NOTE: corrects the values for width and height of the preview image from the image data interface 0242 // See Bug #320382 for details. 0243 0244 uchar* const data = d->previewImageData(); 0245 0246 return DImg(d->previewWidth, d->previewHeight, previewSixteenBit(), previewHasAlpha(), data, false); 0247 } 0248 0249 DImg* ImageIface::original() const 0250 { 0251 return d->core->getImg(); 0252 } 0253 0254 DImg ImageIface::selection() const 0255 { 0256 return d->core->getImgSelection(); 0257 } 0258 0259 void ImageIface::setPreviewIccProfile(const IccProfile& profile) 0260 { 0261 d->targetPreviewImage.detach(); 0262 d->targetPreviewImage.setIccProfile(profile); 0263 } 0264 0265 void ImageIface::crop(const QRect& region) 0266 { 0267 d->core->crop(region); 0268 } 0269 0270 void ImageIface::setOriginalIccProfile(const IccProfile& profile) 0271 { 0272 d->core->putIccProfile(profile); 0273 } 0274 0275 QSize ImageIface::previewSize() const 0276 { 0277 return QSize(d->previewWidth, d->previewHeight); 0278 } 0279 0280 bool ImageIface::previewSixteenBit() const 0281 { 0282 return originalSixteenBit(); 0283 } 0284 0285 bool ImageIface::previewHasAlpha() const 0286 { 0287 return originalHasAlpha(); 0288 } 0289 0290 QSize ImageIface::originalSize() const 0291 { 0292 return QSize(d->core->origWidth(), d->core->origHeight()); 0293 } 0294 0295 bool ImageIface::originalSixteenBit() const 0296 { 0297 return d->core->sixteenBit(); 0298 } 0299 0300 bool ImageIface::originalHasAlpha() const 0301 { 0302 return d->core->hasAlpha(); 0303 } 0304 0305 QRect ImageIface::selectionRect() const 0306 { 0307 return (d->core->getSelectedArea()); 0308 } 0309 0310 void ImageIface::convertOriginalColorDepth(int depth) 0311 { 0312 d->core->convertDepth(depth); 0313 } 0314 0315 QPixmap ImageIface::convertToPixmap(const DImg& img) const 0316 { 0317 return d->core->convertToPixmap(img); 0318 } 0319 0320 IccProfile ImageIface::originalIccProfile() const 0321 { 0322 return d->core->getEmbeddedICC(); 0323 } 0324 0325 MetaEngineData ImageIface::originalMetadata() const 0326 { 0327 DImg* const img = original(); 0328 0329 if (img) 0330 { 0331 return (img->getMetadata()); 0332 } 0333 0334 return MetaEngineData(); 0335 } 0336 0337 void ImageIface::setOriginalMetadata(const MetaEngineData& meta) 0338 { 0339 DImg* const img = original(); 0340 0341 if (img) 0342 { 0343 img->setMetadata(meta); 0344 } 0345 } 0346 0347 PhotoInfoContainer ImageIface::originalPhotoInfo() const 0348 { 0349 return (DMetadata(originalMetadata()).getPhotographInformation()); 0350 } 0351 0352 void ImageIface::paint(QPaintDevice* const device, const QRect& rect, QPainter* const painter) 0353 { 0354 int x = rect.x(); 0355 int y = rect.y(); 0356 int w = rect.width(); 0357 int h = rect.height(); 0358 QPainter* p = nullptr; 0359 QPainter localPainter; 0360 0361 if (painter) 0362 { 0363 p = painter; 0364 } 0365 else 0366 { 0367 p = &localPainter; 0368 p->begin(device); 0369 } 0370 0371 int width = w > 0 ? qMin(d->previewWidth, w) : d->previewWidth; 0372 int height = h > 0 ? qMin(d->previewHeight, h) : d->previewHeight; 0373 0374 if (!d->targetPreviewImage.isNull()) 0375 { 0376 if (d->targetPreviewImage.hasAlpha()) 0377 { 0378 p->drawTiledPixmap(x, y, width, height, d->checkPixmap()); 0379 } 0380 0381 QPixmap pixImage; 0382 bool doSoftProofing = d->core->softProofingEnabled(); 0383 ICCSettingsContainer iccSettings = d->core->getICCSettings(); 0384 0385 if (iccSettings.enableCM && (iccSettings.useManagedView || doSoftProofing)) 0386 { 0387 IccManager manager(d->targetPreviewImage); 0388 IccTransform monitorICCtrans; 0389 0390 if (doSoftProofing) 0391 { 0392 monitorICCtrans = manager.displaySoftProofingTransform(IccProfile(iccSettings.defaultProofProfile)); 0393 } 0394 else 0395 { 0396 monitorICCtrans = manager.displayTransform(); 0397 } 0398 0399 pixImage = d->targetPreviewImage.convertToPixmap(monitorICCtrans); 0400 } 0401 else 0402 { 0403 pixImage = d->targetPreviewImage.convertToPixmap(); 0404 } 0405 0406 p->drawPixmap(x, y, pixImage, 0, 0, width, height); 0407 0408 // Show the Over/Under exposure pixels indicators 0409 0410 ExposureSettingsContainer* const expoSettings = d->core->getExposureSettings(); 0411 0412 if (expoSettings && (expoSettings->underExposureIndicator || expoSettings->overExposureIndicator)) 0413 { 0414 QImage pureColorMask = d->targetPreviewImage.pureColorMask(expoSettings); 0415 QPixmap pixMask = QPixmap::fromImage(pureColorMask); 0416 p->drawPixmap(x, y, pixMask, 0, 0, width, height); 0417 } 0418 } 0419 0420 if (!painter) 0421 { 0422 p->end(); 0423 } 0424 } 0425 0426 void ImageIface::setSelection(const QString& caller, const FilterAction& action, const DImg& img) 0427 { 0428 if ((img.hasAlpha() != originalHasAlpha()) || 0429 (img.sixteenBit() != originalSixteenBit()) || 0430 (img.size() != selectionRect().size()) 0431 ) 0432 { 0433 qCDebug(DIGIKAM_GENERAL_LOG) << "Properties of image to overwrite selection differs than original image"; 0434 return; 0435 } 0436 0437 if (img.isNull()) 0438 { 0439 qCDebug(DIGIKAM_GENERAL_LOG) << "No image data to handle"; 0440 return; 0441 } 0442 0443 d->core->putImgSelection(caller, action, img); 0444 } 0445 0446 void ImageIface::setPreview(const DImg& img) 0447 { 0448 if ((img.hasAlpha() != previewHasAlpha()) || 0449 (img.sixteenBit() != previewSixteenBit()) 0450 ) 0451 { 0452 qCDebug(DIGIKAM_GENERAL_LOG) << "Properties of image differs than preview"; 0453 return; 0454 } 0455 0456 uchar* const data = img.bits(); 0457 0458 if (!data) 0459 { 0460 qCDebug(DIGIKAM_GENERAL_LOG) << "No preview image data to handle"; 0461 return; 0462 } 0463 0464 d->targetPreviewImage.detach(); 0465 d->targetPreviewImage.putImageData(data); 0466 } 0467 0468 void ImageIface::setOriginal(const QString& caller, const FilterAction& action, const DImg& img) 0469 { 0470 if (img.isNull()) 0471 { 0472 qCDebug(DIGIKAM_GENERAL_LOG) << "No image data to handle"; 0473 return; 0474 } 0475 0476 d->core->putImg(caller, action, img); 0477 } 0478 0479 } // namespace Digikam