KoColorSpacePreserveLightnessUtils.h 5.76 KB
Newer Older
1
/*
2
 *  SPDX-FileCopyrightText: 2020 Peter Schatz <voronwe13@gmail.com>
3
 *
Samuel Gaist's avatar
Samuel Gaist committed
4
 *  SPDX-License-Identifier: GPL-2.0-or-later
5
6
7
 */


8
9
10
11
#ifndef KOCOLORSPACEPRESERVELIGHTNESSUTILS_H
#define KOCOLORSPACEPRESERVELIGHTNESSUTILS_H

#include <KoColorSpaceMaths.h>
12
#include "kis_global.h"
13
14

template<typename CSTraits>
15
inline static void fillGrayBrushWithColorPreserveLightnessRGB(quint8 *pixels, const QRgb *brush, quint8 *brushColor, qreal strength, qint32 nPixels) {
16
    using RGBPixel = typename CSTraits::Pixel;
17
18
19
20
21
22
23
24
        using channels_type = typename CSTraits::channels_type;
        static const quint32 pixelSize = CSTraits::pixelSize;

        const RGBPixel *brushColorRGB = reinterpret_cast<const RGBPixel*>(brushColor);

        const float brushColorR = KoColorSpaceMaths<channels_type, float>::scaleToA(brushColorRGB->red);
        const float brushColorG = KoColorSpaceMaths<channels_type, float>::scaleToA(brushColorRGB->green);
        const float brushColorB = KoColorSpaceMaths<channels_type, float>::scaleToA(brushColorRGB->blue);
25
        const float brushColorA = KoColorSpaceMaths<channels_type, float>::scaleToA(brushColorRGB->alpha);
26

27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
        /**
         * Lightness mixing algorithm is developed by Peter Schatz <voronwe13@gmail.com>
         *
         * We use a formula f(x) where f(0) = 0, f(1) = 1, and f(.5) = z,
         * where z is the lightness of the brush color. This can’t be linear unless
         * the color chosen is also .5. So we use a quadratic equation:
         *
         * f(x) = ax^2 + b^x +c
         * 0,0 -> 0 = a0^2 + b0 + c -> c = 0
         * 1,1 -> 1 = a1^2 +b1 + c -> 1 = a + b + 0 -> a = 1 - b
         * .5,z -> z = a*.5^2 + b*.5 + c -> z =
         *           = a/4 + b/2 + 0 -> z =
         *           = 1/4 - b/4 + b/2 -> z = 1/4 + b/4 -> b = 4z - 1
         *
         * f(x) = (1 - (4z - 1)) * x^2 + (4z - 1) * x
         */
43

44
45
46
        const float brushColorL = getLightness<HSLType, float>(brushColorR, brushColorG, brushColorB);
        const float lightnessB = 4 * brushColorL - 1;
        const float lightnessA = 1 - lightnessB;
47

48
49
        for (; nPixels > 0; --nPixels, pixels += pixelSize, ++brush) {
            RGBPixel *pixelRGB = reinterpret_cast<RGBPixel*>(pixels);
50

51
52
            float brushMaskL = qRed(*brush) / 255.0f;
            brushMaskL = (brushMaskL - 0.5) * strength + 0.5;
53
            const float finalLightness = lightnessA * pow2(brushMaskL) + lightnessB * brushMaskL;
54
            const float finalAlpha = qMin(qAlpha(*brush) / 255.0f, brushColorA);
55

56
57
58
            float pixelR = brushColorR;
            float pixelG = brushColorG;
            float pixelB = brushColorB;
59

60
            setLightness<HSLType, float>(pixelR, pixelG, pixelB, finalLightness);
61

62
63
64
            pixelRGB->red = KoColorSpaceMaths<float, channels_type>::scaleToA(pixelR);
            pixelRGB->green = KoColorSpaceMaths<float, channels_type>::scaleToA(pixelG);
            pixelRGB->blue = KoColorSpaceMaths<float, channels_type>::scaleToA(pixelB);
65
            pixelRGB->alpha = KoColorSpaceMaths<quint8, channels_type>::scaleToA(quint8(finalAlpha * 255));
66
        }
67
68
}

