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 }