1 /**
2 Color-correction post-effect.
3 This is a widget intended to correct colors before display, at the Raw level.
4 
5 Copyright: Guillaume Piolat 2018.
6 License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7 */
8 module dplug.pbrwidgets.colorcorrection;
9 
10 
11 import dplug.math.matrix;
12 
13 import dplug.core.math;
14 import dplug.gui.element;
15 
16 /// FlatBackgroundGUI provides a background that is loaded from a PNG or JPEG
17 /// image. The string for backgroundPath should be in "stringImportPaths"
18 /// specified in dub.json
19 class UIColorCorrection : UIElement
20 {
21 public:
22 nothrow:
23 @nogc:
24 
25     this(UIContext context)
26     {
27         super(context, flagRaw);
28 
29         _tableArea.reallocBuffer(256 * 3, 32);
30         _redTransferTable = _tableArea.ptr;
31         _greenTransferTable = _tableArea.ptr + 256;
32         _blueTransferTable = _tableArea.ptr + 512;
33 
34         // Set identity tables
35         foreach(i; 0..256)
36         {
37             _redTransferTable[i] = cast(ubyte)i;
38             _greenTransferTable[i] = cast(ubyte)i;
39             _blueTransferTable[i] = cast(ubyte)i;
40         }
41     }
42 
43     ~this()
44     {
45         _tableArea.reallocBuffer(0, 32);
46     }
47 
48     /// Calling this setup color correction table, with the well
49     /// known lift-gamma-gain formula.
50     void setLiftGammaGainContrast(float lift = 0.0f, float gamma = 1.0f, float gain = 1.0f, float contrast = 0.0f)
51     {
52         setLiftGammaGainContrastRGB(lift, gamma, gain, contrast,
53                                     lift, gamma, gain, contrast,
54                                     lift, gamma, gain, contrast);
55     }
56 
57     /// Calling this setup color correction table, with the well
58     /// known lift-gamma-gain formula, per channel.
59     void setLiftGammaGainRGB(float rLift = 0.0f, float rGamma = 1.0f, float rGain = 1.0f,
60                              float gLift = 0.0f, float gGamma = 1.0f, float gGain = 1.0f,
61                              float bLift = 0.0f, float bGamma = 1.0f, float bGain = 1.0f)
62     {
63         setLiftGammaGainContrastRGB(rLift, rGamma, rGain, 0.0f,
64                                     gLift, gGamma, gGain, 0.0f,
65                                     bLift, bGamma, bGain, 0.0f);
66     }
67 
68     /// Calling this setup color correction table, with the less
69     /// known lift-gamma-gain formula + contrast addition, per channel.
70     void setLiftGammaGainContrastRGB(
71                                      float rLift = 0.0f, float rGamma = 1.0f, float rGain = 1.0f, float rContrast = 0.0f,
72                                      float gLift = 0.0f, float gGamma = 1.0f, float gGain = 1.0f, float gContrast = 0.0f,
73                                      float bLift = 0.0f, float bGamma = 1.0f, float bGain = 1.0f, float bContrast = 0.0f)
74     {
75         static float safePow(float a, float b) nothrow @nogc
76         {
77             if (a < 0)
78                 a = 0;
79             if (a > 1)
80                 a = 1;
81             return a ^^ b;
82         }
83 
84         for (int b = 0; b < 256; ++b)
85         {
86             float inp = b / 255.0f;
87             float outR = rGain*(inp + rLift*(1-inp));
88             float outG = gGain*(inp + gLift*(1-inp));
89             float outB = bGain*(inp + bLift*(1-inp));
90 
91             outR = safePow(outR, 1.0f / rGamma );
92             outG = safePow(outG, 1.0f / gGamma );
93             outB = safePow(outB, 1.0f / bGamma );
94 
95             if (outR < 0)
96                 outR = 0;
97             if (outG < 0)
98                 outG = 0;
99             if (outB < 0)
100                 outB = 0;
101             if (outR > 1)
102                 outR = 1;
103             if (outG > 1)
104                 outG = 1;
105             if (outB > 1)
106                 outB = 1;
107 
108             outR = lerp!float(outR, smoothStep!float(0, 1, outR), rContrast);
109             outG = lerp!float(outG, smoothStep!float(0, 1, outG), gContrast);
110             outB = lerp!float(outB, smoothStep!float(0, 1, outB), bContrast);
111 
112             _redTransferTable[b] = cast(ubyte)(0.5f + outR * 255.0f);
113             _greenTransferTable[b] = cast(ubyte)(0.5f + outG * 255.0f);
114             _blueTransferTable[b] = cast(ubyte)(0.5f + outB * 255.0f);
115         }
116         setDirtyWhole();
117     }
118 
119     /// ditto
120     void setLiftGammaGainContrastRGB(mat3x4!float liftGammaGainContrast)
121     {
122         auto m = liftGammaGainContrast;
123         setLiftGammaGainContrastRGB(m.c[0][0], m.c[0][1], m.c[0][2], m.c[0][3],
124                                     m.c[1][0], m.c[1][1], m.c[1][2], m.c[1][3],
125                                     m.c[2][0], m.c[2][1], m.c[2][2], m.c[2][3]);
126     }
127 
128     override void onDrawRaw(ImageRef!RGBA rawMap, box2i[] dirtyRects)
129     {
130         foreach(dirtyRect; dirtyRects)
131         {
132             rawMap.cropImageRef(dirtyRect).applyColorCorrection(_redTransferTable);
133         }
134     }
135 
136     override bool contains(int x, int y)
137     {
138         // HACK: this is just a way to avoid taking mouseOver.
139         // As this widget is often a top widget,
140         // this avoids capturing mouse instead
141         // of all below widgets.
142         return false;
143     }
144 
145 
146 private:
147     ubyte[] _tableArea = null;
148     ubyte* _redTransferTable = null;
149     ubyte* _greenTransferTable = null;
150     ubyte* _blueTransferTable = null;
151 }
152 
153 // Apply color correction and convert RGBA8 to BGRA8
154 void applyColorCorrection(ImageRef!RGBA image, const(ubyte*) rgbTable) pure nothrow @nogc
155 {
156     int w = image.w;
157     int h = image.h;
158     for (int j = 0; j < h; ++j)
159     {
160         ubyte* scan = cast(ubyte*)image.scanline(j).ptr;
161         for (int i = 0; i < w; ++i)
162         {
163             ubyte r = scan[4*i];
164             ubyte g = scan[4*i+1];
165             ubyte b = scan[4*i+2];
166             scan[4*i]   = rgbTable[r];
167             scan[4*i+1] = rgbTable[g+256];
168             scan[4*i+2] = rgbTable[b+512];
169         }
170     }
171 }