Dmitry Kazakov's avatar
Dmitry Kazakov committed
69
template<typename CSTraits>
Dmitry Kazakov's avatar
Dmitry Kazakov committed
70
inline static void modulateLightnessByGrayBrushRGB(quint8 *pixels, const QRgb *brush, qreal strength, qint32 nPixels) {
Dmitry Kazakov's avatar
Dmitry Kazakov committed
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
    using RGBPixel = typename CSTraits::Pixel;
        using channels_type = typename CSTraits::channels_type;
        static const quint32 pixelSize = CSTraits::pixelSize;


        /**
         * Lightness mixing algorithm is developed by Peter Schatz <voronwe13@gmail.com>
         *
         * We use a formula f(x) where f(0) = 0, f(1) = 1, and f(.5) = z,
         * where z is the lightness of the brush color. This can’t be linear unless
         * the color chosen is also .5. So we use a quadratic equation:
         *
         * f(x) = ax^2 + b^x +c
         * 0,0 -> 0 = a0^2 + b0 + c -> c = 0
         * 1,1 -> 1 = a1^2 +b1 + c -> 1 = a + b + 0 -> a = 1 - b
         * .5,z -> z = a*.5^2 + b*.5 + c -> z =
         *           = a/4 + b/2 + 0 -> z =
         *           = 1/4 - b/4 + b/2 -> z = 1/4 + b/4 -> b = 4z - 1
         *
         * f(x) = (1 - (4z - 1)) * x^2 + (4z - 1) * x
         */

        for (; nPixels > 0; --nPixels, pixels += pixelSize, ++brush) {

            RGBPixel *pixelRGB = reinterpret_cast<RGBPixel*>(pixels);

            const float srcColorR = KoColorSpaceMaths<channels_type, float>::scaleToA(pixelRGB->red);
            const float srcColorG = KoColorSpaceMaths<channels_type, float>::scaleToA(pixelRGB->green);
            const float srcColorB = KoColorSpaceMaths<channels_type, float>::scaleToA(pixelRGB->blue);
Dmitry Kazakov's avatar
Dmitry Kazakov committed
100
            //const float srcColorA = KoColorSpaceMaths<channels_type, float>::scaleToA(pixelRGB->alpha);
Dmitry Kazakov's avatar
Dmitry Kazakov committed
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125

            const float srcColorL = getLightness<HSLType, float>(srcColorR, srcColorG, srcColorB);
            const float lightnessB = 4 * srcColorL - 1;
            const float lightnessA = 1 - lightnessB;



            float brushMaskL = qRed(*brush) / 255.0f;
            brushMaskL = (brushMaskL - 0.5) * strength * qAlpha(*brush) / 255.0 + 0.5;
            const float finalLightness = lightnessA * pow2(brushMaskL) + lightnessB * brushMaskL;
            //const float finalAlpha = qMin(qAlpha(*brush) / 255.0f, srcColorA);

            float pixelR = srcColorR;
            float pixelG = srcColorG;
            float pixelB = srcColorB;

            setLightness<HSLType, float>(pixelR, pixelG, pixelB, finalLightness);

            pixelRGB->red = KoColorSpaceMaths<float, channels_type>::scaleToA(pixelR);
            pixelRGB->green = KoColorSpaceMaths<float, channels_type>::scaleToA(pixelG);
            pixelRGB->blue = KoColorSpaceMaths<float, channels_type>::scaleToA(pixelB);
            //pixelRGB->alpha = KoColorSpaceMaths<quint8, channels_type>::scaleToA(quint8(finalAlpha * 255));
        }
}

126
127

#endif // KOCOLORSPACEPRESERVELIGHTNESSUTILS_H