diff --git a/libs/pigment/KoChannelInfo.h b/libs/pigment/KoChannelInfo.h index 35e9fb11fe..3b88eee6e7 100644 --- a/libs/pigment/KoChannelInfo.h +++ b/libs/pigment/KoChannelInfo.h @@ -1,286 +1,287 @@ /* * Copyright (c) 2004 Boudewijn Rempt + * Copyright (c) 2020 L. E. Segovia * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KOCHANNELINFO_H_ #define KOCHANNELINFO_H_ #include #include #include #include /** * This class gives some basic information about a channel, * that is, one of the components that makes up a particular * pixel. */ class KoChannelInfo { public: /** * Used to represent a min and max range. */ struct DoubleRange { public: double minVal, maxVal; public: /// creates an invalid range of 0,0 DoubleRange(void) : minVal(0), maxVal(0) { } /// creates DoubleRange(qreal _minVal, qreal _maxVal) : minVal(_minVal), maxVal(_maxVal) { Q_ASSERT(minVal <= maxVal); } /// true if this range is usable bool isValid(void) const { return minVal < maxVal; } }; public: /// enum to define the type of the channel enum enumChannelType { COLOR, ///< The channel represents a color ALPHA ///< The channel represents the opacity of a pixel //SUBSTANCE, ///< The channel represents a real-world substance like pigments or medium //SUBSTRATE ///< The channel represents a real-world painting substrate like a canvas }; /// enum to define the value of the channel enum enumChannelValueType { UINT8, ///< use this for an unsigned integer 8bits channel UINT16, ///< use this for an integer 16bits channel UINT32, ///< use this for an unsigned integer 21bits channel FLOAT16, ///< use this for a float 16bits channel FLOAT32, ///< use this for a float 32bits channel FLOAT64, ///< use this for a float 64bits channel INT8, ///< use this for an integer 8bits channel INT16, ///< use this for an integer 16bits channel OTHER ///< Use this if the channel is neither an integer or a float }; public: KoChannelInfo() { } /** * @param name of the channel * @param npos position of the channel in the pixel (in bytes) * @param displayPosition the position of the channel in the user-visible order * @param channelType type of the channel * @param channelValueType type of the numerical data used by the channel * @param size number of bytes (not bits) of the channel (if -1, it is deduced from the channelType) * @param color a color to represent that channel (for instance in an histogram) * @param uiMinMax the UI range */ KoChannelInfo(const QString & name, qint32 npos, qint32 displayPosition, enumChannelType channelType, enumChannelValueType channelValueType, qint32 size = -1, const QColor &color = QColor(0, 0, 0), const DoubleRange &uiMinMax = DoubleRange()) : m_name(name) , m_pos(npos) , m_displayPosition(displayPosition) , m_channelType(channelType) , m_channelValueType(channelValueType) , m_size(size) , m_color(color) , m_uiMinMax(uiMinMax) { switch(m_channelValueType) { case UINT8: case INT8: Q_ASSERT(m_size == -1 || m_size == 1); m_size = 1; break; case UINT16: case INT16: Q_ASSERT(m_size == -1 || m_size == 2); m_size = 2; break; case UINT32: Q_ASSERT(m_size == -1 || m_size == 4); m_size = 4; break; case FLOAT16: Q_ASSERT(m_size == -1 || m_size == 2); m_size = 2; break; case FLOAT32: Q_ASSERT(m_size == -1 || m_size == 4); m_size = 4; break; case FLOAT64: Q_ASSERT(m_size == -1 || m_size == 8); m_size = 8; break; case OTHER: Q_ASSERT(m_size != -1); } if (!uiMinMax.isValid()) { switch (m_channelValueType) { case UINT8: m_uiMinMax.minVal = std::numeric_limits::min(); m_uiMinMax.maxVal = std::numeric_limits::max(); break; case INT8: m_uiMinMax.minVal = std::numeric_limits::min(); m_uiMinMax.maxVal = std::numeric_limits::max(); break; case UINT16: m_uiMinMax.minVal = std::numeric_limits::min(); m_uiMinMax.maxVal = std::numeric_limits::max(); break; case INT16: m_uiMinMax.minVal = std::numeric_limits::min(); m_uiMinMax.maxVal = std::numeric_limits::max(); break; case UINT32: m_uiMinMax.minVal = std::numeric_limits::min(); m_uiMinMax.maxVal = std::numeric_limits::max(); break; default: // assume real otherwise, which is 0..1 by default m_uiMinMax.minVal = 0.0; m_uiMinMax.maxVal = 1.0; break; } } Q_ASSERT(m_uiMinMax.isValid()); } public: /** * converts the display position to the pixel-order index in the channels vector. */ static int displayPositionToChannelIndex(int displayPosition, const QList &channels) { for (int i = 0; i < channels.size(); ++i) { if (channels.at(i)->displayPosition() == displayPosition) { return i; } } return -1; } static QList displayOrderSorted(const QList &channels) { QList sortedChannels; for (int i = 0; i < channels.size(); ++i) { Q_FOREACH (KoChannelInfo* channel, channels) { if (channel->displayPosition() == i) { sortedChannels << channel; break; } } } Q_ASSERT(channels.size() == sortedChannels.size()); return sortedChannels; } /** * User-friendly name for this channel for presentation purposes in the gui */ inline QString name() const { return m_name; } /** * @return the position of the first byte of the channel in the pixel */ inline qint32 pos() const { return m_pos; } /** * @return the displayPosition of the channel in the pixel */ inline qint32 displayPosition() const { return m_displayPosition; } /** * @return the number of bytes this channel takes */ inline qint32 size() const { return m_size; } /** * @return the type of the channel */ inline enumChannelType channelType() const { return m_channelType; } /** * @return the type of the value of the channel (float, uint8 or uint16) */ inline enumChannelValueType channelValueType() const { return m_channelValueType; } /** * This is a color that can be used to represent this channel in histograms and so. * By default this is black, so keep in mind that many channels might look the same */ inline QColor color() const { return m_color; } /** * A channel is less than another channel if its pos is smaller. */ inline bool operator<(const KoChannelInfo & info) { return m_pos < info.m_pos; } /** * Gets the minimum value that this channel should have. * This is suitable for UI use. */ inline double getUIMin(void) const { return m_uiMinMax.minVal; } /** * Gets the minimum value that this channel should have. * This is suitable for UI use. */ inline double getUIMax(void) const { return m_uiMinMax.maxVal; } /** * @brief getUIUnitValue * Gets the unit value for this channel. * This is suitable for converting between representations. */ inline double getUIUnitValue(void) const { return m_uiMinMax.maxVal - m_uiMinMax.minVal; } private: QString m_name; qint32 m_pos; qint32 m_displayPosition; enumChannelType m_channelType; enumChannelValueType m_channelValueType; qint32 m_size; QColor m_color; DoubleRange m_uiMinMax; }; #endif // KOCHANNELINFO_H_ diff --git a/plugins/color/lcms2engine/colorspaces/cmyk_f32/CmykF32ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/cmyk_f32/CmykF32ColorSpace.cpp index 42e83f6de5..6a55982afe 100644 --- a/plugins/color/lcms2engine/colorspaces/cmyk_f32/CmykF32ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/cmyk_f32/CmykF32ColorSpace.cpp @@ -1,143 +1,144 @@ /* * Copyright (c) 2006 Cyrille Berger + * Copyright (c) 2020 L. E. Segovia * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "CmykF32ColorSpace.h" #include #include #include #include "compositeops/KoCompositeOps.h" #include #include CmykF32ColorSpace::CmykF32ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_CMYKA_FLT, cmsSigCmykData, p) { const IccColorProfile *icc_p = dynamic_cast(p); Q_ASSERT(icc_p); QVector uiRanges(icc_p->getFloatUIMinMax()); Q_ASSERT(uiRanges.size() == 4); addChannel(new KoChannelInfo(i18n("Cyan"), 0 * sizeof(float), 0, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, sizeof(float), Qt::cyan, uiRanges[0])); addChannel(new KoChannelInfo(i18n("Magenta"), 1 * sizeof(float), 1, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, sizeof(float), Qt::magenta, uiRanges[1])); addChannel(new KoChannelInfo(i18n("Yellow"), 2 * sizeof(float), 2, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, sizeof(float), Qt::yellow, uiRanges[2])); addChannel(new KoChannelInfo(i18n("Black"), 3 * sizeof(float), 3, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, sizeof(float), Qt::black, uiRanges[3])); addChannel(new KoChannelInfo(i18n("Alpha"), 4 * sizeof(float), 4, KoChannelInfo::ALPHA, KoChannelInfo::FLOAT32, sizeof(float))); init(); dbgPlugins << "CMYK (float) profile bounds for: " << icc_p->name(); dbgPlugins << "C: " << uiRanges[0].minVal << uiRanges[0].maxVal; dbgPlugins << "M: " << uiRanges[1].minVal << uiRanges[1].maxVal; dbgPlugins << "Y: " << uiRanges[2].minVal << uiRanges[2].maxVal; dbgPlugins << "K: " << uiRanges[3].minVal << uiRanges[3].maxVal; addStandardCompositeOps(this); } bool CmykF32ColorSpace::willDegrade(ColorSpaceIndependence independence) const { if (independence == TO_RGBA16) { return true; } else { return false; } } KoColorSpace *CmykF32ColorSpace::clone() const { return new CmykF32ColorSpace(name(), profile()->clone()); } void CmykF32ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const KoCmykF32Traits::Pixel *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("CMYK"); // XML expects 0-1, we need 0-100 // Get the bounds from the channels and adjust the calculations labElt.setAttribute("c", KisDomUtils::toString(KoColorSpaceMaths< KoCmykF32Traits::channels_type, qreal>::scaleToA(1.f / this->channels()[0]->getUIUnitValue() * (p->cyan - this->channels()[0]->getUIMin())))); labElt.setAttribute("m", KisDomUtils::toString(KoColorSpaceMaths< KoCmykF32Traits::channels_type, qreal>::scaleToA(1.f / this->channels()[1]->getUIUnitValue() * (p->magenta - this->channels()[1]->getUIMin())))); labElt.setAttribute("y", KisDomUtils::toString(KoColorSpaceMaths< KoCmykF32Traits::channels_type, qreal>::scaleToA(1.f / this->channels()[2]->getUIUnitValue() * (p->yellow - this->channels()[2]->getUIMin())))); labElt.setAttribute("k", KisDomUtils::toString(KoColorSpaceMaths< KoCmykF32Traits::channels_type, qreal>::scaleToA(1.f / this->channels()[3]->getUIUnitValue() * (p->black - this->channels()[3]->getUIMin())))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void CmykF32ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { KoCmykF32Traits::Pixel *p = reinterpret_cast(pixel); p->cyan = this->channels()[0]->getUIMin() + KoColorSpaceMaths< qreal, KoCmykF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("c"))) * this->channels()[0]->getUIUnitValue(); p->magenta = this->channels()[1]->getUIMin() + KoColorSpaceMaths< qreal, KoCmykF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("m"))) * this->channels()[1]->getUIUnitValue(); p->yellow = this->channels()[2]->getUIMin() + KoColorSpaceMaths< qreal, KoCmykF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("y"))) * this->channels()[2]->getUIUnitValue(); p->black = this->channels()[2]->getUIMin() + KoColorSpaceMaths< qreal, KoCmykF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("k"))) * this->channels()[2]->getUIUnitValue(); p->alpha = 1.0; } void CmykF32ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { qreal c0 = channelValues[0]; qreal c1 = channelValues[1]; qreal c2 = channelValues[2]; qreal c3 = channelValues[3]; //we use HSI here because we can't linearise CMYK, and HSY doesn't work right with... CMYKToCMY(&c0, &c1, &c2, &c3); c0 = 1.0 - c0; c1 = 1.0 - c1; c2 = 1.0 - c2; RGBToHSI(c0, c1, c2, hue, sat, luma); } QVector CmykF32ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(5); channelValues.fill(1.0); HSIToRGB(*hue, *sat, *luma, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[0] = qBound(0.0,1.0-channelValues[0],1.0); channelValues[1] = qBound(0.0,1.0-channelValues[1],1.0); channelValues[2] = qBound(0.0,1.0-channelValues[2],1.0); CMYToCMYK(&channelValues[0],&channelValues[1],&channelValues[2],&channelValues[3]); return channelValues; } void CmykF32ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { qreal c0 = channelValues[0]; qreal c1 = channelValues[1]; qreal c2 = channelValues[2]; qreal c3 = channelValues[3]; CMYKToCMY(&c0, &c1, &c2, &c3); c0 = 1.0 - c0; c1 = 1.0 - c1; c2 = 1.0 - c2; RGBToYUV(c0, c1, c2, y, u, v, (1.0 - 0.299),(1.0 - 0.587), (1.0 - 0.114)); } QVector CmykF32ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(5); channelValues.fill(1.0); YUVToRGB(*y, *u, *v, &channelValues[0],&channelValues[1],&channelValues[2], 0.33, 0.33, 0.33); channelValues[0] = qBound(0.0,1.0-channelValues[0],1.0); channelValues[1] = qBound(0.0,1.0-channelValues[1],1.0); channelValues[2] = qBound(0.0,1.0-channelValues[2],1.0); CMYToCMYK(&channelValues[0],&channelValues[1],&channelValues[2],&channelValues[3]); return channelValues; } diff --git a/plugins/color/lcms2engine/colorspaces/lab_f32/LabF32ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/lab_f32/LabF32ColorSpace.cpp index f1f08a0dc2..a11749a3d0 100644 --- a/plugins/color/lcms2engine/colorspaces/lab_f32/LabF32ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/lab_f32/LabF32ColorSpace.cpp @@ -1,117 +1,118 @@ /* * Copyright (c) 2006 Cyrille Berger + * Copyright (c) 2020 L. E. Segovia * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "LabF32ColorSpace.h" #include #include #include "../compositeops/KoCompositeOps.h" #include #include LabF32ColorSpace::LabF32ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_LabA_FLT, cmsSigLabData, p) { const IccColorProfile *icc_p = dynamic_cast(p); Q_ASSERT(icc_p); QVector uiRanges(icc_p->getFloatUIMinMax()); Q_ASSERT(uiRanges.size() == 3); addChannel(new KoChannelInfo(i18n("Lightness"), 0 * sizeof(float), 0, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, sizeof(float), QColor(100, 100, 100), uiRanges[0])); addChannel(new KoChannelInfo(i18n("a*"), 1 * sizeof(float), 1, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, sizeof(float), QColor(150, 150, 150), uiRanges[1])); addChannel(new KoChannelInfo(i18n("b*"), 2 * sizeof(float), 2, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, sizeof(float), QColor(200, 200, 200), uiRanges[2])); addChannel(new KoChannelInfo(i18n("Alpha"), 3 * sizeof(float), 3, KoChannelInfo::ALPHA, KoChannelInfo::FLOAT32, sizeof(float))); init(); addStandardCompositeOps(this); dbgPlugins << "La*b* (float) channel bounds for: " << icc_p->name(); dbgPlugins << "L: " << uiRanges[0].minVal << uiRanges[0].maxVal; dbgPlugins << "a: " << uiRanges[1].minVal << uiRanges[1].maxVal; dbgPlugins << "b: " << uiRanges[2].minVal << uiRanges[2].maxVal; } bool LabF32ColorSpace::willDegrade(ColorSpaceIndependence independence) const { if (independence == TO_RGBA16) { return true; } else { return false; } } KoColorSpace *LabF32ColorSpace::clone() const { return new LabF32ColorSpace(name(), profile()->clone()); } void LabF32ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const KoLabF32Traits::Pixel *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("Lab"); // XML expects 0-1, we need 0-100, -128-+127 // Get the bounds from the channels and adjust the calculations labElt.setAttribute("L", KisDomUtils::toString(KoColorSpaceMaths< KoLabF32Traits::channels_type, qreal>::scaleToA(1.f / this->channels()[0]->getUIUnitValue() * (p->L - this->channels()[0]->getUIMin())))); labElt.setAttribute("a", KisDomUtils::toString(KoColorSpaceMaths< KoLabF32Traits::channels_type, qreal>::scaleToA(1.f / this->channels()[1]->getUIUnitValue() * (p->a - this->channels()[1]->getUIMin())))); labElt.setAttribute("b", KisDomUtils::toString(KoColorSpaceMaths< KoLabF32Traits::channels_type, qreal>::scaleToA(1.f / this->channels()[2]->getUIUnitValue() * (p->b - this->channels()[2]->getUIMin())))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void LabF32ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { KoLabF32Traits::Pixel *p = reinterpret_cast(pixel); p->L = this->channels()[0]->getUIMin() + KoColorSpaceMaths< qreal, KoLabF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("L"))) * this->channels()[0]->getUIUnitValue(); p->a = this->channels()[1]->getUIMin() + KoColorSpaceMaths< qreal, KoLabF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("a"))) * this->channels()[1]->getUIUnitValue(); p->b = this->channels()[2]->getUIMin() + KoColorSpaceMaths< qreal, KoLabF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("b"))) * this->channels()[2]->getUIUnitValue(); p->alpha = 1.0; } void LabF32ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { LabToLCH(channelValues[0],channelValues[1],channelValues[2], luma, sat, hue); } QVector LabF32ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); LCHToLab(*luma, *sat, *hue, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[3]=1.0; return channelValues; } void LabF32ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { *y =channelValues[0]; *u=channelValues[1]; *v=channelValues[2]; } QVector LabF32ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); channelValues[0]=*y; channelValues[1]=*u; channelValues[2]=*v; channelValues[3]=1.0; return channelValues; }