diff --git a/core/libs/widgets/iccprofiles/cietonguewidget.cpp b/core/libs/widgets/iccprofiles/cietonguewidget.cpp index fb04272769..47bef6e1b8 100644 --- a/core/libs/widgets/iccprofiles/cietonguewidget.cpp +++ b/core/libs/widgets/iccprofiles/cietonguewidget.cpp @@ -1,823 +1,844 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2006-01-10 * Description : a widget to display CIE tongue from an ICC profile. * * Copyright (C) 2006-2020 by Gilles Caulier * * 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. * * ============================================================ */ #include "cietonguewidget.h" // C++ includes #include // Qt includes #include #include #include #include #include #include // KDE includes #include // Local includes #include "digikam_config.h" #include "digikam_debug.h" #include "iccprofile.h" #include "dlayoutbox.h" #include "dworkingpixmap.h" namespace Digikam { /** -The following table gives the CIE color matching functions -\f$\bar{x}(\lambda)\f$, \f$\bar{y}(\lambda)\f$, and -\f$\bar{z}(\lambda)\f$, for wavelengths \f$\lambda\f$ at 5 nanometer -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 -\f$x(\lambda)\f$ and \f$y(\lambda)\f$ for wavelengths in 5 nanometer -increments from 380 nm through 780 nm. These coordinates represent the -position in the CIE x-y space of pure spectral colors of the given -wavelength, and thus define the outline of the CIE "tongue" diagram. -*/ + * The following table gives the CIE color matching functions + * \f$\bar{x}(\lambda)\f$, \f$\bar{y}(\lambda)\f$, and + * \f$\bar{z}(\lambda)\f$, for wavelengths \f$\lambda\f$ at 5 nanometer + * 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 + * \f$x(\lambda)\f$ and \f$y(\lambda)\f$ for wavelengths in 5 nanometer + * increments from 380 nm through 780 nm. These coordinates represent the + * position in the CIE x-y space of pure spectral colors 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 Q_DECL_HIDDEN CIETongueWidget::Private { public: explicit Private() : profileDataAvailable(true), loadingImageMode(false), loadingImageSucess(false), needUpdatePixmap(false), uncalibratedColor(false), xBias(0), yBias(0), pxcols(0), pxrows(0), progressCount(0), gridside(0), progressTimer(nullptr), + progressPix(DWorkingPixmap()), hMonitorProfile(nullptr), hXFORM(nullptr) { - progressPix = DWorkingPixmap(); } bool profileDataAvailable; bool loadingImageMode; bool loadingImageSucess; bool needUpdatePixmap; bool uncalibratedColor; int xBias; int yBias; int pxcols; int pxrows; - int progressCount; // Position of animation during loading/calculation. + int progressCount; ///< Position of animation during loading/calculation. double gridside; QPainter painter; QTimer* progressTimer; QPixmap pixmap; - DWorkingPixmap progressPix; + DWorkingPixmap progressPix; cmsHPROFILE hMonitorProfile; cmsHTRANSFORM hXFORM; cmsCIExyYTRIPLE Primaries; cmsCIEXYZ MediaWhite; }; CIETongueWidget::CIETongueWidget(int w, int h, QWidget* const parent, cmsHPROFILE hMonitor) : QWidget(parent), d(new Private) { cmsHPROFILE hXYZProfile; d->progressTimer = new QTimer(this); setMinimumSize(w, h); setAttribute(Qt::WA_DeleteOnClose); dkCmsErrorAction(LCMS_ERROR_SHOW); if (hMonitor) { d->hMonitorProfile = hMonitor; } else { d->hMonitorProfile = dkCmsCreate_sRGBProfile(); } hXYZProfile = dkCmsCreateXYZProfile(); if (hXYZProfile == nullptr) + { return; + } d->hXFORM = dkCmsCreateTransform(hXYZProfile, TYPE_XYZ_16, d->hMonitorProfile, TYPE_RGB_8, INTENT_PERCEPTUAL, 0); dkCmsCloseProfile(hXYZProfile); if (d->hXFORM == nullptr) + { qCDebug(DIGIKAM_WIDGETS_LOG) << "Wrong d->hXFORM" ; + } connect(d->progressTimer, SIGNAL(timeout()), this, SLOT(slotProgressTimerDone())); } CIETongueWidget::~CIETongueWidget() { dkCmsDeleteTransform(d->hXFORM); dkCmsCloseProfile(d->hMonitorProfile); delete d; } int CIETongueWidget::grids(double val) const { - return (int) floor(val * d->gridside + 0.5); + return ((int)floor(val * d->gridside + 0.5)); } bool CIETongueWidget::setProfileData(const QByteArray& profileData) { if (!profileData.isEmpty()) { LcmsLock lock; cmsHPROFILE hProfile = dkCmsOpenProfileFromMem((void*)profileData.data(), (DWORD)profileData.size()); if (!hProfile) { d->profileDataAvailable = false; d->loadingImageSucess = false; } else { setProfile(hProfile); dkCmsCloseProfile(hProfile); d->profileDataAvailable = true; d->loadingImageSucess = true; } } else { d->profileDataAvailable = false; d->loadingImageSucess = false; } - d->loadingImageMode = false; + d->loadingImageMode = false; d->uncalibratedColor = false; d->progressTimer->stop(); - d->needUpdatePixmap = true; + d->needUpdatePixmap = true; update(); + return (d->profileDataAvailable); } bool CIETongueWidget::setProfileFromFile(const QUrl& file) { if (!file.isEmpty() && file.isValid()) { LcmsLock lock; cmsHPROFILE hProfile = dkCmsOpenProfileFromFile(QFile::encodeName(file.toLocalFile()).constData(), "r"); if (!hProfile) { d->profileDataAvailable = false; d->loadingImageSucess = false; } else { setProfile(hProfile); dkCmsCloseProfile(hProfile); d->profileDataAvailable = true; d->loadingImageSucess = true; } } else { d->profileDataAvailable = false; d->loadingImageSucess = false; } d->loadingImageMode = false; d->uncalibratedColor = false; d->progressTimer->stop(); - d->needUpdatePixmap = true; + d->needUpdatePixmap = true; update(); + return (d->profileDataAvailable); } void CIETongueWidget::setProfile(cmsHPROFILE hProfile) { // Get the white point. dkCmsTakeMediaWhitePoint(&(d->MediaWhite), hProfile); cmsCIExyY White; dkCmsXYZ2xyY(&White, &(d->MediaWhite)); qCDebug(DIGIKAM_WIDGETS_LOG) << "Profile white point : x=" << White.x << " y=" << White.y << " Y=" << White.Y; // Get the colorant matrix. memset(&(d->Primaries),0,sizeof(cmsCIExyYTRIPLE)); if (dkCmsIsTag(hProfile, icSigRedColorantTag) && dkCmsIsTag(hProfile, icSigGreenColorantTag) && dkCmsIsTag(hProfile, icSigBlueColorantTag)) { MAT3 Mat; if (dkCmsReadICCMatrixRGB2XYZ(&Mat, hProfile)) { #if defined(USE_LCMS_VERSION_1000) qCDebug(DIGIKAM_WIDGETS_LOG) << "dkCmsReadICCMatrixRGB2XYZ(1): " \ << "[" << Mat.v[0].n[0] << ", " << Mat.v[0].n[1] << ", " << Mat.v[0].n[2] << "]" \ << "[" << Mat.v[1].n[0] << ", " << Mat.v[1].n[1] << ", " << Mat.v[1].n[2] << "]" \ << "[" << Mat.v[2].n[0] << ", " << Mat.v[2].n[1] << ", " << Mat.v[2].n[2] << "]" ; #else qCDebug(DIGIKAM_WIDGETS_LOG) << "dkCmsReadICCMatrixRGB2XYZ(2): " \ << "[" << Mat.Red.X << ", " << Mat.Red.Y << ", " << Mat.Red.Z << "]" \ << "[" << Mat.Green.X << ", " << Mat.Green.Y << ", " << Mat.Green.Z << "]" \ << "[" << Mat.Blue.X << ", " << Mat.Blue.Y << ", " << Mat.Blue.Z << "]" ; #endif // Undo chromatic adaptation + if (dkCmsAdaptMatrixFromD50(&Mat, &White)) { #if defined(USE_LCMS_VERSION_1000) cmsCIEXYZ tmp; tmp.X = Mat.v[0].n[0]; tmp.Y = Mat.v[1].n[0]; tmp.Z = Mat.v[2].n[0]; qCDebug(DIGIKAM_WIDGETS_LOG) << "d->Primaries.Red : X=" << tmp.X << " Y=" << tmp.Y << " Z=" << tmp.Z; - // ScaleToWhite(&MediaWhite, &tmp); +/* + ScaleToWhite(&MediaWhite, &tmp); +*/ dkCmsXYZ2xyY(&(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]; qCDebug(DIGIKAM_WIDGETS_LOG) << "d->Primaries.Green : X=" << tmp.X << " Y=" << tmp.Y << " Z=" << tmp.Z; // ScaleToWhite(&MediaWhite, &tmp); dkCmsXYZ2xyY(&(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]; qCDebug(DIGIKAM_WIDGETS_LOG) << "d->Primaries.Blue : X=" << tmp.X << " Y=" << tmp.Y << " Z=" << tmp.Z; - // ScaleToWhite(&MediaWhite, &tmp); +/* + ScaleToWhite(&MediaWhite, &tmp); +*/ dkCmsXYZ2xyY(&(d->Primaries.Blue), &tmp); #else cmsCIEXYZ tmp; tmp.X = Mat.Red.X; tmp.Y = Mat.Green.X; tmp.Z = Mat.Blue.X; qCDebug(DIGIKAM_WIDGETS_LOG) << "d->Primaries.Red : X=" << tmp.X << " Y=" << tmp.Y << " Z=" << tmp.Z; - // ScaleToWhite(&MediaWhite, &tmp); +/* + ScaleToWhite(&MediaWhite, &tmp); +*/ dkCmsXYZ2xyY(&(d->Primaries.Red), &tmp); tmp.X = Mat.Red.Y; tmp.Y = Mat.Green.Y; tmp.Z = Mat.Blue.Y; qCDebug(DIGIKAM_WIDGETS_LOG) << "d->Primaries.Green : X=" << tmp.X << " Y=" << tmp.Y << " Z=" << tmp.Z; - // ScaleToWhite(&MediaWhite, &tmp); +/* + ScaleToWhite(&MediaWhite, &tmp); +*/ dkCmsXYZ2xyY(&(d->Primaries.Green), &tmp); tmp.X = Mat.Red.Z; tmp.Y = Mat.Green.Z; tmp.Z = Mat.Blue.Z; qCDebug(DIGIKAM_WIDGETS_LOG) << "d->Primaries.Blue : X=" << tmp.X << " Y=" << tmp.Y << " Z=" << tmp.Z; - // ScaleToWhite(&MediaWhite, &tmp); +/* + ScaleToWhite(&MediaWhite, &tmp); +*/ dkCmsXYZ2xyY(&(d->Primaries.Blue), &tmp); #endif } } } } 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, const QString& txt) { d->painter.drawText(QPoint(d->xBias + x, y), txt); } QRgb 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]; dkCmsFloat2XYZEncoded(XYZW, &XYZ); dkCmsDoTransform(d->hXFORM, XYZW, RGB, 1); return qRgb(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) + for (int x = 380 ; x <= 700 ; x += 5) { int ix = (x - 380) / 5; - cmsCIExyY p = {spectral_chromaticity[ix][0], + 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() { QImage Img = d->pixmap.toImage(); int x; - for (int y = 0; y < d->pxrows; ++y) + 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 (QColor(Img.pixel(x + d->xBias, y)) != QColor(Qt::black)) { - for (xe = d->pxcols - 1; xe >= x; --xe) + for (xe = d->pxcols - 1 ; xe >= x ; --xe) { if (QColor(Img.pixel(xe + d->xBias, y)) != QColor(Qt::black)) { break; } } break; } } if (x < d->pxcols) { - for ( ; x <= xe; ++x) + for ( ; x <= xe ; ++x) { QRgb Color = colorByCoord(x, y); Img.setPixel(x + d->xBias, y, Color); } } } d->pixmap = QPixmap::fromImage(Img, Qt::AvoidDither); } void CIETongueWidget::drawTongueAxis() { QFont font; font.setPointSize(6); d->painter.setFont(font); d->painter.setPen(qRgb(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) + for (int y = 1 ; y <= 9 ; y += 1) { QString 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(qRgb(80, 80, 80)); - for (int y = 1; y <= 9; y += 1) + 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() { QFont font; font.setPointSize(5); d->painter.setFont(font); - for (int x = 450; x <= 650; x += (x > 470 && x < 600) ? 5 : 10) + for (int x = 450 ; x <= 650 ; x += (x > 470 && x < 600) ? 5 : 10) { QString wl; int bx = 0, by = 0, tx, ty; - if (x < 520) + 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], + 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(qRgb(255, 255, 255)); biasedLine(icx, icy, tx, ty); QRgb 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(qRgb(r, g, b)); d->painter.drawEllipse(icx + d->xBias- sz/2, icy-sz/2, sz, sz); } 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(qRgb(255, 255, 255)); biasedLine(x1, y1, x2, y2); biasedLine(x2, y2, x3, y3); biasedLine(x3, y3, x1, y1); } void CIETongueWidget::drawWhitePoint() { cmsCIExyY Whitem_pntxyY; dkCmsXYZ2xyY(&Whitem_pntxyY, &(d->MediaWhite)); drawSmallElipse(&Whitem_pntxyY, 255, 255, 255, 8); } void CIETongueWidget::loadingStarted() { d->progressCount = 0; d->loadingImageMode = true; d->loadingImageSucess = false; update(); d->progressTimer->start(200); } void CIETongueWidget::loadingFailed() { d->progressTimer->stop(); d->progressCount = 0; d->loadingImageMode = false; d->loadingImageSucess = false; update(); } void CIETongueWidget::uncalibratedColor() { d->progressTimer->stop(); d->progressCount = 0; d->loadingImageMode = false; d->loadingImageSucess = false; d->uncalibratedColor = true; update(); } void CIETongueWidget::updatePixmap() { d->needUpdatePixmap = false; - d->pixmap = QPixmap(size()); + d->pixmap = QPixmap(size()); // Draw the CIE tongue curve. d->pixmap.fill(Qt::black); d->painter.begin(&d->pixmap); int pixcols = d->pixmap.width(); int pixrows = d->pixmap.height(); d->gridside = (qMin(pixcols, pixrows)) / 512.0; d->xBias = grids(32); d->yBias = grids(20); d->pxcols = pixcols - d->xBias; d->pxrows = pixrows - d->yBias; d->painter.setBackground(QBrush(qRgb(0, 0, 0))); d->painter.setPen(qRgb(255, 255, 255)); outlineTongue(); d->painter.end(); fillTongue(); d->painter.begin(&d->pixmap); drawTongueAxis(); drawLabels(); drawTongueGrid(); if (d->MediaWhite.Y > 0.0) { drawWhitePoint(); } if (d->Primaries.Red.Y != 0.0) { drawColorantTriangle(); } d->painter.end(); } void CIETongueWidget::paintEvent(QPaintEvent*) { QPainter p(this); // Widget is disable : drawing grayed frame. if (!isEnabled()) { p.fillRect(0, 0, width(), height(), palette().color(QPalette::Disabled, QPalette::Window)); QPen pen(palette().color(QPalette::Disabled, QPalette::WindowText)); pen.setStyle(Qt::SolidLine); pen.setWidth(1); p.setPen(pen); p.drawRect(0, 0, width(), height()); return; } // Loading image mode. if (d->loadingImageMode && !d->loadingImageSucess) { // In first, we draw an animation. QPixmap anim(d->progressPix.frameAt(d->progressCount)); d->progressCount++; if (d->progressCount >= d->progressPix.frameCount()) { d->progressCount = 0; } // ... and we render busy text. p.fillRect(0, 0, width(), height(), palette().color(QPalette::Active, QPalette::Window)); p.drawPixmap(width()/2 - anim.width() /2, anim.height(), anim); QPen pen(palette().color(QPalette::Active, QPalette::Text)); pen.setStyle(Qt::SolidLine); pen.setWidth(1); p.setPen(pen); p.drawRect(0, 0, width(), height()); p.drawText(0, 0, width(), height(), Qt::AlignCenter, i18n("Loading image...")); return; } // No profile data to show, or RAW file if (!d->profileDataAvailable || (!d->loadingImageMode && !d->loadingImageSucess)) { p.fillRect(0, 0, width(), height(), palette().color(QPalette::Active, QPalette::Window)); QPen pen(palette().color(QPalette::Active, QPalette::Text)); pen.setStyle(Qt::SolidLine); pen.setWidth(1); p.setPen(pen); p.drawRect(0, 0, width(), height()); if (d->uncalibratedColor) { p.drawText(0, 0, width(), height(), Qt::AlignCenter, i18n("Uncalibrated color space")); } else { p.setPen(Qt::red); p.drawText(0, 0, width(), height(), Qt::AlignCenter, i18n("No profile available...")); } return; } // Create CIE tongue if needed + if (d->needUpdatePixmap) { updatePixmap(); } // draw prerendered tongue + p.drawPixmap(0, 0, d->pixmap); } void CIETongueWidget::resizeEvent(QResizeEvent* event) { Q_UNUSED(event); setMinimumHeight(width()); d->needUpdatePixmap = true; } void CIETongueWidget::slotProgressTimerDone() { update(); d->progressTimer->start(200); } } // namespace Digikam diff --git a/core/libs/widgets/iccprofiles/cietonguewidget.h b/core/libs/widgets/iccprofiles/cietonguewidget.h index f4f096b6df..51985bc824 100644 --- a/core/libs/widgets/iccprofiles/cietonguewidget.h +++ b/core/libs/widgets/iccprofiles/cietonguewidget.h @@ -1,99 +1,99 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2006-01-10 * Description : a widget to display CIE tongue from an ICC profile. * * Copyright (C) 2006-2020 by Gilles Caulier * * 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. * * ============================================================ */ #ifndef DIGIKAM_CIE_TONGUE_WIDGET_H #define DIGIKAM_CIE_TONGUE_WIDGET_H // Qt includes #include #include #include #include // Local includes #include "digikam-lcms.h" #include "digikam_export.h" namespace Digikam { class DIGIKAM_EXPORT CIETongueWidget : public QWidget { Q_OBJECT public: explicit CIETongueWidget(int w, int h, QWidget* const parent=nullptr, cmsHPROFILE hMonitor=nullptr); ~CIETongueWidget(); bool setProfileData(const QByteArray& profileData=QByteArray()); bool setProfileFromFile(const QUrl& file=QUrl()); void loadingStarted(); void loadingFailed(); void uncalibratedColor(); protected: int grids(double val) const; void outlineTongue(); void fillTongue(); void drawTongueAxis(); void drawTongueGrid(); void drawLabels(); QRgb colorByCoord(double x, double y); void drawSmallElipse(LPcmsCIExyY xyY, BYTE r, BYTE g, BYTE b, int sz); void resizeEvent(QResizeEvent* event) override; - void paintEvent(QPaintEvent*) override; + void paintEvent(QPaintEvent*) override; private: void drawColorantTriangle(); void drawWhitePoint(); void drawPatches(); void updatePixmap(); void mapPoint(int& icx, int& icy, LPcmsCIExyY xyY); void biasedLine(int x1, int y1, int x2, int y2); void biasedText(int x, int y, const QString& txt); void setProfile(cmsHPROFILE hProfile); private Q_SLOTS: void slotProgressTimerDone(); private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_CIE_TONGUE_WIDGET_H diff --git a/core/libs/widgets/iccprofiles/iccprofilescombobox.cpp b/core/libs/widgets/iccprofiles/iccprofilescombobox.cpp index 8c4a4e52b3..1a0355ca65 100644 --- a/core/libs/widgets/iccprofiles/iccprofilescombobox.cpp +++ b/core/libs/widgets/iccprofiles/iccprofilescombobox.cpp @@ -1,312 +1,314 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-08-11 * Description : a combo box containing ICC profiles * * Copyright (C) 2009-2010 by Marcel Wiesweg * * 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. * * ============================================================ */ #include "iccprofilescombobox.h" // Qt includes #include #include #include #include #include // KDE includes #include // Local includes #include "icctransform.h" namespace Digikam { IccProfilesComboBox::IccProfilesComboBox(QWidget* const parent) : SqueezedComboBox( parent ) { } IccProfilesComboBox::~IccProfilesComboBox() { } bool iccProfileLessThan(IccProfile a, IccProfile b) { - return a.description() < b.description(); + return (a.description() < b.description()); } // if needed outside this class, make it a public static method in a namespace static QString profileUserString(const IccProfile& p) { IccProfile profile(p); QFileInfo info(profile.filePath()); - QString fileName = info.fileName(); + QString fileName = info.fileName(); QString description = profile.description(); if (!description.isEmpty() && !fileName.isEmpty()) { return i18nc(" ()", "%1 (%2)", description, fileName); } else if (!fileName.isEmpty()) { return fileName; } else { return QString(); } } -// if needed outside this class, make it a public static method in a namespace +/** + * NOTE: if needed outside this class, make it a public static method in a namespace + */ static void formatProfiles(const QList& givenProfiles, QList* const returnedProfiles, QStringList* const userText) { QList profiles; QSet filePaths; foreach (IccProfile profile, givenProfiles) // krazy:exclude=foreach { QString filePath = profile.filePath(); if (!profile.description().isNull() && (filePath.isNull() || !filePaths.contains(filePath)) ) { - profiles << profile; + profiles << profile; filePaths << filePath; } } std::sort(profiles.begin(), profiles.end(), iccProfileLessThan); foreach (IccProfile profile, profiles) // krazy:exclude=foreach { QString description = profileUserString(profile); if (description.isNull()) { continue; } *returnedProfiles << profile; - *userText << description; + *userText << description; } } void IccProfilesComboBox::addProfilesSqueezed(const QList& givenProfiles) { QList profiles; QStringList userDescription; formatProfiles(givenProfiles, &profiles, &userDescription); for (int i = 0 ; i < profiles.size() ; ++i) { addSqueezedItem(userDescription.at(i), QVariant::fromValue(profiles.at(i))); } } void IccProfilesComboBox::addProfileSqueezed(const IccProfile& profile, const QString& d) { QString description = d; if (description.isNull()) { description = profileUserString(profile); } addSqueezedItem(description, QVariant::fromValue(profile)); } void IccProfilesComboBox::replaceProfilesSqueezed(const QList& profiles) { IccProfile current = currentProfile(); clear(); addProfilesSqueezed(profiles); setCurrentProfile(current); } void IccProfilesComboBox::setNoProfileIfEmpty(const QString& message) { if (count() == 0) { setEnabled(false); addSqueezedItem(message); setCurrentIndex(0); } } IccProfile IccProfilesComboBox::currentProfile() const { return itemData(currentIndex()).value(); } void IccProfilesComboBox::setCurrentProfile(const IccProfile& profile) { if (profile.isNull()) { setCurrentIndex(-1); return; } const int size = count(); for (int i = 0 ; i < size ; ++i) { if (itemData(i).value() == profile) { setCurrentIndex(i); return; } } setCurrentIndex(-1); } // ------------------------------------------------------------------------------------------ IccProfilesMenuAction::IccProfilesMenuAction(const QIcon& icon, const QString& text, QObject* const parent) : QMenu(text), m_parent(parent) { setIcon(icon); } IccProfilesMenuAction::IccProfilesMenuAction(const QString& text, QObject* const parent) : QMenu(text), m_parent(parent) { } void IccProfilesMenuAction::replaceProfiles(const QList& profiles) { clear(); addProfiles(profiles); } void IccProfilesMenuAction::addProfiles(const QList& givenProfiles) { QList profiles; QStringList userDescription; formatProfiles(givenProfiles, &profiles, &userDescription); for (int i = 0 ; i < profiles.size() ; ++i) { addProfile(profiles.at(i), userDescription.at(i)); } } void IccProfilesMenuAction::addProfile(const IccProfile& profile, const QString& d) { QString description = d; if (description.isNull()) { description = profileUserString(profile); } QAction* const action = new QAction(d.left(50), m_parent); action->setData(QVariant::fromValue(profile)); addAction(action); connect(action, &QAction::triggered, this, [this, action]() { slotTriggered(action); }); } void IccProfilesMenuAction::disableIfEmpty() { if (isEmpty()) { setEnabled(false); } } void IccProfilesMenuAction::slotTriggered(QObject* obj) { QAction* const action = static_cast(obj); IccProfile profile = action->data().value(); if (!profile.isNull()) { emit triggered(profile); } } QObject* IccProfilesMenuAction::parentObject() const { return m_parent; } // ------------------------------------------------------------------------------------------ IccRenderingIntentComboBox::IccRenderingIntentComboBox(QWidget* const parent) : QComboBox(parent) { addItem(QLatin1String("Perceptual"), IccTransform::Perceptual); addItem(QLatin1String("Relative Colorimetric"), IccTransform::RelativeColorimetric); addItem(QLatin1String("Absolute Colorimetric"), IccTransform::AbsoluteColorimetric); addItem(QLatin1String("Saturation"), IccTransform::Saturation); setWhatsThis( i18n("
  • Perceptual intent causes the full gamut of the image to be " "compressed or expanded to fill the gamut of the destination device, so that gray balance is " "preserved but colorimetric accuracy may not be preserved.

    " "

    In other words, if certain colors in an image fall outside of the range of colors that the output " "device can render, the image intent will cause all the colors in the image to be adjusted so that " "the every color in the image falls within the range that can be rendered and so that the relationship " "between colors is preserved as much as possible.

    " "

    This intent is most suitable for display of photographs and images, and is the default intent.

  • " "
  • Absolute Colorimetric intent causes any colors that fall outside the range that the output device " "can render to be adjusted to the closest color that can be rendered, while all other colors are " "left unchanged.

    " "

    This intent preserves the white point and is most suitable for spot colors (Pantone, TruMatch, " "logo colors, ....)

  • " "
  • Relative Colorimetric intent is defined such that any colors that fall outside the range that the " "output device can render are adjusted to the closest color that can be rendered, while all other colors " "are left unchanged. Proof intent does not preserve the white point.

  • " "
  • Saturation intent preserves the saturation of colors in the image at the possible expense of " "hue and lightness.

    " "

    Implementation of this intent remains somewhat problematic, and the ICC is still working on methods to " "achieve the desired effects.

    " "

    This intent is most suitable for business graphics such as charts, where it is more important that the " "colors be vivid and contrast well with each other rather than a specific color.

