File indexing completed on 2024-06-16 04:16:37

0001 /*
0002  * This file is part of Krita
0003  *
0004  * SPDX-FileCopyrightText: 2004 Cyrille Berger <cberger@cberger.net>
0005  * SPDX-FileCopyrightText: 2021 Deif Lou <giniba@gmail.net>
0006  *
0007  *  SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include <KoColorSpace.h>
0011 #include <KoChannelInfo.h>
0012 #include <KoColorModelStandardIds.h>
0013 #include <kis_assert.h>
0014 #include <KoCompositeColorTransformation.h>
0015 #include <kis_cubic_curve.h>
0016 
0017 #include "../../color/colorspaceextensions/kis_hsv_adjustment.h"
0018 
0019 #include "kis_multichannel_utils.h"
0020 
0021 namespace KisMultiChannelUtils {
0022 
0023 QVector<VirtualChannelInfo> getVirtualChannels(const KoColorSpace *cs,
0024                                                int maxChannels,
0025                                                bool supportsLightness,
0026                                                bool supportsHue,
0027                                                bool supportsSaturation)
0028 {
0029     supportsLightness =
0030         supportsLightness &&
0031         cs->colorModelId() != LABAColorModelID &&
0032         cs->colorModelId() != GrayAColorModelID &&
0033         cs->colorModelId() != GrayColorModelID &&
0034         cs->colorModelId() != AlphaColorModelID;
0035 
0036     supportsHue = supportsHue && supportsLightness;
0037     supportsSaturation = supportsSaturation && supportsLightness;
0038 
0039     QVector<VirtualChannelInfo> vchannels;
0040 
0041     QList<KoChannelInfo *> sortedChannels =
0042         KoChannelInfo::displayOrderSorted(cs->channels());
0043 
0044     if (supportsLightness) {
0045         vchannels << VirtualChannelInfo(VirtualChannelInfo::ALL_COLORS, -1, 0, cs);
0046     }
0047 
0048     Q_FOREACH (KoChannelInfo *channel, sortedChannels) {
0049         int pixelIndex = KoChannelInfo::displayPositionToChannelIndex(channel->displayPosition(), cs->channels());
0050         vchannels << VirtualChannelInfo(VirtualChannelInfo::REAL, pixelIndex, channel, cs);
0051     }
0052 
0053     if (supportsHue) {
0054         vchannels << VirtualChannelInfo(VirtualChannelInfo::HUE, -1, 0, cs);
0055     }
0056 
0057     if (supportsSaturation) {
0058         vchannels << VirtualChannelInfo(VirtualChannelInfo::SATURATION, -1, 0, cs);
0059     }
0060 
0061     if (supportsLightness) {
0062         vchannels << VirtualChannelInfo(VirtualChannelInfo::LIGHTNESS, -1, 0, cs);
0063     }
0064 
0065     if (maxChannels >= 0 && vchannels.size() > maxChannels) {
0066         vchannels.resize(maxChannels);
0067     }
0068 
0069     return vchannels;
0070 }
0071 
0072 int findChannel(const QVector<VirtualChannelInfo> &virtualChannels,
0073                 const VirtualChannelInfo::Type &channelType)
0074 {
0075     for (int i = 0; i < virtualChannels.size(); i++) {
0076         if (virtualChannels[i].type() == channelType) {
0077             return i;
0078         }
0079     }
0080     return -1;
0081 }
0082 
0083 KoColorTransformation* createPerChannelTransformationFromTransfers(const KoColorSpace *cs,
0084                                                                    const QVector<QVector<quint16>> &transfers,
0085                                                                    const QList<bool> &transferIsIdentity)
0086 {
0087     /**
0088      * TODO: What about the order of channels? (DK)
0089      *
0090      * Virtual channels are sorted in display order, does Lcms accepts
0091      * transforms in display order? Why on Earth it works?! Is it
0092      * documented anywhere?
0093      */
0094     const QVector<VirtualChannelInfo> virtualChannels = getVirtualChannels(cs, transfers.size());
0095 
0096     if (transfers.size() > int(virtualChannels.size())) {
0097         // We got an illegal number of colorchannels :(
0098         return 0;
0099     }
0100 
0101     bool colorsNull = true;
0102     bool hueNull = true;
0103     bool saturationNull = true;
0104     bool lightnessNull = true;
0105     bool allColorsNull = true;
0106     int alphaIndexInReal = -1;
0107 
0108     QVector<QVector<quint16> > realTransfers;
0109     QVector<quint16> hueTransfer;
0110     QVector<quint16> saturationTransfer;
0111     QVector<quint16> lightnessTransfer;
0112     QVector<quint16> allColorsTransfer;
0113 
0114     for (int i = 0; i < virtualChannels.size(); i++) {
0115         if (virtualChannels[i].type() == VirtualChannelInfo::REAL) {
0116             realTransfers << transfers[i];
0117 
0118             if (virtualChannels[i].isAlpha()) {
0119                 alphaIndexInReal = realTransfers.size() - 1;
0120             }
0121 
0122             if (colorsNull && !transferIsIdentity[i]) {
0123                 colorsNull = false;
0124             }
0125         } else if (virtualChannels[i].type() == VirtualChannelInfo::HUE) {
0126             KIS_ASSERT_RECOVER_NOOP(hueTransfer.isEmpty());
0127             hueTransfer = transfers[i];
0128 
0129             if (hueNull && !transferIsIdentity[i]) {
0130                 hueNull = false;
0131             }
0132         } else if (virtualChannels[i].type() == VirtualChannelInfo::SATURATION) {
0133             KIS_ASSERT_RECOVER_NOOP(saturationTransfer.isEmpty());
0134             saturationTransfer = transfers[i];
0135 
0136             if (saturationNull && !transferIsIdentity[i]) {
0137                 saturationNull = false;
0138             }
0139         } else if (virtualChannels[i].type() == VirtualChannelInfo::LIGHTNESS) {
0140             KIS_ASSERT_RECOVER_NOOP(lightnessTransfer.isEmpty());
0141             lightnessTransfer = transfers[i];
0142 
0143             if (lightnessNull && !transferIsIdentity[i]) {
0144                 lightnessNull = false;
0145             }
0146         } else if (virtualChannels[i].type() == VirtualChannelInfo::ALL_COLORS) {
0147             KIS_ASSERT_RECOVER_NOOP(allColorsTransfer.isEmpty());
0148             allColorsTransfer = transfers[i];
0149 
0150             if (allColorsNull && !transferIsIdentity[i]) {
0151                 allColorsNull = false;
0152             }
0153         }
0154     }
0155 
0156     KoColorTransformation *hueTransform = 0;
0157     KoColorTransformation *saturationTransform = 0;
0158     KoColorTransformation *lightnessTransform = 0;
0159     KoColorTransformation *allColorsTransform = 0;
0160     KoColorTransformation *colorTransform = 0;
0161 
0162     /**
0163      * Sometimes the realTransfers are too low, this often happens with faulty config,
0164      * which in turn leads to trouble when creating a transform.
0165      */
0166     int missingTransfers = qMax(0, int(cs->channelCount()-realTransfers.size()));
0167     for (int i=0; i < missingTransfers; i++) {
0168         realTransfers.append(KisCubicCurve().uint16Transfer());
0169     }
0170 
0171     if (!colorsNull) {
0172         const quint16** transfers = new const quint16*[realTransfers.size()];
0173         for(int i = 0; i < realTransfers.size(); ++i) {
0174             transfers[i] = realTransfers[i].constData();
0175 
0176             /**
0177              * createPerChannelAdjustment() expects alpha channel to
0178              * be the last channel in the list, so just it here
0179              */
0180             KIS_ASSERT_RECOVER_NOOP(i != alphaIndexInReal ||
0181                                     alphaIndexInReal == (realTransfers.size() - 1));
0182         }
0183 
0184         colorTransform = cs->createPerChannelAdjustment(transfers);
0185         delete [] transfers;
0186     }
0187 
0188     if (!hueNull) {
0189         QHash<QString, QVariant> params;
0190         params["curve"] = QVariant::fromValue(hueTransfer);
0191         params["channel"] = KisHSVCurve::Hue;
0192         params["relative"] = false;
0193         params["lumaRed"]   = cs->lumaCoefficients()[0];
0194         params["lumaGreen"] = cs->lumaCoefficients()[1];
0195         params["lumaBlue"]  = cs->lumaCoefficients()[2];
0196 
0197         hueTransform = cs->createColorTransformation("hsv_curve_adjustment", params);
0198     }
0199 
0200     if (!saturationNull) {
0201         QHash<QString, QVariant> params;
0202         params["curve"] = QVariant::fromValue(saturationTransfer);
0203         params["channel"] = KisHSVCurve::Saturation;
0204         params["relative"] = false;
0205         params["lumaRed"]   = cs->lumaCoefficients()[0];
0206         params["lumaGreen"] = cs->lumaCoefficients()[1];
0207         params["lumaBlue"]  = cs->lumaCoefficients()[2];
0208 
0209         saturationTransform = cs->createColorTransformation("hsv_curve_adjustment", params);
0210     }
0211 
0212     if (!lightnessNull) {
0213         lightnessTransform = cs->createBrightnessContrastAdjustment(lightnessTransfer.constData());
0214     }
0215 
0216     if (!allColorsNull) {
0217         const quint16** allColorsTransfers = new const quint16*[realTransfers.size()];
0218         for(int i = 0; i < realTransfers.size(); ++i) {
0219             allColorsTransfers[i] = (i != alphaIndexInReal) ?
0220                 allColorsTransfer.constData() : 0;
0221 
0222             /**
0223              * createPerChannelAdjustment() expects alpha channel to
0224              * be the last channel in the list, so just it here
0225              */
0226             KIS_ASSERT_RECOVER_NOOP(i != alphaIndexInReal ||
0227                                     alphaIndexInReal == (realTransfers.size() - 1));
0228         }
0229 
0230         allColorsTransform = cs->createPerChannelAdjustment(allColorsTransfers);
0231         delete[] allColorsTransfers;
0232     }
0233 
0234     QVector<KoColorTransformation*> allTransforms;
0235     allTransforms << colorTransform;
0236     allTransforms << allColorsTransform;
0237     allTransforms << hueTransform;
0238     allTransforms << saturationTransform;
0239     allTransforms << lightnessTransform;
0240 
0241     return KoCompositeColorTransformation::createOptimizedCompositeTransform(allTransforms);
0242 }
0243 
0244 }