1#version 130
2
3/*
4   Colorimetry shader
5   Ported from Drag's NES Palette Generator
6   http://drag.wootest.net/misc/palgen.html
7*/
8
9// Parameter lines go here:
10#pragma parameter color_mode "Colorimetry mode" 0.0 0.0 2.0 1.0
11#pragma parameter white_point_d93 "Use D93 white point" 1.0 0.0 1.0 1.0
12#pragma parameter clipping_method "Color clipping method" 0.0 0.0 2.0 1.0
13
14#if defined(VERTEX)
15
16#if __VERSION__ >= 130
17#define COMPAT_VARYING out
18#define COMPAT_ATTRIBUTE in
19#define COMPAT_TEXTURE texture
20#else
21#define COMPAT_VARYING varying
22#define COMPAT_ATTRIBUTE attribute
23#define COMPAT_TEXTURE texture2D
24#endif
25
26#ifdef GL_ES
27#define COMPAT_PRECISION mediump
28#else
29#define COMPAT_PRECISION
30#endif
31
32COMPAT_ATTRIBUTE vec4 VertexCoord;
33COMPAT_ATTRIBUTE vec4 COLOR;
34COMPAT_ATTRIBUTE vec4 TexCoord;
35COMPAT_VARYING vec4 COL0;
36COMPAT_VARYING vec4 TEX0;
37
38vec4 _oPosition1;
39uniform mat4 MVPMatrix;
40uniform COMPAT_PRECISION int FrameDirection;
41uniform COMPAT_PRECISION int FrameCount;
42uniform COMPAT_PRECISION vec2 OutputSize;
43uniform COMPAT_PRECISION vec2 TextureSize;
44uniform COMPAT_PRECISION vec2 InputSize;
45
46void main()
47{
48    gl_Position = MVPMatrix * VertexCoord;
49    COL0 = COLOR;
50    TEX0.xy = TexCoord.xy;
51}
52
53#elif defined(FRAGMENT)
54
55#ifdef GL_ES
56#ifdef GL_FRAGMENT_PRECISION_HIGH
57precision highp float;
58#else
59precision mediump float;
60#endif
61#define COMPAT_PRECISION mediump
62#else
63#define COMPAT_PRECISION
64#endif
65
66#if __VERSION__ >= 130
67#define COMPAT_VARYING in
68#define COMPAT_TEXTURE texture
69out COMPAT_PRECISION vec4 FragColor;
70#else
71#define COMPAT_VARYING varying
72#define FragColor gl_FragColor
73#define COMPAT_TEXTURE texture2D
74#endif
75
76uniform COMPAT_PRECISION int FrameDirection;
77uniform COMPAT_PRECISION int FrameCount;
78uniform COMPAT_PRECISION vec2 OutputSize;
79uniform COMPAT_PRECISION vec2 TextureSize;
80uniform COMPAT_PRECISION vec2 InputSize;
81uniform sampler2D Texture;
82COMPAT_VARYING vec4 TEX0;
83
84// compatibility #defines
85#define Source Texture
86#define vTexCoord TEX0.xy
87
88#define SourceSize vec4(TextureSize, 1.0 / TextureSize) //either TextureSize or InputSize
89
90#ifdef PARAMETER_UNIFORM
91uniform COMPAT_PRECISION float color_mode;
92uniform COMPAT_PRECISION float white_point_d93;
93uniform COMPAT_PRECISION float clipping_method;
94#else
95#define color_mode 0.0
96#define white_point_d93 0.0
97#define clipping_method 0.0
98#endif
99
100vec3 huePreserveClipDarken(float r, float g, float b)
101{
102   float ratio = 1.0;
103   if ((r > 1.0) || (g > 1.0) || (b > 1.0))
104   {
105      float max = r;
106      if (g > max)
107         max = g;
108      if (b > max)
109         max = b;
110      ratio = 1.0 / max;
111   }
112
113   r *= ratio;
114   g *= ratio;
115   b *= ratio;
116
117   r = clamp(r, 0.0, 1.0);
118   g = clamp(g, 0.0, 1.0);
119   b = clamp(b, 0.0, 1.0);
120
121   return vec3(r, g, b);
122}
123
124vec3 huePreserveClipDesaturate(float r, float g, float b)
125{
126   float l = (.299 * r) + (0.587 * g) + (0.114 * b);
127   bool ovr = false;
128   float ratio = 1.0;
129
130   if ((r > 1.0) || (g > 1.0) || (b > 1.0))
131   {
132      ovr = true;
133      float max = r;
134      if (g > max) max = g;
135      if (b > max) max = b;
136      ratio = 1.0 / max;
137   }
138
139   if (ovr)
140   {
141      r -= 1.0;
142      g -= 1.0;
143      b -= 1.0;
144      r *= ratio;
145      g *= ratio;
146      b *= ratio;
147      r += 1.0;
148      g += 1.0;
149      b += 1.0;
150   }
151
152   r = clamp(r, 0.0, 1.0);
153   g = clamp(g, 0.0, 1.0);
154   b = clamp(b, 0.0, 1.0);
155
156   return vec3(r, g, b);
157}
158
159vec3 ApplyColorimetry(vec3 color)
160{
161   //http://www.brucelindbloom.com/Eqn_RGB_XYZ_Matrix.html
162   //http://www.dr-lex.be/random/matrix_inv.html
163   // Convert the (x,y) values to X Y Z.
164
165   float R = color.r;
166   float G = color.g;
167   float B = color.b;
168
169   float Rx = 0.0;
170   float Ry = 0.0;
171   float Gx = 0.0;
172   float Gy = 0.0;
173   float Bx = 0.0;
174   float By = 0.0;
175   float Wx = 0.0;
176   float Wy = 0.0;
177
178   if (white_point_d93 > 0.5)
179   {
180      Wx = 0.31;
181      Wy = 0.316;
182   }
183   else
184   {
185      Wx = 0.3127;
186      Wy = 0.329;
187   }
188
189   if (color_mode < 0.5)
190   {
191      // FCC 1953
192      // Original FCC standard for the color of the phosphors
193      Rx = 0.67;
194      Ry = 0.33;
195      Gx = 0.21;
196      Gy = 0.71;
197      Bx = 0.14;
198      By = 0.08;
199   }
200   else if (color_mode == 1.0)
201   {
202      // SMPTE C (1987)
203      // A newer standard for the color of the phospors. (Not used in Japan)
204      Rx = 0.63;
205      Ry = 0.34;
206      Gx = 0.31;
207      Gy = 0.595;
208      Bx = 0.155;
209      By = 0.07;
210   }
211   else
212   {
213      //sRGB (PC Monitors)
214      //The colorimetry used in PC monitors, like the one you're (probably) looking at right now.
215      Rx = 0.64;
216      Ry = 0.33;
217      Gx = 0.3;
218      Gy = 0.6;
219      Bx = 0.15;
220      By = 0.06;
221   }
222
223   float Xr = Rx / Ry;
224   float Xg = Gx / Gy;
225   float Xb = Bx / By;
226   float Xw = Wx / Wy;
227   float Yr = 1.0;
228   float Yg = 1.0;
229   float Yb = 1.0;
230   float Yw = 1.0;
231   float Zr = (1.0 - Rx - Ry) / Ry;
232   float Zg = (1.0 - Gx - Gy) / Gy;
233   float Zb = (1.0 - Bx - By) / By;
234   float Zw = (1.0 - Wx - Wy) / Wy;
235
236   // Get ready for a bunch of painful math. I need to invert a matrix, then multiply it by a vector.
237   // Determinant for inverse matrix
238
239   float sDet = (Xr*((Zb*Yg)-(Zg*Yb)))-(Yr*((Zb*Xg)-(Zg*Xb)))+(Zr*((Yb*Xg)-(Yg*Xb)));
240
241   float Sr = ((((Zb*Yg)-(Zg*Yb))/sDet)*Xw) + ((-((Zb*Xg)-(Zg*Xb))/sDet)*Yw) + ((((Yb*Xg)-(Yg*Xb))/sDet)*Zw);
242   float Sg = ((-((Zb*Yr)-(Zr*Yb))/sDet)*Xw) + ((((Zb*Xr)-(Zr*Xb))/sDet)*Yw) + ((-((Yb*Xr)-(Yr*Xb))/sDet)*Zw);
243   float Sb = ((((Zg*Yr)-(Zr*Yg))/sDet)*Xw) + ((-((Zg*Xr)-(Zr*Xg))/sDet)*Yw) + ((((Yg*Xr)-(Yr*Xg))/sDet)*Zw);
244
245   // This should be the completed RGB -> XYZ matrix.
246   // Multiply each of the first three members by R, then add them together to get X
247   float convMatrix[9] = float[] (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
248
249   convMatrix[0] = Sr*Xr;
250   convMatrix[1] = Sg*Xg;
251   convMatrix[2] = Sb*Xb;
252   convMatrix[3] = Sr*Yr;
253   convMatrix[4] = Sg*Yg;
254   convMatrix[5] = Sb*Yb;
255   convMatrix[6] = Sr*Zr;
256   convMatrix[7] = Sg*Zg;
257   convMatrix[8] = Sb*Zb;
258
259   // Convert RGB to XYZ using the matrix generated with the specified RGB and W points.
260   float X = (convMatrix[0] * R) + (convMatrix[1] * G) + (convMatrix[2] * B);
261   float Y = (convMatrix[3] * R) + (convMatrix[4] * G) + (convMatrix[5] * B);
262   float Z = (convMatrix[6] * R) + (convMatrix[7] * G) + (convMatrix[8] * B);
263
264   // This is the conversion matrix for CIEXYZ -> sRGB. I nicked this from:
265   // http://www.brucelindbloom.com/Eqn_RGB_XYZ_Matrix.html
266   // and I know it's right because when you use the sRGB colorimetry, this matrix produces identical results to
267   // just using the raw R, G, and B above.
268   float xyztorgb[9] = float[] (3.2404, -1.5371, -0.4985, -0.9693, 1.876, 0.0416, 0.0556, -0.204, 1.0572);
269
270   // Convert back to RGB using the XYZ->sRGB matrix.
271   R = (xyztorgb[0]*X) + (xyztorgb[1]*Y) + (xyztorgb[2]*Z);
272   G = (xyztorgb[3]*X) + (xyztorgb[4]*Y) + (xyztorgb[5]*Z);
273   B = (xyztorgb[6]*X) + (xyztorgb[7]*Y) + (xyztorgb[8]*Z);
274
275   vec3 corrected_rgb;
276
277   // Apply desired clipping method to out-of-gamut colors.
278   if (clipping_method < 0.5)
279   {
280      //If a channel is out of range (> 1.0), it's simply clamped to 1.0. This may change hue, saturation, and/or lightness.
281      R = clamp(R, 0.0, 1.0);
282      G = clamp(G, 0.0, 1.0);
283      B = clamp(B, 0.0, 1.0);
284      corrected_rgb = vec3(R, G, B);
285   }
286   else if (clipping_method == 1.0)
287   {
288      //If any channels are out of range, the color is darkened until it is completely in range.
289      corrected_rgb = huePreserveClipDarken(R, G, B);
290   }
291   else if (clipping_method == 2.0)
292   {
293      //If any channels are out of range, the color is desaturated towards the luminance it would've had.
294      corrected_rgb = huePreserveClipDesaturate(R, G, B);
295   }
296
297   return corrected_rgb;
298}
299
300void main()
301{
302   vec4 c = COMPAT_TEXTURE(Source, vTexCoord.xy);
303   vec3 rgb = vec3(c.r, c.g, c.b);
304
305   vec3 out_color = ApplyColorimetry(rgb);
306   FragColor = vec4(out_color, 1.0);
307}
308#endif
309