")); } void IccRenderingIntentComboBox::setIntent(int intent) { const int size = count(); for (int i = 0 ; i < size ; ++i) { if (itemData(i).toInt() == intent) { setCurrentIndex(i); return; } } setCurrentIndex(-1); } int IccRenderingIntentComboBox::intent() const { return itemData(currentIndex()).toInt(); } } // namespace Digikam diff --git a/core/libs/widgets/iccprofiles/iccprofilescombobox.h b/core/libs/widgets/iccprofiles/iccprofilescombobox.h index 90e85626de..5c1db15b5b 100644 --- a/core/libs/widgets/iccprofiles/iccprofilescombobox.h +++ b/core/libs/widgets/iccprofiles/iccprofilescombobox.h @@ -1,147 +1,153 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2009-08-11 * Description : a combo box containing ICC profiles * * Copyright (C) 2009-2010 by Marcel Wiesweg * * 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. * * ============================================================ */ #ifndef DIGIKAM_ICC_PROFILES_COMBOBOX_H #define DIGIKAM_ICC_PROFILES_COMBOBOX_H // Qt includes #include #include #include // Local includes #include "squeezedcombobox.h" #include "digikam_export.h" #include "iccprofile.h" namespace Digikam { class DIGIKAM_EXPORT IccProfilesComboBox : public SqueezedComboBox { Q_OBJECT public: + /** + * NOTE: Use the signal currentIndexChanged(int) for change notification + */ explicit IccProfilesComboBox(QWidget* const parent = nullptr); ~IccProfilesComboBox(); /** * Checks the given profiles for validity, creates a suitable description (ICC profile description, file path), * removes duplicates by file path, sorts them and adds them in sorted order. */ void addProfilesSqueezed(const QList& profiles); + /** * Add the given profile with the given description, or, if null, a standard description. * Does not test for duplicity, does not sort into existing profiles. */ void addProfileSqueezed(const IccProfile& profile, const QString& description = QString()); + /** * Clears, does the same as addProfilesSqueezed, and restores the current entry if possible. */ void replaceProfilesSqueezed(const QList& profiles); + /** * Sets a message the is displayed in the combo box and disables the combo box, * if the combo box is currently empty */ void setNoProfileIfEmpty(const QString& message); - /** Retrieves the current profile, or a null profile if none is selected. + /** + * Retrieves the current profile, or a null profile if none is selected. */ IccProfile currentProfile() const; - /** Sets the current profile. If profile is not in the list, sets no current item (-1) + /** + * Sets the current profile. If profile is not in the list, sets no current item (-1) */ void setCurrentProfile(const IccProfile& profile); - - /// Use the signal currentIndexChanged(int) for change notification }; // ---------------------------------------------------------------------------- class DIGIKAM_EXPORT IccRenderingIntentComboBox : public QComboBox { public: explicit IccRenderingIntentComboBox(QWidget* const parent = nullptr); void setIntent(int intent); int intent() const; }; // ---------------------------------------------------------------------------- class DIGIKAM_EXPORT IccProfilesMenuAction : public QMenu { Q_OBJECT public: IccProfilesMenuAction(const QIcon& icon, const QString& text, QObject* const parent); IccProfilesMenuAction(const QString& text, QObject* const parent); /** * Checks the given profiles for validity, creates a suitable description (ICC profile description, file path), * removes duplicates (in newly added list) by file path, sorts them and adds them in sorted order. */ void addProfiles(const QList& profile); /** * Add the given profile with the given description, or, if null, a standard description. * Does not test for duplicity, does not sort into existing profiles. */ void addProfile(const IccProfile& profile, const QString& description = QString()); /** * Equivalent to calling clear() and addProfiles(). */ void replaceProfiles(const QList& profile); /** * Disables if the menu is currently empty. */ void disableIfEmpty(); /** * Return the parent QObject. */ QObject* parentObject() const; Q_SIGNALS: void triggered(const IccProfile& profile); protected Q_SLOTS: void slotTriggered(QObject*); protected: QObject* m_parent; }; } // namespace Digikam #endif // DIGIKAM_ICC_PROFILES_COMBOBOX_H diff --git a/core/libs/widgets/iccprofiles/iccprofilewidget.cpp b/core/libs/widgets/iccprofiles/iccprofilewidget.cpp index e34da0553d..52ef100306 100644 --- a/core/libs/widgets/iccprofiles/iccprofilewidget.cpp +++ b/core/libs/widgets/iccprofiles/iccprofilewidget.cpp @@ -1,500 +1,538 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2006-06-23 * Description : a tab widget to display ICC profile infos * * Copyright (C) 2006-2020 by Gilles Caulier * * 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. * * ============================================================ */ #include "iccprofilewidget.h" // Qt includes #include #include #include #include #include #include // KDE includes #include // Local includes #include "iccprofile.h" #include "digikam_debug.h" #include "cietonguewidget.h" #include "metadatalistview.h" namespace { static const char* ICCHumanList[] = { "Icc.Header.ColorSpace", "Icc.Header.Copyright", "Icc.Header.DeviceClass", "Icc.Header.Name", "Icc.Header.Description", "Icc.Header.RenderingIntent", "-1" }; -// This entry list is only require for compatibility with MetadataWidget implementation. +/** + * NOTE: This entry list is only require for compatibility with MetadataWidget implementation. + */ static const char* ICCEntryList[] = { "Header", "-1" }; } namespace Digikam { class Q_DECL_HIDDEN ICCTagInfo { public: ICCTagInfo() { } ICCTagInfo(const QString& title, const QString& description) - : m_title(title), m_description(description) + : m_title(title), + m_description(description) { } QString title() const { return m_title; } QString description() const { return m_description; } private: QString m_title; QString m_description; }; typedef QMap ICCTagInfoMap; // --------------------------------------------------------------------------------------- class Q_DECL_HIDDEN ICCProfileWidget::Private { public: explicit Private() + : cieTongue(nullptr) { - cieTongue = nullptr; } IccProfile profile; QStringList keysFilter; CIETongueWidget* cieTongue; ICCTagInfoMap iccTagsDescription; }; ICCProfileWidget::ICCProfileWidget(QWidget* const parent, int w, int h) : MetadataWidget(parent), d(new Private) { setup(); dkCmsErrorAction(LCMS_ERROR_SHOW); // Set the translated ICC tags titles/descriptions list d->iccTagsDescription[QLatin1String("Icc.Header.Name")] = ICCTagInfo(i18n("Name"), i18n("The ICC profile product name")); d->iccTagsDescription[QLatin1String("Icc.Header.Description")] = ICCTagInfo(i18n("Description"), i18n("The ICC profile product description")); d->iccTagsDescription[QLatin1String("Icc.Header.Information")] = ICCTagInfo(i18n("Information"), i18n("Additional ICC profile information")); d->iccTagsDescription[QLatin1String("Icc.Header.Manufacturer")] = ICCTagInfo(i18n("Manufacturer"), i18n("Raw information about the ICC profile manufacturer")); d->iccTagsDescription[QLatin1String("Icc.Header.Model")] = ICCTagInfo(i18n("Model"), i18n("Raw information about the ICC profile model")); d->iccTagsDescription[QLatin1String("Icc.Header.Copyright")] = ICCTagInfo(i18n("Copyright"), i18n("Raw information about the ICC profile copyright")); d->iccTagsDescription[QLatin1String("Icc.Header.ProfileID")] = ICCTagInfo(i18n("Profile ID"), i18n("The ICC profile ID number")); d->iccTagsDescription[QLatin1String("Icc.Header.ColorSpace")] = ICCTagInfo(i18n("Color Space"), i18n("The color space used by the ICC profile")); d->iccTagsDescription[QLatin1String("Icc.Header.ConnectionSpace")] = ICCTagInfo(i18n("Connection Space"), i18n("The connection space used by the ICC profile")); d->iccTagsDescription[QLatin1String("Icc.Header.DeviceClass")] = ICCTagInfo(i18n("Device Class"), i18n("The ICC profile device class")); d->iccTagsDescription[QLatin1String("Icc.Header.RenderingIntent")] = ICCTagInfo(i18n("Rendering Intent"), i18n("The ICC profile rendering intent")); d->iccTagsDescription[QLatin1String("Icc.Header.ProfileVersion")] = ICCTagInfo(i18n("Profile Version"), i18n("The ICC version used to record the profile")); d->iccTagsDescription[QLatin1String("Icc.Header.CMMFlags")] = ICCTagInfo(i18n("CMM Flags"), i18n("The ICC profile color management flags")); // Set the list of keys and tags filters. + for (int i = 0 ; QLatin1String(ICCEntryList[i]) != QLatin1String("-1") ; ++i) { d->keysFilter << QLatin1String(ICCEntryList[i]); } QStringList tagsFilter; for (int i = 0 ; QLatin1String(ICCHumanList[i]) != QLatin1String("-1") ; ++i) { tagsFilter << QLatin1String(ICCHumanList[i]); } setTagsFilter(tagsFilter); // Add CIE tongue graph to the widget area d->cieTongue = new CIETongueWidget(w, h, this); d->cieTongue->setWhatsThis( i18n("

