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"