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