This area contains a CIE or chromaticity diagram. " "A CIE diagram is a representation of all the colors " "that a person with normal vision can see. This is represented " "by the colored sail-shaped area. In addition you will see a " "triangle that is superimposed on the diagram outlined in white. " "This triangle represents the outer boundaries of the color space " "of the device that is characterized by the inspected profile. " "This is called the device gamut.

" "

In addition there are black dots and yellow lines on the diagram. " "Each black dot represents one of the measurement points that were " "used to create this profile. The yellow line represents the " "amount that each point is corrected by the profile, and the " "direction of this correction.

")); setUserAreaWidget(d->cieTongue); decodeMetadata(); } ICCProfileWidget::~ICCProfileWidget() { delete d; } bool ICCProfileWidget::setProfile(const IccProfile& profile) { // Cleanup all metadata contents. + setMetadataMap(); d->profile = profile; if (!d->profile.open()) { setMetadataEmpty(); d->cieTongue->setProfileData(); d->profile = IccProfile(); return false; } // Try to decode current metadata. + enabledToolButtons(decodeMetadata()); // Refresh view using decoded metadata. + buildView(); + return true; } IccProfile ICCProfileWidget::getProfile() const { return d->profile; } void ICCProfileWidget::setDataLoading() { d->cieTongue->loadingStarted(); } void ICCProfileWidget::setLoadingFailed() { d->cieTongue->loadingFailed(); } void ICCProfileWidget::setUncalibratedColor() { d->cieTongue->uncalibratedColor(); } QString ICCProfileWidget::getMetadataTitle() { return i18n("ICC Color Profile Information"); } bool ICCProfileWidget::loadFromURL(const QUrl& url) { setFileName(url.toLocalFile()); if (url.isEmpty()) { setProfile(IccProfile()); d->cieTongue->setProfileData(); return false; } else { IccProfile profile(url.toLocalFile()); if (!setProfile(profile)) { setProfile(IccProfile()); d->cieTongue->setProfileData(); return false; } } return true; } bool ICCProfileWidget::loadFromProfileData(const QString& fileName, const QByteArray& data) { setFileName(fileName); + return(setProfile(IccProfile(data))); } bool ICCProfileWidget::loadProfile(const QString& fileName, const IccProfile& profile) { setFileName(fileName); + return(setProfile(profile)); } bool ICCProfileWidget::decodeMetadata() { if (!d->profile.isOpen()) { return false; } d->cieTongue->setProfileData(d->profile.data()); LcmsLock lock; cmsHPROFILE hProfile = d->profile.handle(); if (!hProfile) { qCDebug(DIGIKAM_WIDGETS_LOG) << "Cannot parse ICC profile tags using LCMS"; return false; } DMetadata::MetaDataMap metaDataMap; if (!QString(dkCmsTakeProductName(hProfile)).isEmpty()) { metaDataMap.insert(QLatin1String("Icc.Header.Name"), dkCmsTakeProductName(hProfile).replace(QLatin1Char('\n'), QLatin1Char(' '))); } if (!dkCmsTakeProductDesc(hProfile).isEmpty()) { metaDataMap.insert(QLatin1String("Icc.Header.Description"), dkCmsTakeProductDesc(hProfile).replace(QLatin1Char('\n'), QLatin1Char(' '))); } if (!dkCmsTakeProductInfo(hProfile).isEmpty()) { metaDataMap.insert(QLatin1String("Icc.Header.Information"), dkCmsTakeProductInfo(hProfile).replace(QLatin1Char('\n'), QLatin1Char(' '))); } if (!dkCmsTakeManufacturer(hProfile).isEmpty()) { metaDataMap.insert(QLatin1String("Icc.Header.Manufacturer"), dkCmsTakeManufacturer(hProfile).replace(QLatin1Char('\n'), QLatin1Char(' '))); } if (!dkCmsTakeModel(hProfile).isEmpty()) { metaDataMap.insert(QLatin1String("Icc.Header.Model"), dkCmsTakeModel(hProfile).replace(QLatin1Char('\n'), QLatin1Char(' '))); } if (!dkCmsTakeCopyright(hProfile).isEmpty()) { metaDataMap.insert(QLatin1String("Icc.Header.Copyright"), dkCmsTakeCopyright(hProfile).replace(QLatin1Char('\n'), QLatin1Char(' '))); } metaDataMap.insert(QLatin1String("Icc.Header.ProfileID"), QString::number((uint)*dkCmsTakeProfileID(hProfile))); metaDataMap.insert(QLatin1String("Icc.Header.ProfileVersion"), QString::number((uint)dkCmsGetProfileICCversion(hProfile))); metaDataMap.insert(QLatin1String("Icc.Header.CMMFlags"), QString::number((uint)dkCmsTakeHeaderFlags(hProfile))); QString colorSpace; switch (dkCmsGetColorSpace(hProfile)) { case icSigLabData: colorSpace = i18n("Lab"); break; + case icSigLuvData: colorSpace = i18n("Luv"); break; + case icSigRgbData: colorSpace = i18n("RGB"); break; + case icSigGrayData: colorSpace = i18n("GRAY"); break; + case icSigHsvData: colorSpace = i18n("HSV"); break; + case icSigHlsData: colorSpace = i18n("HLS"); break; + case icSigCmykData: colorSpace = i18n("CMYK"); break; + case icSigCmyData: colorSpace= i18n("CMY"); break; + default: colorSpace = i18n("Unknown"); break; } metaDataMap.insert(QLatin1String("Icc.Header.ColorSpace"), colorSpace); QString connectionSpace; switch (dkCmsGetPCS(hProfile)) { case icSigLabData: connectionSpace = i18n("Lab"); break; + case icSigLuvData: connectionSpace = i18n("Luv"); break; + case icSigRgbData: connectionSpace = i18n("RGB"); break; + case icSigGrayData: connectionSpace = i18n("GRAY"); break; + case icSigHsvData: connectionSpace = i18n("HSV"); break; + case icSigHlsData: connectionSpace = i18n("HLS"); break; case icSigCmykData: connectionSpace = i18n("CMYK"); break; + case icSigCmyData: connectionSpace= i18n("CMY"); break; + default: connectionSpace = i18n("Unknown"); break; } metaDataMap.insert(QLatin1String("Icc.Header.ConnectionSpace"), connectionSpace); QString device; switch ((int)dkCmsGetDeviceClass(hProfile)) { case icSigInputClass: device = i18n("Input device"); break; + case icSigDisplayClass: device = i18n("Display device"); break; + case icSigOutputClass: device = i18n("Output device"); break; + case icSigColorSpaceClass: device = i18n("Color space"); break; + case icSigLinkClass: device = i18n("Link device"); break; + case icSigAbstractClass: device = i18n("Abstract"); break; + case icSigNamedColorClass: device = i18n("Named color"); break; + default: device = i18n("Unknown"); break; } metaDataMap.insert(QLatin1String("Icc.Header.DeviceClass"), device); QString intent; switch (dkCmsTakeRenderingIntent(hProfile)) { case 0: intent = i18n("Perceptual"); break; + case 1: intent = i18n("Relative Colorimetric"); break; + case 2: intent = i18n("Saturation"); break; + case 3: intent = i18n("Absolute Colorimetric"); break; + default: intent = i18n("Unknown"); break; } metaDataMap.insert(QLatin1String("Icc.Header.RenderingIntent"), intent); // Update all metadata contents. + setMetadataMap(metaDataMap); + return true; } void ICCProfileWidget::buildView() { if (getMode() == CUSTOM) { setIfdList(getMetadataMap(), d->keysFilter, getTagsFilter()); } else { setIfdList(getMetadataMap(), d->keysFilter, QStringList() << QLatin1String("FULL")); } MetadataWidget::buildView(); } QString ICCProfileWidget::getTagTitle(const QString& key) { ICCTagInfoMap::const_iterator it = d->iccTagsDescription.constFind(key); if (it != d->iccTagsDescription.constEnd()) { return(it.value().title()); } return key.section(QLatin1Char('.'), 2, 2); } void ICCProfileWidget::slotSaveMetadataToFile() { QUrl url = saveMetadataToFile(i18n("ICC color profile File to Save"), QString(QLatin1String("*.icc *.icm|") + i18n("ICC Files (*.icc; *.icm)"))); storeMetadataToFile(url, d->profile.data()); } QString ICCProfileWidget::getTagDescription(const QString& key) { ICCTagInfoMap::const_iterator it = d->iccTagsDescription.constFind(key); if (it != d->iccTagsDescription.constEnd()) { return(it.value().description()); } return key.section(QLatin1Char('.'), 2, 2); } } // namespace Digikam diff --git a/core/libs/widgets/iccprofiles/iccprofilewidget.h b/core/libs/widgets/iccprofiles/iccprofilewidget.h index 38e53a90b6..c299b42444 100644 --- a/core/libs/widgets/iccprofiles/iccprofilewidget.h +++ b/core/libs/widgets/iccprofiles/iccprofilewidget.h @@ -1,84 +1,86 @@ /* ============================================================ * * This file is a part of digiKam project * https://www.digikam.org * * Date : 2006-06-23 * Description : a tab widget to display ICC profile infos * * Copyright (C) 2006-2020 by Gilles Caulier * * 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. * * ============================================================ */ #ifndef DIGIKAM_ICC_PROFILE_WIDGET_H #define DIGIKAM_ICC_PROFILE_WIDGET_H // Qt includes #include #include // Local includes #include "metadatawidget.h" #include "digikam_export.h" namespace Digikam { class IccProfile; class DIGIKAM_EXPORT ICCProfileWidget : public MetadataWidget { Q_OBJECT public: explicit ICCProfileWidget(QWidget* const parent, int w=256, int h=256); ~ICCProfileWidget(); - bool loadFromURL(const QUrl& url) override; - bool loadFromProfileData(const QString& fileName, const QByteArray& data); - bool loadProfile(const QString& fileName, const IccProfile& data); + bool loadFromURL(const QUrl& url) override; + bool loadFromProfileData(const QString& fileName, + const QByteArray& data); + bool loadProfile(const QString& fileName, + const IccProfile& data); - QString getTagDescription(const QString& key) override; - QString getTagTitle(const QString& key) override; + QString getTagDescription(const QString& key) override; + QString getTagTitle(const QString& key) override; - QString getMetadataTitle() override; + QString getMetadataTitle() override; void setLoadingFailed(); void setDataLoading(); void setUncalibratedColor(); bool setProfile(const IccProfile& profile); - IccProfile getProfile() const; + IccProfile getProfile() const; protected Q_SLOTS: - virtual void slotSaveMetadataToFile() override; + virtual void slotSaveMetadataToFile() override; private: - bool decodeMetadata() override; - void buildView() override; + bool decodeMetadata() override; + void buildView() override; private: class Private; Private* const d; }; } // namespace Digikam #endif // DIGIKAM_ICC_PROFILE_WIDGET_H