File indexing completed on 2024-05-12 15:59:34

0001 /*
0002  *  SPDX-FileCopyrightText: 2020 Peter Schatz <voronwe13@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 
0008 #ifndef KOCOLORSPACEPRESERVELIGHTNESSUTILS_H
0009 #define KOCOLORSPACEPRESERVELIGHTNESSUTILS_H
0010 
0011 #include <KoColorSpaceMaths.h>
0012 #include "kis_global.h"
0013 
0014 template<typename CSTraits>
0015 inline static void fillGrayBrushWithColorPreserveLightnessRGB(quint8 *pixels, const QRgb *brush, quint8 *brushColor, qreal strength, qint32 nPixels) {
0016     using RGBPixel = typename CSTraits::Pixel;
0017         using channels_type = typename CSTraits::channels_type;
0018         static const quint32 pixelSize = CSTraits::pixelSize;
0019 
0020         const RGBPixel *srcColorRGB = reinterpret_cast<const RGBPixel*>(brushColor);
0021 
0022         const float srcColorR = KoColorSpaceMaths<channels_type, float>::scaleToA(srcColorRGB->red);
0023         const float srcColorG = KoColorSpaceMaths<channels_type, float>::scaleToA(srcColorRGB->green);
0024         const float srcColorB = KoColorSpaceMaths<channels_type, float>::scaleToA(srcColorRGB->blue);
0025         const float srcColorA = KoColorSpaceMaths<channels_type, float>::scaleToA(srcColorRGB->alpha);
0026 
0027         /**
0028          * Lightness mixing algorithm is developed by Peter Schatz <voronwe13@gmail.com>
0029          *
0030          * We use a formula f(x) where f(0) = 0, f(1) = 1, and f(.5) = z,
0031          * where z is the lightness of the brush color. This can’t be linear unless
0032          * the color chosen is also .5. So we use a quadratic equation:
0033          *
0034          * f(x) = ax^2 + b^x +c
0035          * 0,0 -> 0 = a0^2 + b0 + c -> c = 0
0036          * 1,1 -> 1 = a1^2 +b1 + c -> 1 = a + b + 0 -> a = 1 - b
0037          * .5,z -> z = a*.5^2 + b*.5 + c -> z =
0038          *           = a/4 + b/2 + 0 -> z =
0039          *           = 1/4 - b/4 + b/2 -> z = 1/4 + b/4 -> b = 4z - 1
0040          *
0041          * f(x) = (1 - (4z - 1)) * x^2 + (4z - 1) * x
0042          */
0043 
0044         const float srcColorL = getLightness<HSLType, float>(srcColorR, srcColorG, srcColorB);
0045         const float lightnessB = 4 * srcColorL - 1;
0046         const float lightnessA = 1 - lightnessB;
0047 
0048         for (; nPixels > 0; --nPixels, pixels += pixelSize, ++brush) {
0049             float brushMaskL = qRed(*brush) / 255.0f;
0050             brushMaskL = (brushMaskL - 0.5) * strength + 0.5;
0051             const float finalAlpha = qMin(qAlpha(*brush) / 255.0f, srcColorA);
0052             float finalLightness = lightnessA * pow2(brushMaskL) + lightnessB * brushMaskL;
0053             finalLightness = qBound(0.0f, finalLightness, 1.0f);
0054 
0055             float pixelR = srcColorR;
0056             float pixelG = srcColorG;
0057             float pixelB = srcColorB;
0058 
0059             setLightness<HSLType, float>(pixelR, pixelG, pixelB, finalLightness);
0060 
0061             RGBPixel *pixelRGB = reinterpret_cast<RGBPixel*>(pixels);
0062             pixelRGB->red = KoColorSpaceMaths<float, channels_type>::scaleToA(pixelR);
0063             pixelRGB->green = KoColorSpaceMaths<float, channels_type>::scaleToA(pixelG);
0064             pixelRGB->blue = KoColorSpaceMaths<float, channels_type>::scaleToA(pixelB);
0065             pixelRGB->alpha = KoColorSpaceMaths<quint8, channels_type>::scaleToA(quint8(finalAlpha * 255));
0066         }
0067 }
0068 
0069 template<typename CSTraits>
0070 inline static void modulateLightnessByGrayBrushRGB(quint8 *pixels, const QRgb *brush, qreal strength, qint32 nPixels) {
0071     using RGBPixel = typename CSTraits::Pixel;
0072         using channels_type = typename CSTraits::channels_type;
0073         static const quint32 pixelSize = CSTraits::pixelSize;
0074 
0075 
0076         /**
0077          * Lightness mixing algorithm is developed by Peter Schatz <voronwe13@gmail.com>
0078          *
0079          * We use a formula f(x) where f(0) = 0, f(1) = 1, and f(.5) = z,
0080          * where z is the lightness of the brush color. This can’t be linear unless
0081          * the color chosen is also .5. So we use a quadratic equation:
0082          *
0083          * f(x) = ax^2 + b^x +c
0084          * 0,0 -> 0 = a0^2 + b0 + c -> c = 0
0085          * 1,1 -> 1 = a1^2 +b1 + c -> 1 = a + b + 0 -> a = 1 - b
0086          * .5,z -> z = a*.5^2 + b*.5 + c -> z =
0087          *           = a/4 + b/2 + 0 -> z =
0088          *           = 1/4 - b/4 + b/2 -> z = 1/4 + b/4 -> b = 4z - 1
0089          *
0090          * f(x) = (1 - (4z - 1)) * x^2 + (4z - 1) * x
0091          */
0092 
0093         for (; nPixels > 0; --nPixels, pixels += pixelSize, ++brush) {
0094 
0095             RGBPixel *pixelRGB = reinterpret_cast<RGBPixel*>(pixels);
0096 
0097             const float srcColorR = KoColorSpaceMaths<channels_type, float>::scaleToA(pixelRGB->red);
0098             const float srcColorG = KoColorSpaceMaths<channels_type, float>::scaleToA(pixelRGB->green);
0099             const float srcColorB = KoColorSpaceMaths<channels_type, float>::scaleToA(pixelRGB->blue);
0100             //const float srcColorA = KoColorSpaceMaths<channels_type, float>::scaleToA(pixelRGB->alpha);
0101 
0102             const float srcColorL = getLightness<HSLType, float>(srcColorR, srcColorG, srcColorB);
0103             float brushMaskL = qRed(*brush) / 255.0f;
0104             brushMaskL = (brushMaskL - 0.5) * strength * qAlpha(*brush) / 255.0 + 0.5;
0105 
0106             const float lightnessB = 4 * srcColorL - 1;
0107             const float lightnessA = 1 - lightnessB;
0108 
0109             float finalLightness = lightnessA * pow2(brushMaskL) + lightnessB * brushMaskL;
0110             finalLightness = qBound(0.0f, finalLightness, 1.0f);
0111 
0112             float pixelR = srcColorR;
0113             float pixelG = srcColorG;
0114             float pixelB = srcColorB;
0115 
0116             setLightness<HSLType, float>(pixelR, pixelG, pixelB, finalLightness);
0117 
0118             pixelRGB->red = KoColorSpaceMaths<float, channels_type>::scaleToA(pixelR);
0119             pixelRGB->green = KoColorSpaceMaths<float, channels_type>::scaleToA(pixelG);
0120             pixelRGB->blue = KoColorSpaceMaths<float, channels_type>::scaleToA(pixelB);
0121             //pixelRGB->alpha = KoColorSpaceMaths<quint8, channels_type>::scaleToA(quint8(finalAlpha * 255));
0122         }
0123 }
0124 
0125 
0126 #endif // KOCOLORSPACEPRESERVELIGHTNESSUTILS_H