|
|
|
/* ============================================================
|
|
|
|
*
|
|
|
|
* 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, TQT_SIGNAL(timeout()),
|
|
|
|
this, TQT_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, TQT_TQOBJECT(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
|