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

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2006-01-10
0007  * Description : a widget to display CIE tongue from an ICC profile.
0008  *
0009  * SPDX-FileCopyrightText: 2006-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  *
0011  * Any source code are inspired from lprof project and
0012  * SPDX-FileCopyrightText: 1998-2001 Marti Maria <info at littlecms dot com>
0013  *
0014  * SPDX-License-Identifier: GPL-2.0-or-later
0015  *
0016  * ============================================================ */
0017 
0018 #include "cietonguewidget.h"
0019 
0020 // C++ includes
0021 
0022 #include <cmath>
0023 
0024 // Qt includes
0025 
0026 #include <QImage>
0027 #include <QPainter>
0028 #include <QPixmap>
0029 #include <QFile>
0030 #include <QTimer>
0031 
0032 // KDE includes
0033 
0034 #include <klocalizedstring.h>
0035 
0036 // Local includes
0037 
0038 #include "digikam_config.h"
0039 #include "digikam_debug.h"
0040 #include "iccprofile.h"
0041 #include "dlayoutbox.h"
0042 #include "dworkingpixmap.h"
0043 
0044 namespace Digikam
0045 {
0046 
0047 /**
0048  * The following table gives the CIE color matching functions
0049  * \f$\bar{x}(\lambda)\f$, \f$\bar{y}(\lambda)\f$, and
0050  * \f$\bar{z}(\lambda)\f$, for wavelengths \f$\lambda\f$ at 5 nanometer
0051  * increments from 380 nm through 780 nm. This table is used in conjunction
0052  * with Planck's law for the energy spectrum of a black body at a given
0053  * temperature to plot the black body curve on the CIE chart.
0054  *
0055  * The following table gives the spectral chromaticity co-ordinates
0056  * \f$x(\lambda)\f$ and \f$y(\lambda)\f$ for wavelengths in 5 nanometer
0057  * increments from 380 nm through 780 nm. These coordinates represent the
0058  * position in the CIE x-y space of pure spectral colors of the given
0059  * wavelength, and thus define the outline of the CIE "tongue" diagram.
0060  */
0061 static const double spectral_chromaticity[81][3] =
0062 {
0063     { 0.1741, 0.0050 },               // 380 nm
0064     { 0.1740, 0.0050 },
0065     { 0.1738, 0.0049 },
0066     { 0.1736, 0.0049 },
0067     { 0.1733, 0.0048 },
0068     { 0.1730, 0.0048 },
0069     { 0.1726, 0.0048 },
0070     { 0.1721, 0.0048 },
0071     { 0.1714, 0.0051 },
0072     { 0.1703, 0.0058 },
0073     { 0.1689, 0.0069 },
0074     { 0.1669, 0.0086 },
0075     { 0.1644, 0.0109 },
0076     { 0.1611, 0.0138 },
0077     { 0.1566, 0.0177 },
0078     { 0.1510, 0.0227 },
0079     { 0.1440, 0.0297 },
0080     { 0.1355, 0.0399 },
0081     { 0.1241, 0.0578 },
0082     { 0.1096, 0.0868 },
0083     { 0.0913, 0.1327 },
0084     { 0.0687, 0.2007 },
0085     { 0.0454, 0.2950 },
0086     { 0.0235, 0.4127 },
0087     { 0.0082, 0.5384 },
0088     { 0.0039, 0.6548 },
0089     { 0.0139, 0.7502 },
0090     { 0.0389, 0.8120 },
0091     { 0.0743, 0.8338 },
0092     { 0.1142, 0.8262 },
0093     { 0.1547, 0.8059 },
0094     { 0.1929, 0.7816 },
0095     { 0.2296, 0.7543 },
0096     { 0.2658, 0.7243 },
0097     { 0.3016, 0.6923 },
0098     { 0.3373, 0.6589 },
0099     { 0.3731, 0.6245 },
0100     { 0.4087, 0.5896 },
0101     { 0.4441, 0.5547 },
0102     { 0.4788, 0.5202 },
0103     { 0.5125, 0.4866 },
0104     { 0.5448, 0.4544 },
0105     { 0.5752, 0.4242 },
0106     { 0.6029, 0.3965 },
0107     { 0.6270, 0.3725 },
0108     { 0.6482, 0.3514 },
0109     { 0.6658, 0.3340 },
0110     { 0.6801, 0.3197 },
0111     { 0.6915, 0.3083 },
0112     { 0.7006, 0.2993 },
0113     { 0.7079, 0.2920 },
0114     { 0.7140, 0.2859 },
0115     { 0.7190, 0.2809 },
0116     { 0.7230, 0.2770 },
0117     { 0.7260, 0.2740 },
0118     { 0.7283, 0.2717 },
0119     { 0.7300, 0.2700 },
0120     { 0.7311, 0.2689 },
0121     { 0.7320, 0.2680 },
0122     { 0.7327, 0.2673 },
0123     { 0.7334, 0.2666 },
0124     { 0.7340, 0.2660 },
0125     { 0.7344, 0.2656 },
0126     { 0.7346, 0.2654 },
0127     { 0.7347, 0.2653 },
0128     { 0.7347, 0.2653 },
0129     { 0.7347, 0.2653 },
0130     { 0.7347, 0.2653 },
0131     { 0.7347, 0.2653 },
0132     { 0.7347, 0.2653 },
0133     { 0.7347, 0.2653 },
0134     { 0.7347, 0.2653 },
0135     { 0.7347, 0.2653 },
0136     { 0.7347, 0.2653 },
0137     { 0.7347, 0.2653 },
0138     { 0.7347, 0.2653 },
0139     { 0.7347, 0.2653 },
0140     { 0.7347, 0.2653 },
0141     { 0.7347, 0.2653 },
0142     { 0.7347, 0.2653 },
0143     { 0.7347, 0.2653 }  // 780 nm
0144 };
0145 
0146 class Q_DECL_HIDDEN CIETongueWidget::Private
0147 {
0148 public:
0149 
0150     explicit Private()
0151       : profileDataAvailable(true),
0152         loadingImageMode    (false),
0153         loadingImageSucess  (false),
0154         needUpdatePixmap    (false),
0155         uncalibratedColor   (false),
0156         xBias               (0),
0157         yBias               (0),
0158         pxcols              (0),
0159         pxrows              (0),
0160         progressCount       (0),
0161         gridside            (0),
0162         progressTimer       (nullptr),
0163         progressPix         (nullptr),
0164         hMonitorProfile     (nullptr),
0165         hXFORM              (nullptr)
0166     {
0167     }
0168 
0169     bool                         profileDataAvailable;
0170     bool                         loadingImageMode;
0171     bool                         loadingImageSucess;
0172     bool                         needUpdatePixmap;
0173     bool                         uncalibratedColor;
0174 
0175     int                          xBias;
0176     int                          yBias;
0177     int                          pxcols;
0178     int                          pxrows;
0179     int                          progressCount;           ///< Position of animation during loading/calculation.
0180 
0181     double                       gridside;
0182 
0183     QPainter                     painter;
0184 
0185     QTimer*                      progressTimer;
0186 
0187     QPixmap                      pixmap;
0188     DWorkingPixmap*              progressPix;
0189 
0190     cmsHPROFILE                  hMonitorProfile;
0191     cmsHTRANSFORM                hXFORM;
0192     cmsCIExyYTRIPLE              Primaries;
0193     cmsCIEXYZ                    MediaWhite;
0194 };
0195 
0196 CIETongueWidget::CIETongueWidget(int w, int h, QWidget* const parent, cmsHPROFILE hMonitor)
0197     : QWidget(parent),
0198       d      (new Private)
0199 {
0200     cmsHPROFILE hXYZProfile;
0201     d->progressTimer = new QTimer(this);
0202     d->progressPix   = new DWorkingPixmap(this);
0203     setMinimumSize(w, h);
0204     setAttribute(Qt::WA_DeleteOnClose);
0205     dkCmsErrorAction(LCMS_ERROR_SHOW);
0206 
0207     if (hMonitor)
0208     {
0209         d->hMonitorProfile = hMonitor;
0210     }
0211     else
0212     {
0213         d->hMonitorProfile = dkCmsCreate_sRGBProfile();
0214     }
0215 
0216     hXYZProfile = dkCmsCreateXYZProfile();
0217 
0218     if (hXYZProfile == nullptr)
0219     {
0220         return;
0221     }
0222 
0223     d->hXFORM = dkCmsCreateTransform(hXYZProfile, TYPE_XYZ_16,
0224                                      d->hMonitorProfile, TYPE_RGB_8,
0225                                      INTENT_PERCEPTUAL, 0);
0226 
0227     dkCmsCloseProfile(hXYZProfile);
0228 
0229     if (d->hXFORM == nullptr)
0230     {
0231         qCDebug(DIGIKAM_WIDGETS_LOG) << "Wrong d->hXFORM" ;
0232     }
0233 
0234     connect(d->progressTimer, SIGNAL(timeout()),
0235             this, SLOT(slotProgressTimerDone()));
0236 }
0237 
0238 CIETongueWidget::~CIETongueWidget()
0239 {
0240     dkCmsDeleteTransform(d->hXFORM);
0241     dkCmsCloseProfile(d->hMonitorProfile);
0242     delete d;
0243 }
0244 
0245 int CIETongueWidget::grids(double val) const
0246 {
0247     return ((int)floor(val * d->gridside + 0.5));
0248 }
0249 
0250 bool CIETongueWidget::setProfileData(const QByteArray& profileData)
0251 {
0252     if (!profileData.isEmpty())
0253     {
0254         LcmsLock lock;
0255         cmsHPROFILE hProfile = dkCmsOpenProfileFromMem((void*)profileData.data(), (DWORD)profileData.size());
0256 
0257         if (!hProfile)
0258         {
0259             d->profileDataAvailable = false;
0260             d->loadingImageSucess   = false;
0261         }
0262         else
0263         {
0264             setProfile(hProfile);
0265             dkCmsCloseProfile(hProfile);
0266             d->profileDataAvailable = true;
0267             d->loadingImageSucess   = true;
0268         }
0269     }
0270     else
0271     {
0272         d->profileDataAvailable = false;
0273         d->loadingImageSucess   = false;
0274     }
0275 
0276     d->loadingImageMode  = false;
0277     d->uncalibratedColor = false;
0278 
0279     d->progressTimer->stop();
0280     d->needUpdatePixmap  = true;
0281     update();
0282 
0283     return (d->profileDataAvailable);
0284 }
0285 
0286 bool CIETongueWidget::setProfileFromFile(const QUrl& file)
0287 {
0288     if (!file.isEmpty() && file.isValid())
0289     {
0290         LcmsLock lock;
0291         cmsHPROFILE hProfile = dkCmsOpenProfileFromFile(QFile::encodeName(file.toLocalFile()).constData(), "r");
0292 
0293         if (!hProfile)
0294         {
0295             d->profileDataAvailable = false;
0296             d->loadingImageSucess   = false;
0297         }
0298         else
0299         {
0300             setProfile(hProfile);
0301             dkCmsCloseProfile(hProfile);
0302             d->profileDataAvailable = true;
0303             d->loadingImageSucess   = true;
0304         }
0305     }
0306     else
0307     {
0308         d->profileDataAvailable = false;
0309         d->loadingImageSucess   = false;
0310     }
0311 
0312     d->loadingImageMode  = false;
0313     d->uncalibratedColor = false;
0314 
0315     d->progressTimer->stop();
0316     d->needUpdatePixmap  = true;
0317     update();
0318 
0319     return (d->profileDataAvailable);
0320 }
0321 
0322 void CIETongueWidget::setProfile(cmsHPROFILE hProfile)
0323 {
0324     // Get the white point.
0325 
0326     dkCmsTakeMediaWhitePoint(&(d->MediaWhite), hProfile);
0327     cmsCIExyY White;
0328     dkCmsXYZ2xyY(&White, &(d->MediaWhite));
0329     qCDebug(DIGIKAM_WIDGETS_LOG) << "Profile white point : x=" << White.x << " y=" << White.y << " Y=" << White.Y;
0330 
0331     // Get the colorant matrix.
0332 
0333     memset(&(d->Primaries),0,sizeof(cmsCIExyYTRIPLE));
0334 
0335     if (dkCmsIsTag(hProfile, icSigRedColorantTag)   &&
0336         dkCmsIsTag(hProfile, icSigGreenColorantTag) &&
0337         dkCmsIsTag(hProfile, icSigBlueColorantTag))
0338     {
0339         MAT3 Mat;
0340 
0341         if (dkCmsReadICCMatrixRGB2XYZ(&Mat, hProfile))
0342         {
0343 
0344 #if defined(USE_LCMS_VERSION_1000)
0345 
0346             qCDebug(DIGIKAM_WIDGETS_LOG) << "dkCmsReadICCMatrixRGB2XYZ(1): " \
0347             << "[" << Mat.v[0].n[0] << ", " << Mat.v[0].n[1] << ", " << Mat.v[0].n[2] << "]" \
0348             << "[" << Mat.v[1].n[0] << ", " << Mat.v[1].n[1] << ", " << Mat.v[1].n[2] << "]" \
0349             << "[" << Mat.v[2].n[0] << ", " << Mat.v[2].n[1] << ", " << Mat.v[2].n[2] << "]" ;
0350 
0351 #else
0352 
0353             qCDebug(DIGIKAM_WIDGETS_LOG) << "dkCmsReadICCMatrixRGB2XYZ(2): " \
0354             << "[" << Mat.Red.X << ", " << Mat.Red.Y << ", " << Mat.Red.Z << "]" \
0355             << "[" << Mat.Green.X << ", " << Mat.Green.Y << ", " << Mat.Green.Z << "]" \
0356             << "[" << Mat.Blue.X << ", " << Mat.Blue.Y << ", " << Mat.Blue.Z << "]" ;
0357 
0358 #endif
0359 
0360             // Undo chromatic adaptation
0361 
0362             if (dkCmsAdaptMatrixFromD50(&Mat, &White))
0363             {
0364 
0365 #if defined(USE_LCMS_VERSION_1000)
0366 
0367                 cmsCIEXYZ tmp;
0368 
0369                 tmp.X = Mat.v[0].n[0];
0370                 tmp.Y = Mat.v[1].n[0];
0371                 tmp.Z = Mat.v[2].n[0];
0372                 qCDebug(DIGIKAM_WIDGETS_LOG) << "d->Primaries.Red   : X=" << tmp.X << " Y=" << tmp.Y << " Z=" << tmp.Z;
0373 /*
0374                 ScaleToWhite(&MediaWhite, &tmp);
0375 */
0376                 dkCmsXYZ2xyY(&(d->Primaries.Red), &tmp);
0377 
0378                 tmp.X = Mat.v[0].n[1];
0379                 tmp.Y = Mat.v[1].n[1];
0380                 tmp.Z = Mat.v[2].n[1];
0381                 qCDebug(DIGIKAM_WIDGETS_LOG) << "d->Primaries.Green : X=" << tmp.X << " Y=" << tmp.Y << " Z=" << tmp.Z;
0382                 // ScaleToWhite(&MediaWhite, &tmp);
0383                 dkCmsXYZ2xyY(&(d->Primaries.Green), &tmp);
0384 
0385                 tmp.X = Mat.v[0].n[2];
0386                 tmp.Y = Mat.v[1].n[2];
0387                 tmp.Z = Mat.v[2].n[2];
0388                 qCDebug(DIGIKAM_WIDGETS_LOG) << "d->Primaries.Blue  : X=" << tmp.X << " Y=" << tmp.Y << " Z=" << tmp.Z;
0389 /*
0390                 ScaleToWhite(&MediaWhite, &tmp);
0391 */
0392                 dkCmsXYZ2xyY(&(d->Primaries.Blue), &tmp);
0393 
0394 #else
0395 
0396                 cmsCIEXYZ tmp;
0397 
0398                 tmp.X = Mat.Red.X;
0399                 tmp.Y = Mat.Green.X;
0400                 tmp.Z = Mat.Blue.X;
0401                 qCDebug(DIGIKAM_WIDGETS_LOG) << "d->Primaries.Red   : X=" << tmp.X << " Y=" << tmp.Y << " Z=" << tmp.Z;
0402 /*
0403                 ScaleToWhite(&MediaWhite, &tmp);
0404 */
0405                 dkCmsXYZ2xyY(&(d->Primaries.Red), &tmp);
0406 
0407                 tmp.X = Mat.Red.Y;
0408                 tmp.Y = Mat.Green.Y;
0409                 tmp.Z = Mat.Blue.Y;
0410                 qCDebug(DIGIKAM_WIDGETS_LOG) << "d->Primaries.Green : X=" << tmp.X << " Y=" << tmp.Y << " Z=" << tmp.Z;
0411 /*
0412                 ScaleToWhite(&MediaWhite, &tmp);
0413 */
0414                 dkCmsXYZ2xyY(&(d->Primaries.Green), &tmp);
0415 
0416                 tmp.X = Mat.Red.Z;
0417                 tmp.Y = Mat.Green.Z;
0418                 tmp.Z = Mat.Blue.Z;
0419                 qCDebug(DIGIKAM_WIDGETS_LOG) << "d->Primaries.Blue  : X=" << tmp.X << " Y=" << tmp.Y << " Z=" << tmp.Z;
0420 /*
0421                 ScaleToWhite(&MediaWhite, &tmp);
0422 */
0423                 dkCmsXYZ2xyY(&(d->Primaries.Blue), &tmp);
0424 
0425 #endif
0426 
0427             }
0428         }
0429     }
0430 }
0431 
0432 void CIETongueWidget::mapPoint(int& icx, int& icy, LPcmsCIExyY xyY)
0433 {
0434     icx = (int) floor((xyY->x * (d->pxcols - 1)) + .5);
0435     icy = (int) floor(((d->pxrows - 1) - xyY->y * (d->pxrows - 1)) + .5);
0436 }
0437 
0438 void CIETongueWidget::biasedLine(int x1, int y1, int x2, int y2)
0439 {
0440     d->painter.drawLine(x1 + d->xBias, y1, x2 + d->xBias, y2);
0441 }
0442 
0443 void CIETongueWidget::biasedText(int x, int y, const QString& txt)
0444 {
0445     d->painter.drawText(QPoint(d->xBias + x, y), txt);
0446 }
0447 
0448 QRgb CIETongueWidget::colorByCoord(double x, double y)
0449 {
0450     // Get xyz components scaled from coordinates
0451 
0452     double cx =       ((double) x) / (d->pxcols - 1);
0453     double cy = 1.0 - ((double) y) / (d->pxrows - 1);
0454     double cz = 1.0 - cx - cy;
0455 
0456     // Project xyz to XYZ space. Note that in this
0457     // particular case we are substituting XYZ with xyz
0458 
0459     cmsCIEXYZ XYZ = { cx , cy , cz };
0460 
0461     WORD XYZW[3];
0462     BYTE RGB[3];
0463 
0464     dkCmsFloat2XYZEncoded(XYZW, &XYZ);
0465     dkCmsDoTransform(d->hXFORM, XYZW, RGB, 1);
0466 
0467     return qRgb(RGB[0], RGB[1], RGB[2]);
0468 }
0469 
0470 void CIETongueWidget::outlineTongue()
0471 {
0472     int lx=0, ly=0;
0473     int fx=0, fy=0;
0474 
0475     for (int x = 380 ; x <= 700 ; x += 5)
0476     {
0477         int ix = (x - 380) / 5;
0478 
0479         cmsCIExyY p = {
0480                        spectral_chromaticity[ix][0],
0481                        spectral_chromaticity[ix][1], 1
0482                       };
0483 
0484         int icx, icy;
0485         mapPoint(icx, icy, &p);
0486 
0487         if (x > 380)
0488         {
0489             biasedLine(lx, ly, icx, icy);
0490         }
0491         else
0492         {
0493             fx = icx;
0494             fy = icy;
0495         }
0496 
0497         lx = icx;
0498         ly = icy;
0499     }
0500 
0501     biasedLine(lx, ly, fx, fy);
0502 }
0503 
0504 void CIETongueWidget::fillTongue()
0505 {
0506     QImage Img = d->pixmap.toImage();
0507 
0508     int x;
0509 
0510     for (int y = 0 ; y < d->pxrows ; ++y)
0511     {
0512         int xe = 0;
0513 
0514         // Find horizontal extents of tongue on this line.
0515 
0516         for (x = 0; x < d->pxcols; ++x)
0517         {
0518             if (QColor(Img.pixel(x + d->xBias, y)) != QColor(Qt::black))
0519             {
0520                 for (xe = d->pxcols - 1 ; xe >= x ; --xe)
0521                 {
0522                     if (QColor(Img.pixel(xe + d->xBias, y)) != QColor(Qt::black))
0523                     {
0524                         break;
0525                     }
0526                 }
0527 
0528                 break;
0529             }
0530         }
0531 
0532         if (x < d->pxcols)
0533         {
0534             for ( ; x <= xe ; ++x)
0535             {
0536                 QRgb Color = colorByCoord(x, y);
0537                 Img.setPixel(x + d->xBias, y, Color);
0538             }
0539         }
0540     }
0541 
0542     d->pixmap = QPixmap::fromImage(Img, Qt::AvoidDither);
0543 }
0544 
0545 void CIETongueWidget::drawTongueAxis()
0546 {
0547     QFont font;
0548     font.setPointSize(6);
0549     d->painter.setFont(font);
0550 
0551     d->painter.setPen(qRgb(255, 255, 255));
0552 
0553     biasedLine(0, 0,           0,           d->pxrows - 1);
0554     biasedLine(0, d->pxrows-1, d->pxcols-1, d->pxrows - 1);
0555 
0556     for (int y = 1 ; y <= 9 ; y += 1)
0557     {
0558         QString s;
0559         int xstart = (y * (d->pxcols - 1)) / 10;
0560         int ystart = (y * (d->pxrows - 1)) / 10;
0561 
0562         s = QString().asprintf("0.%d", y);
0563         biasedLine(xstart, d->pxrows - grids(1), xstart,   d->pxrows - grids(4));
0564         biasedText(xstart - grids(11), d->pxrows + grids(15), s);
0565 
0566         s = QString().asprintf("0.%d", 10 - y);
0567         biasedLine(0, ystart, grids(3), ystart);
0568         biasedText(grids(-25), ystart + grids(5), s);
0569     }
0570 }
0571 
0572 void CIETongueWidget::drawTongueGrid()
0573 {
0574     d->painter.setPen(qRgb(80, 80, 80));
0575 
0576     for (int y = 1 ; y <= 9 ; y += 1)
0577     {
0578         int xstart =  (y * (d->pxcols - 1)) / 10;
0579         int ystart =  (y * (d->pxrows - 1)) / 10;
0580 
0581         biasedLine(xstart, grids(4), xstart,   d->pxrows - grids(4) - 1);
0582         biasedLine(grids(7), ystart, d->pxcols-1-grids(7), ystart);
0583     }
0584 }
0585 
0586 void CIETongueWidget::drawLabels()
0587 {
0588     QFont font;
0589     font.setPointSize(5);
0590     d->painter.setFont(font);
0591 
0592     for (int x = 450 ; x <= 650 ; x += (x > 470 && x < 600) ? 5 : 10)
0593     {
0594         QString wl;
0595         int bx = 0, by = 0, tx, ty;
0596 
0597         if      (x < 520)
0598         {
0599             bx = grids(-22);
0600             by = grids(2);
0601         }
0602         else if (x < 535)
0603         {
0604             bx = grids(-8);
0605             by = grids(-6);
0606         }
0607         else
0608         {
0609             bx = grids(4);
0610         }
0611 
0612         int ix = (x - 380) / 5;
0613 
0614         cmsCIExyY p = {
0615                        spectral_chromaticity[ix][0],
0616                        spectral_chromaticity[ix][1], 1
0617                       };
0618 
0619         int icx, icy;
0620         mapPoint(icx, icy, &p);
0621 
0622         tx = icx + ((x < 520) ? grids(-2) : ((x >= 535) ? grids(2) : 0));
0623         ty = icy + ((x < 520) ? 0 : ((x >= 535) ? grids(-1) : grids(-2)));
0624 
0625         d->painter.setPen(qRgb(255, 255, 255));
0626         biasedLine(icx, icy, tx, ty);
0627 
0628         QRgb Color = colorByCoord(icx, icy);
0629         d->painter.setPen(Color);
0630 
0631         wl = QString().asprintf("%d", x);
0632         biasedText(icx+bx, icy+by, wl);
0633     }
0634 }
0635 
0636 void CIETongueWidget::drawSmallElipse(LPcmsCIExyY xyY, BYTE r, BYTE g, BYTE b, int sz)
0637 {
0638     int icx, icy;
0639 
0640     mapPoint(icx, icy, xyY);
0641     d->painter.setPen(qRgb(r, g, b));
0642     d->painter.drawEllipse(icx + d->xBias- sz/2, icy-sz/2, sz, sz);
0643 }
0644 
0645 void CIETongueWidget::drawColorantTriangle()
0646 {
0647     drawSmallElipse(&(d->Primaries.Red),   255, 128, 128, 6);
0648     drawSmallElipse(&(d->Primaries.Green), 128, 255, 128, 6);
0649     drawSmallElipse(&(d->Primaries.Blue),  128, 128, 255, 6);
0650 
0651     int x1, y1, x2, y2, x3, y3;
0652 
0653     mapPoint(x1, y1, &(d->Primaries.Red));
0654     mapPoint(x2, y2, &(d->Primaries.Green));
0655     mapPoint(x3, y3, &(d->Primaries.Blue));
0656 
0657     d->painter.setPen(qRgb(255, 255, 255));
0658 
0659     biasedLine(x1, y1, x2, y2);
0660     biasedLine(x2, y2, x3, y3);
0661     biasedLine(x3, y3, x1, y1);
0662 }
0663 
0664 void CIETongueWidget::drawWhitePoint()
0665 {
0666     cmsCIExyY Whitem_pntxyY;
0667     dkCmsXYZ2xyY(&Whitem_pntxyY, &(d->MediaWhite));
0668     drawSmallElipse(&Whitem_pntxyY,  255, 255, 255, 8);
0669 }
0670 
0671 void CIETongueWidget::loadingStarted()
0672 {
0673     d->progressCount      = 0;
0674     d->loadingImageMode   = true;
0675     d->loadingImageSucess = false;
0676     update();
0677     d->progressTimer->start(200);
0678 }
0679 
0680 void CIETongueWidget::loadingFailed()
0681 {
0682     d->progressTimer->stop();
0683     d->progressCount      = 0;
0684     d->loadingImageMode   = false;
0685     d->loadingImageSucess = false;
0686     update();
0687 }
0688 
0689 void CIETongueWidget::uncalibratedColor()
0690 {
0691     d->progressTimer->stop();
0692     d->progressCount      = 0;
0693     d->loadingImageMode   = false;
0694     d->loadingImageSucess = false;
0695     d->uncalibratedColor  = true;
0696     update();
0697 }
0698 
0699 void CIETongueWidget::updatePixmap()
0700 {
0701     d->needUpdatePixmap = false;
0702     d->pixmap           = QPixmap(size());
0703 
0704     // Draw the CIE tongue curve.
0705 
0706     d->pixmap.fill(Qt::black);
0707     d->painter.begin(&d->pixmap);
0708 
0709     int pixcols = d->pixmap.width();
0710     int pixrows = d->pixmap.height();
0711 
0712     d->gridside = (qMin(pixcols, pixrows)) / 512.0;
0713     d->xBias    = grids(32);
0714     d->yBias    = grids(20);
0715     d->pxcols   = pixcols - d->xBias;
0716     d->pxrows   = pixrows - d->yBias;
0717 
0718     d->painter.setBackground(QBrush(qRgb(0, 0, 0)));
0719     d->painter.setPen(qRgb(255, 255, 255));
0720 
0721     outlineTongue();
0722     d->painter.end();
0723 
0724     fillTongue();
0725 
0726     d->painter.begin(&d->pixmap);
0727     drawTongueAxis();
0728     drawLabels();
0729     drawTongueGrid();
0730 
0731     if (d->MediaWhite.Y > 0.0)
0732     {
0733         drawWhitePoint();
0734     }
0735 
0736     if (d->Primaries.Red.Y != 0.0)
0737     {
0738         drawColorantTriangle();
0739     }
0740 
0741     d->painter.end();
0742 }
0743 
0744 void CIETongueWidget::paintEvent(QPaintEvent*)
0745 {
0746     QPainter p(this);
0747 
0748     // Widget is disable : drawing grayed frame.
0749 
0750     if (!isEnabled())
0751     {
0752         p.fillRect(0, 0, width(), height(),
0753                    palette().color(QPalette::Disabled, QPalette::Window));
0754 
0755         QPen pen(palette().color(QPalette::Disabled, QPalette::WindowText));
0756         pen.setStyle(Qt::SolidLine);
0757         pen.setWidth(1);
0758 
0759         p.setPen(pen);
0760         p.drawRect(0, 0, width(), height());
0761 
0762         return;
0763     }
0764 
0765     // Loading image mode.
0766 
0767     if (d->loadingImageMode && !d->loadingImageSucess)
0768     {
0769         // In first, we draw an animation.
0770 
0771         QPixmap anim(d->progressPix->frameAt(d->progressCount));
0772         d->progressCount++;
0773 
0774         if (d->progressCount >= d->progressPix->frameCount())
0775         {
0776             d->progressCount = 0;
0777         }
0778 
0779         // ... and we render busy text.
0780 
0781         p.fillRect(0, 0, width(), height(), palette().color(QPalette::Active, QPalette::Window));
0782         p.drawPixmap(width()/2 - anim.width() /2, anim.height(), anim);
0783         QPen pen(palette().color(QPalette::Active, QPalette::Text));
0784         pen.setStyle(Qt::SolidLine);
0785         pen.setWidth(1);
0786 
0787         p.setPen(pen);
0788         p.drawRect(0, 0, width(), height());
0789         p.drawText(0, 0, width(), height(), Qt::AlignCenter,
0790                    i18n("Loading image..."));
0791 
0792         return;
0793     }
0794 
0795     // No profile data to show, or RAW file
0796 
0797     if (!d->profileDataAvailable || (!d->loadingImageMode && !d->loadingImageSucess))
0798     {
0799         p.fillRect(0, 0, width(), height(), palette().color(QPalette::Active, QPalette::Window));
0800         QPen pen(palette().color(QPalette::Active, QPalette::Text));
0801         pen.setStyle(Qt::SolidLine);
0802         pen.setWidth(1);
0803 
0804         p.setPen(pen);
0805         p.drawRect(0, 0, width(), height());
0806 
0807         if (d->uncalibratedColor)
0808         {
0809             p.drawText(0, 0, width(), height(), Qt::AlignCenter,
0810                        i18n("Uncalibrated color space"));
0811         }
0812         else
0813         {
0814             p.setPen(Qt::red);
0815             p.drawText(0, 0, width(), height(), Qt::AlignCenter,
0816                        i18n("No profile available..."));
0817         }
0818 
0819         return;
0820     }
0821 
0822     // Create CIE tongue if needed
0823 
0824     if (d->needUpdatePixmap)
0825     {
0826         updatePixmap();
0827     }
0828 
0829     // draw prerendered tongue
0830 
0831     p.drawPixmap(0, 0, d->pixmap);
0832 }
0833 
0834 void CIETongueWidget::resizeEvent(QResizeEvent* event)
0835 {
0836     Q_UNUSED(event);
0837     setMinimumHeight(width());
0838     d->needUpdatePixmap = true;
0839 }
0840 
0841 void CIETongueWidget::slotProgressTimerDone()
0842 {
0843     update();
0844     d->progressTimer->start(200);
0845 }
0846 
0847 } // namespace Digikam
0848 
0849 #include "moc_cietonguewidget.cpp"