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 }