diff options
Diffstat (limited to 'src/libs/widgets/iccprofiles/cietonguewidget.cpp')
-rw-r--r-- | src/libs/widgets/iccprofiles/cietonguewidget.cpp | 816 |
1 files changed, 816 insertions, 0 deletions
diff --git a/src/libs/widgets/iccprofiles/cietonguewidget.cpp b/src/libs/widgets/iccprofiles/cietonguewidget.cpp new file mode 100644 index 00000000..2ec738b3 --- /dev/null +++ b/src/libs/widgets/iccprofiles/cietonguewidget.cpp @@ -0,0 +1,816 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2006-01-10 + * Description : a widget to display CIE tongue from + * an ICC profile. + * + * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com> + * + * Any source code are inspired from lprof project and + * Copyright (C) 1998-2001 Marti Maria + * + * This program is free software; you can redistribute it + * and/or modify it under the terms of the GNU General + * Public License as published by the Free Software Foundation; + * either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * ============================================================ */ + +// C++ includes. + +#include <cmath> + +// TQt includes. + +#include <tqimage.h> +#include <tqpainter.h> +#include <tqpixmap.h> +#include <tqfile.h> +#include <tqtimer.h> + +// KDE includes. + +#include <tdelocale.h> + +// Local includes. + +#include "ddebug.h" +#include "lcmsprf.h" +#include "cietonguewidget.h" +#include "cietonguewidget.moc" + +namespace Digikam +{ + + // The following table gives the CIE colour matching functions + // \bar{x}(\lambda), \bar{y}(\lambda), and \bar{z}(\lambda), for + // wavelengths \lambda at 5 nanometre increments from 380 nm through + // 780 nm. This table is used in conjunction with Planck's law for + // the energy spectrum of a black body at a given temperature to plot + // the black body curve on the CIE chart. + + // The following table gives the spectral chromaticity co-ordinates + // x(\lambda) and y(\lambda) for wavelengths in 5 nanometre increments + // from 380 nm through 780 nm. These co-ordinates represent the + // position in the CIE x-y space of pure spectral colours of the given + // wavelength, and thus define the outline of the CIE "tongue" + // diagram. + + static const double spectral_chromaticity[81][3] = + { + { 0.1741, 0.0050 }, // 380 nm + { 0.1740, 0.0050 }, + { 0.1738, 0.0049 }, + { 0.1736, 0.0049 }, + { 0.1733, 0.0048 }, + { 0.1730, 0.0048 }, + { 0.1726, 0.0048 }, + { 0.1721, 0.0048 }, + { 0.1714, 0.0051 }, + { 0.1703, 0.0058 }, + { 0.1689, 0.0069 }, + { 0.1669, 0.0086 }, + { 0.1644, 0.0109 }, + { 0.1611, 0.0138 }, + { 0.1566, 0.0177 }, + { 0.1510, 0.0227 }, + { 0.1440, 0.0297 }, + { 0.1355, 0.0399 }, + { 0.1241, 0.0578 }, + { 0.1096, 0.0868 }, + { 0.0913, 0.1327 }, + { 0.0687, 0.2007 }, + { 0.0454, 0.2950 }, + { 0.0235, 0.4127 }, + { 0.0082, 0.5384 }, + { 0.0039, 0.6548 }, + { 0.0139, 0.7502 }, + { 0.0389, 0.8120 }, + { 0.0743, 0.8338 }, + { 0.1142, 0.8262 }, + { 0.1547, 0.8059 }, + { 0.1929, 0.7816 }, + { 0.2296, 0.7543 }, + { 0.2658, 0.7243 }, + { 0.3016, 0.6923 }, + { 0.3373, 0.6589 }, + { 0.3731, 0.6245 }, + { 0.4087, 0.5896 }, + { 0.4441, 0.5547 }, + { 0.4788, 0.5202 }, + { 0.5125, 0.4866 }, + { 0.5448, 0.4544 }, + { 0.5752, 0.4242 }, + { 0.6029, 0.3965 }, + { 0.6270, 0.3725 }, + { 0.6482, 0.3514 }, + { 0.6658, 0.3340 }, + { 0.6801, 0.3197 }, + { 0.6915, 0.3083 }, + { 0.7006, 0.2993 }, + { 0.7079, 0.2920 }, + { 0.7140, 0.2859 }, + { 0.7190, 0.2809 }, + { 0.7230, 0.2770 }, + { 0.7260, 0.2740 }, + { 0.7283, 0.2717 }, + { 0.7300, 0.2700 }, + { 0.7311, 0.2689 }, + { 0.7320, 0.2680 }, + { 0.7327, 0.2673 }, + { 0.7334, 0.2666 }, + { 0.7340, 0.2660 }, + { 0.7344, 0.2656 }, + { 0.7346, 0.2654 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 }, + { 0.7347, 0.2653 } // 780 nm + }; + +class CIETongueWidgetPriv +{ +public: + + CIETongueWidgetPriv() + { + hMonitorProfile = 0; + hXYZProfile = 0; + hXFORM = 0; + + Measurement.Patches = 0; + Measurement.Allowed = 0; + blinkTimer = 0; + pos = 0; + + profileDataAvailable = true; + loadingImageMode = false; + loadingImageSucess = false; + } + + bool profileDataAvailable; + bool loadingImageMode; + bool loadingImageSucess; + + double gridside; + + int xBias; + int yBias; + int pxcols; + int pxrows; + int pos; // Position of animation during loading/calculation. + + TQPainter painter; + TQPixmap pixmap; + TQTimer *blinkTimer; + + cmsHPROFILE hMonitorProfile; + cmsHPROFILE hXYZProfile; + cmsHTRANSFORM hXFORM; + cmsCIExyYTRIPLE Primaries; + cmsCIEXYZ MediaWhite; + + MEASUREMENT Measurement; +}; + +CIETongueWidget::CIETongueWidget(int w, int h, TQWidget *parent, cmsHPROFILE hMonitor) + : TQWidget(parent, 0, TQt::WDestructiveClose) +{ + d = new CIETongueWidgetPriv; + d->blinkTimer = new TQTimer( this ); + setMinimumSize(w, h); + cmsErrorAction(LCMS_ERROR_SHOW); + + if (hMonitor) + d->hMonitorProfile = hMonitor; + else + d->hMonitorProfile = cmsCreate_sRGBProfile(); + + d->hXYZProfile = cmsCreateXYZProfile(); + d->hXFORM = cmsCreateTransform(d->hXYZProfile, TYPE_XYZ_16, + d->hMonitorProfile, TYPE_RGB_8, + INTENT_PERCEPTUAL, 0); + + connect(d->blinkTimer, TQ_SIGNAL(timeout()), + this, TQ_SLOT(slotBlinkTimerDone())); +} + +CIETongueWidget::~CIETongueWidget() +{ + if (d->Measurement.Patches) + free(d->Measurement.Patches); + + if (d->Measurement.Allowed) + free(d->Measurement.Allowed); + + cmsDeleteTransform(d->hXFORM); + cmsCloseProfile(d->hXYZProfile); + cmsCloseProfile(d->hMonitorProfile); + delete d; +} + +int CIETongueWidget::grids(double val) const +{ + return (int) floor(val * d->gridside + 0.5); +} + +bool CIETongueWidget::setProfileData(const TQByteArray &profileData) +{ + if (!profileData.isEmpty()) + { + cmsHPROFILE hProfile = cmsOpenProfileFromMem(const_cast<char*>(profileData.data()), + (DWORD)profileData.size()); + + if (!hProfile) + { + d->profileDataAvailable = false; + d->loadingImageSucess = false; + } + else + { + setProfile(hProfile); + cmsCloseProfile(hProfile); + d->profileDataAvailable = true; + d->loadingImageSucess = true; + } + } + else + { + d->profileDataAvailable = false; + d->loadingImageSucess = false; + } + + d->loadingImageMode = false; + + d->blinkTimer->stop(); + repaint(false); + return (d->profileDataAvailable); +} + +bool CIETongueWidget::setProfileFromFile(const KURL& file) +{ + if (!file.isEmpty() && file.isValid()) + { + cmsHPROFILE hProfile = cmsOpenProfileFromFile(TQFile::encodeName(file.path()), "r"); + + if (!hProfile) + { + d->profileDataAvailable = false; + d->loadingImageSucess = false; + } + else + { + setProfile(hProfile); + cmsCloseProfile(hProfile); + d->profileDataAvailable = true; + d->loadingImageSucess = true; + } + } + else + { + d->profileDataAvailable = false; + d->loadingImageSucess = false; + } + + d->blinkTimer->stop(); + repaint(false); + return (d->profileDataAvailable); +} + +void CIETongueWidget::setProfile(cmsHPROFILE hProfile) +{ + // Get the white point. + + ZeroMemory(&(d->MediaWhite), sizeof(cmsCIEXYZ)); + cmsTakeMediaWhitePoint(&(d->MediaWhite), hProfile); + cmsCIExyY White; + cmsXYZ2xyY(&White, &(d->MediaWhite)); + + // Get the colorant matrix. + + ZeroMemory(&(d->Primaries), sizeof(cmsCIExyYTRIPLE)); + + if (cmsIsTag(hProfile, icSigRedColorantTag) && + cmsIsTag(hProfile, icSigGreenColorantTag) && + cmsIsTag(hProfile, icSigBlueColorantTag)) + { + MAT3 Mat; + + if (cmsReadICCMatrixRGB2XYZ(&Mat, hProfile)) + { + // Undo chromatic adaptation + if (cmsAdaptMatrixFromD50(&Mat, &White)) + { + cmsCIEXYZ tmp; + + tmp.X = Mat.v[0].n[0]; + tmp.Y = Mat.v[1].n[0]; + tmp.Z = Mat.v[2].n[0]; + + // ScaleToWhite(&MediaWhite, &tmp); + cmsXYZ2xyY(&(d->Primaries.Red), &tmp); + + tmp.X = Mat.v[0].n[1]; + tmp.Y = Mat.v[1].n[1]; + tmp.Z = Mat.v[2].n[1]; + // ScaleToWhite(&MediaWhite, &tmp); + cmsXYZ2xyY(&(d->Primaries.Green), &tmp); + + tmp.X = Mat.v[0].n[2]; + tmp.Y = Mat.v[1].n[2]; + tmp.Z = Mat.v[2].n[2]; + // ScaleToWhite(&MediaWhite, &tmp); + cmsXYZ2xyY(&(d->Primaries.Blue), &tmp); + } + } + } + + // Get target data stored in profile + + ZeroMemory(&(d->Measurement), sizeof(MEASUREMENT)); + char* CharTarget; + size_t CharTargetSize; + + if (cmsTakeCharTargetData(hProfile, &CharTarget, &CharTargetSize)) + { + LCMSHANDLE hSheet = cmsxIT8LoadFromMem(CharTarget, CharTargetSize); + if (hSheet != NULL) + { + cmsxPCollLoadFromSheet(&(d->Measurement), hSheet); + cmsxIT8Free(hSheet); + cmsxPCollValidatePatches(&(d->Measurement), PATCH_HAS_XYZ|PATCH_HAS_RGB); + } + } +} + +void CIETongueWidget::mapPoint(int& icx, int& icy, LPcmsCIExyY xyY) +{ + icx = (int) floor((xyY->x * (d->pxcols - 1)) + .5); + icy = (int) floor(((d->pxrows - 1) - xyY->y * (d->pxrows - 1)) + .5); +} + +void CIETongueWidget::biasedLine(int x1, int y1, int x2, int y2) +{ + d->painter.drawLine(x1 + d->xBias, y1, x2 + d->xBias, y2); +} + +void CIETongueWidget::biasedText(int x, int y, TQString Txt) +{ + d->painter.drawText(TQPoint(d->xBias + x, y), Txt); +} + +TQRgb CIETongueWidget::colorByCoord(double x, double y) +{ + // Get xyz components scaled from coordinates + + double cx = ((double) x) / (d->pxcols - 1); + double cy = 1.0 - ((double) y) / (d->pxrows - 1); + double cz = 1.0 - cx - cy; + + // Project xyz to XYZ space. Note that in this + // particular case we are substituting XYZ with xyz + + cmsCIEXYZ XYZ = { cx , cy , cz }; + + WORD XYZW[3]; + BYTE RGB[3]; + + cmsFloat2XYZEncoded(XYZW, &XYZ); + cmsDoTransform(d->hXFORM, XYZW, RGB, 1); + + return tqRgb(RGB[0], RGB[1], RGB[2]); +} + +void CIETongueWidget::outlineTongue() +{ + int lx = 0, ly = 0; + int fx=0, fy=0; + + for (int x = 380; x <= 700; x += 5) + { + int ix = (x - 380) / 5; + + cmsCIExyY p = {spectral_chromaticity[ix][0], + spectral_chromaticity[ix][1], 1}; + + int icx, icy; + mapPoint(icx, icy, &p); + + if (x > 380) + { + biasedLine(lx, ly, icx, icy); + } + else + { + fx = icx; + fy = icy; + } + + lx = icx; + ly = icy; + } + + biasedLine(lx, ly, fx, fy); +} + +void CIETongueWidget::fillTongue() +{ + + TQImage Img = d->pixmap.convertToImage(); + + int x; + + for (int y = 0; y < d->pxrows; y++) + { + int xe = 0; + + // Find horizontal extents of tongue on this line. + + for (x = 0; x < d->pxcols; x++) + { + if ((TQColor) Img.pixel(x + d->xBias, y) != TQt::black) + { + for (xe = d->pxcols - 1; xe >= x; xe--) + { + if ((TQColor) Img.pixel(xe + d->xBias, y) != TQt::black) + { + break; + } + } + + break; + } + } + + if (x < d->pxcols) + { + for ( ; x <= xe; x++) + { + TQRgb Color = colorByCoord(x, y); + Img.setPixel(x + d->xBias, y, Color); + } + } + } + + d->pixmap.convertFromImage(Img, TQPixmap::AvoidDither ); +} + +void CIETongueWidget::drawTongueAxis() +{ + TQFont font; + font.setPointSize(6); + d->painter.setFont(font); + + d->painter.setPen(tqRgb(255, 255, 255)); + + biasedLine(0, 0, 0, d->pxrows - 1); + biasedLine(0, d->pxrows-1, d->pxcols-1, d->pxrows - 1); + + for (int y = 1; y <= 9; y += 1) + { + TQString s; + int xstart = (y * (d->pxcols - 1)) / 10; + int ystart = (y * (d->pxrows - 1)) / 10; + + s.sprintf("0.%d", y); + biasedLine(xstart, d->pxrows - grids(1), xstart, d->pxrows - grids(4)); + biasedText(xstart - grids(11), d->pxrows + grids(15), s); + + s.sprintf("0.%d", 10 - y); + biasedLine(0, ystart, grids(3), ystart); + biasedText(grids(-25), ystart + grids(5), s); + } +} + +void CIETongueWidget::drawTongueGrid() +{ + d->painter.setPen(tqRgb(80, 80, 80)); + + for (int y = 1; y <= 9; y += 1) + { + int xstart = (y * (d->pxcols - 1)) / 10; + int ystart = (y * (d->pxrows - 1)) / 10; + + biasedLine(xstart, grids(4), xstart, d->pxrows - grids(4) - 1); + biasedLine(grids(7), ystart, d->pxcols-1-grids(7), ystart); + } +} + +void CIETongueWidget::drawLabels() +{ + TQFont font; + font.setPointSize(5); + d->painter.setFont(font); + + for (int x = 450; x <= 650; x += (x > 470 && x < 600) ? 5 : 10) + { + TQString wl; + int bx = 0, by = 0, tx, ty; + + if (x < 520) + { + bx = grids(-22); + by = grids(2); + } + else if (x < 535) + { + bx = grids(-8); + by = grids(-6); + } + else + { + bx = grids(4); + } + + int ix = (x - 380) / 5; + + cmsCIExyY p = {spectral_chromaticity[ix][0], + spectral_chromaticity[ix][1], 1}; + + int icx, icy; + mapPoint(icx, icy, &p); + + tx = icx + ((x < 520) ? grids(-2) : ((x >= 535) ? grids(2) : 0)); + ty = icy + ((x < 520) ? 0 : ((x >= 535) ? grids(-1) : grids(-2))); + + d->painter.setPen(tqRgb(255, 255, 255)); + biasedLine(icx, icy, tx, ty); + + TQRgb Color = colorByCoord(icx, icy); + d->painter.setPen(Color); + + wl.sprintf("%d", x); + biasedText(icx+bx, icy+by, wl); + } +} + +void CIETongueWidget::drawSmallElipse(LPcmsCIExyY xyY, BYTE r, BYTE g, BYTE b, int sz) +{ + int icx, icy; + + mapPoint(icx, icy, xyY); + d->painter.setPen(tqRgb(r, g, b)); + d->painter.drawEllipse(icx + d->xBias- sz/2, icy-sz/2, sz, sz); +} + +void CIETongueWidget::drawPatches() +{ + for (int i=0; i < d->Measurement.nPatches; i++) + { + LPPATCH p = d->Measurement.Patches + i; + + if (d->Measurement.Allowed[i]) + { + LPcmsCIEXYZ XYZ = &p ->XYZ; + cmsCIExyY xyY; + cmsXYZ2xyY(&xyY, XYZ); + + drawSmallElipse(&xyY, 0, 0, 0, 4); + + if (p->dwFlags & PATCH_HAS_XYZ_PROOF) + { + if (p->XYZ.Y < 0.03) + continue; + + if (p->XYZProof.Y < 0.03) + continue; + + cmsCIExyY Pt; + cmsXYZ2xyY(&Pt, &p->XYZProof); + int icx1, icx2, icy1, icy2; + + mapPoint(icx1, icy1, &xyY); + mapPoint(icx2, icy2, &Pt); + + if (icx2 < 5 || icy2 < 5 || icx1 < 5 || icy1 < 5) + continue; + + d->painter.setPen(tqRgb(255, 255, 0)); + biasedLine(icx1, icy1, icx2, icy2); + } + } + } +} + +void CIETongueWidget::drawColorantTriangle() +{ + drawSmallElipse(&(d->Primaries.Red), 255, 128, 128, 6); + drawSmallElipse(&(d->Primaries.Green), 128, 255, 128, 6); + drawSmallElipse(&(d->Primaries.Blue), 128, 128, 255, 6); + + int x1, y1, x2, y2, x3, y3; + + mapPoint(x1, y1, &(d->Primaries.Red)); + mapPoint(x2, y2, &(d->Primaries.Green)); + mapPoint(x3, y3, &(d->Primaries.Blue)); + + d->painter.setPen(tqRgb(255, 255, 255)); + + biasedLine(x1, y1, x2, y2); + biasedLine(x2, y2, x3, y3); + biasedLine(x3, y3, x1, y1); +} + +void CIETongueWidget::sweep_sRGB() +{ + int r, g, b; + cmsHPROFILE hXYZ, hsRGB; + + hXYZ = cmsCreateXYZProfile(); + hsRGB = cmsCreate_sRGBProfile(); + + cmsHTRANSFORM xform = cmsCreateTransform(hsRGB, TYPE_RGB_16, hXYZ, TYPE_XYZ_16, + INTENT_ABSOLUTE_COLORIMETRIC, cmsFLAGS_NOTPRECALC); + WORD RGB[3], XYZ[3]; + cmsCIEXYZ xyz, MediaWhite; + cmsCIExyY xyY, WhitePt; + int x1, y1; + + cmsTakeMediaWhitePoint(&MediaWhite, hsRGB); + cmsXYZ2xyY(&WhitePt, &MediaWhite); + + for (r=0; r < 65536; r += 1024) + { + for (g=0; g < 65536; g += 1024) + { + for (b=0; b < 65536; b += 1024) + { + RGB[0] = r; RGB[1] = g; RGB[2] = b; + cmsDoTransform(xform, RGB, XYZ, 1); + cmsXYZEncoded2Float(&xyz, XYZ); + cmsXYZ2xyY(&xyY, &xyz); + mapPoint(x1, y1, &xyY); + d->painter.drawPoint(x1 + d->xBias, y1); + } + } + } + + cmsDeleteTransform(xform); + cmsCloseProfile(hXYZ); + cmsCloseProfile(hsRGB); +} + +void CIETongueWidget::drawWhitePoint() +{ + cmsCIExyY Whitem_pntxyY; + cmsXYZ2xyY(&Whitem_pntxyY, &(d->MediaWhite)); + drawSmallElipse(&Whitem_pntxyY, 255, 255, 255, 8); +} + +void CIETongueWidget::loadingStarted() +{ + d->pos = 0; + d->loadingImageMode = true; + d->loadingImageSucess = false; + repaint(false); + d->blinkTimer->start(200); +} + +void CIETongueWidget::loadingFailed() +{ + d->blinkTimer->stop(); + d->pos = 0; + d->loadingImageMode = false; + d->loadingImageSucess = false; + repaint(false); +} + +void CIETongueWidget::paintEvent(TQPaintEvent*) +{ + d->pixmap = TQPixmap(size()); + d->pixmap.setOptimization(TQPixmap::BestOptim); + + // Widget is disable : drawing grayed frame. + + if ( !isEnabled() ) + { + d->painter.begin(&d->pixmap); + d->painter.fillRect(0, 0, size().width(), size().height(), palette().disabled().background()); + d->painter.setPen(TQPen(palette().disabled().foreground(), 1, TQt::SolidLine)); + d->painter.drawRect(0, 0, width(), height()); + d->painter.end(); + bitBlt(this, 0, 0, &d->pixmap); + return; + } + + // Loading image mode. + + if (d->loadingImageMode && !d->loadingImageSucess) + { + // In first, we draw an animation. + + int asize = 24; + TQPixmap anim(asize, asize); + TQPainter p2; + p2.begin(&anim, this); + p2.fillRect(0, 0, asize, asize, palette().active().background()); + p2.translate(asize/2, asize/2); + + d->pos = (d->pos + 10) % 360; + p2.setPen(TQPen(palette().active().text())); + p2.rotate(d->pos); + for ( int i=0 ; i<12 ; i++ ) + { + p2.drawLine(asize/2-5, 0, asize/2-2, 0); + p2.rotate(30); + } + p2.end(); + + // ... and we render busy text. + + d->painter.begin(&d->pixmap); + d->painter.fillRect(0, 0, size().width(), size().height(), palette().active().background()); + d->painter.drawPixmap(width()/2 - asize /2, asize, anim); + d->painter.setPen(TQPen(palette().active().text(), 1, TQt::SolidLine)); + d->painter.drawRect(0, 0, width(), height()); + d->painter.drawText(0, 0, size().width(), size().height(), TQt::AlignCenter, + i18n("Loading image...")); + + d->painter.end(); + bitBlt(this, 0, 0, &d->pixmap); + return; + } + + // No profile data to show. + + if (!d->profileDataAvailable || (!d->loadingImageMode && !d->loadingImageSucess)) + { + d->painter.begin(&d->pixmap); + d->painter.fillRect(0, 0, size().width(), size().height(), palette().active().background()); + d->painter.setPen(TQPen(palette().active().text(), 1, TQt::SolidLine)); + d->painter.drawRect(0, 0, width(), height()); + d->painter.drawText(0, 0, size().width(), size().height(), TQt::AlignCenter, + i18n("No profile available...")); + + d->painter.end(); + bitBlt(this, 0, 0, &d->pixmap); + return; + } + + // Draw the CIE tongue curve. + + d->pixmap.fill(TQt::black); + d->painter.begin(&d->pixmap); + + int pixcols = d->pixmap.width(); + int pixrows = d->pixmap.height(); + + d->gridside = (TQMIN(pixcols, pixrows)) / 512.0; + d->xBias = grids(32); + d->yBias = grids(20); + d->pxcols = pixcols - d->xBias; + d->pxrows = pixrows - d->yBias; + + d->painter.setBackgroundColor(tqRgb(0, 0, 0)); + d->painter.setPen(tqRgb(255, 255, 255)); + + outlineTongue(); + fillTongue(); + + drawTongueAxis(); + drawLabels(); + drawTongueGrid(); + + if (d->MediaWhite.Y > 0.0) + drawWhitePoint(); + + if (d->Primaries.Red.Y != 0.0) + drawColorantTriangle(); + + if (d->Measurement.Patches && d->Measurement.Allowed) + drawPatches(); + + d->painter.end(); + + bitBlt(this, 0, 0, &d->pixmap); +} + +void CIETongueWidget::slotBlinkTimerDone() +{ + repaint(false); + d->blinkTimer->start( 200 ); +} + +} // namespace Digikam |