|
1 |
| -#version 130 |
| 1 | +#version 140 |
| 2 | + |
| 3 | +precision highp float; |
2 | 4 |
|
3 | 5 | uniform int debug;
|
| 6 | +uniform int cs_in; |
| 7 | +uniform int cs_out; |
4 | 8 | uniform bool grayscale;
|
5 | 9 | uniform int gamma;
|
6 | 10 | uniform float ev;
|
@@ -39,6 +43,18 @@ float max3(vec3 v) {
|
39 | 43 | }
|
40 | 44 |
|
41 | 45 |
|
| 46 | +mat3 mround(mat3 mtx) { |
| 47 | + /** |
| 48 | + * Rounds the elements of the given matrix to the nearest integer. Annoyingly, |
| 49 | + * the built-in round() function does not work with matrices. |
| 50 | + */ |
| 51 | + mtx[0] = round(mtx[0]); |
| 52 | + mtx[1] = round(mtx[1]); |
| 53 | + mtx[2] = round(mtx[2]); |
| 54 | + return mtx; |
| 55 | +} |
| 56 | + |
| 57 | + |
42 | 58 | /**************************************************************************************/
|
43 | 59 | /*
|
44 | 60 | /* G A M M A
|
@@ -102,6 +118,245 @@ vec3 apply_gamma(vec3 rgb) {
|
102 | 118 | }
|
103 | 119 |
|
104 | 120 |
|
| 121 | +/**************************************************************************************/ |
| 122 | +/* |
| 123 | +/* C O L O R S P A C E C O N V E R S I O N S |
| 124 | +/* |
| 125 | +/**************************************************************************************/ |
| 126 | + |
| 127 | + |
| 128 | +vec3 xy_to_xyz(vec2 xy) { |
| 129 | + /** |
| 130 | + * Transforms the given color coordinates from CIE xy to CIE XYZ. |
| 131 | + */ |
| 132 | + vec3 xyz; |
| 133 | + xyz.x = xy.x / xy.y; |
| 134 | + xyz.y = 1.0; |
| 135 | + xyz.z = (1.0 - xy.x - xy.y) / xy.y; |
| 136 | + return xyz; |
| 137 | +} |
| 138 | + |
| 139 | + |
| 140 | +mat3 rgb_to_xyz_mtx(vec2 xy_r, vec2 xy_g, vec2 xy_b, vec2 xy_w) { |
| 141 | + /** |
| 142 | + * Returns a 3 x 3 conversion matrix from RGB to XYZ, given the (x, y) chromaticity |
| 143 | + * coordinates of the RGB primaries and the reference white. Conversion formula taken |
| 144 | + * from http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html. |
| 145 | + */ |
| 146 | + vec3 XYZ_r = xy_to_xyz(xy_r); |
| 147 | + vec3 XYZ_g = xy_to_xyz(xy_g); |
| 148 | + vec3 XYZ_b = xy_to_xyz(xy_b); |
| 149 | + vec3 XYZ_w = xy_to_xyz(xy_w); |
| 150 | + mat3 M = mat3(XYZ_r, XYZ_g, XYZ_b); |
| 151 | + |
| 152 | + // Scale each column of the RGB-to-XYZ matrix with a scalar such |
| 153 | + // that [1, 1, 1] gets transformed to the given whitepoint (XYZ_w); |
| 154 | + // for example, M * [1, 1, 1] = [0.9504, 1.0, 1.0888] in case of D65. |
| 155 | + |
| 156 | + vec3 S = inverse(M) * XYZ_w; // whitepoint in RGB |
| 157 | + M[0] *= S[0]; // R column vector scale |
| 158 | + M[1] *= S[1]; // G column vector scale |
| 159 | + M[2] *= S[2]; // B column vector scale |
| 160 | + return M; |
| 161 | +} |
| 162 | + |
| 163 | + |
| 164 | +mat3 xyz_to_rgb_mtx(vec2 xy_r, vec2 xy_g, vec2 xy_b, vec2 xy_w) { |
| 165 | + /** |
| 166 | + * Returns a 3 x 3 conversion matrix from XYZ to RGB, given the (x, y) chromaticity |
| 167 | + * coordinates of the RGB primaries and the reference white. Conversion formula taken |
| 168 | + * from http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html. |
| 169 | + */ |
| 170 | + mat3 M = rgb_to_xyz_mtx(xy_r, xy_g, xy_b, xy_w); |
| 171 | + M = inverse(M); |
| 172 | + return M; |
| 173 | +} |
| 174 | + |
| 175 | + |
| 176 | +mat3 srgb_to_xyz_mtx() { |
| 177 | + /** |
| 178 | + * Returns the exact sRGB to XYZ conversion matrix defined by the sRGB specification. |
| 179 | + * Note that the matrix is computed from limited-precision primaries and whitepoint, |
| 180 | + * and rounded to four decimals at the end, as per the specification. |
| 181 | + */ |
| 182 | + vec2 D65_WP = vec2(0.3127, 0.3290); |
| 183 | + vec2 xy_r = vec2(0.640, 0.330); |
| 184 | + vec2 xy_g = vec2(0.300, 0.600); |
| 185 | + vec2 xy_b = vec2(0.150, 0.060); |
| 186 | + mat3 M = rgb_to_xyz_mtx(xy_r, xy_g, xy_b, D65_WP); |
| 187 | + M = mround(M * 10000.0) / 10000.0; |
| 188 | + return M; |
| 189 | +} |
| 190 | + |
| 191 | + |
| 192 | +mat3 xyz_to_srgb_mtx() { |
| 193 | + /** |
| 194 | + * Returns the exact XYZ to sRGB conversion matrix defined by the sRGB specification. |
| 195 | + * Note that the matrix is computed from limited-precision primaries and whitepoint, |
| 196 | + * and rounded to four decimals at the end, as per the specification. |
| 197 | + */ |
| 198 | + mat3 M = inverse(srgb_to_xyz_mtx()); |
| 199 | + M = mround(M * 10000.0) / 10000.0; |
| 200 | + return M; |
| 201 | +} |
| 202 | + |
| 203 | + |
| 204 | +mat3 p3_to_xyz_mtx() { |
| 205 | + /** |
| 206 | + * Returns the exact DCI-P3 to XYZ conversion matrix defined by the P3 specification. |
| 207 | + * Note that the matrix is computed from limited-precision primaries and whitepoint, |
| 208 | + * as per the specification. |
| 209 | + */ |
| 210 | + vec2 D65_WP = vec2(0.3127, 0.3290); |
| 211 | + vec2 xy_r = vec2(0.680, 0.320); |
| 212 | + vec2 xy_g = vec2(0.265, 0.690); |
| 213 | + vec2 xy_b = vec2(0.150, 0.060); |
| 214 | + mat3 M = rgb_to_xyz_mtx(xy_r, xy_g, xy_b, D65_WP); |
| 215 | + return M; |
| 216 | +} |
| 217 | + |
| 218 | + |
| 219 | +mat3 xyz_to_p3_mtx() { |
| 220 | + /** |
| 221 | + * Returns the exact XYZ to DCI-P3 conversion matrix defined by the P3 specification. |
| 222 | + * Note that the matrix is computed from limited-precision primaries and whitepoint, |
| 223 | + * as per the specification. |
| 224 | + */ |
| 225 | + mat3 M = inverse(p3_to_xyz_mtx()); |
| 226 | + return M; |
| 227 | +} |
| 228 | + |
| 229 | + |
| 230 | +mat3 rec2020_to_xyz_mtx() { |
| 231 | + /** |
| 232 | + * Returns the exact Rec2020 to XYZ conversion matrix defined by the Rec2020 |
| 233 | + * specification. Note that the matrix is computed from limited-precision |
| 234 | + * primaries and whitepoint, as per the specification. |
| 235 | + */ |
| 236 | + vec2 D65_WP = vec2(0.3127, 0.3290); |
| 237 | + vec2 xy_r = vec2(0.708, 0.292); |
| 238 | + vec2 xy_g = vec2(0.170, 0.797); |
| 239 | + vec2 xy_b = vec2(0.131, 0.046); |
| 240 | + mat3 M = rgb_to_xyz_mtx(xy_r, xy_g, xy_b, D65_WP); |
| 241 | + return M; |
| 242 | +} |
| 243 | + |
| 244 | + |
| 245 | +mat3 xyz_to_rec2020_mtx() { |
| 246 | + /** |
| 247 | + * Returns the exact XYZ to Rec2020 conversion matrix defined by the Rec2020 |
| 248 | + * specification. Note that the matrix is computed from limited-precision |
| 249 | + * primaries and whitepoint, as per the specification. |
| 250 | + */ |
| 251 | + mat3 M = inverse(rec2020_to_xyz_mtx()); |
| 252 | + return M; |
| 253 | +} |
| 254 | + |
| 255 | + |
| 256 | +vec3 srgb_to_xyz(vec3 rgb) { |
| 257 | + /** |
| 258 | + * Transforms the given sRGB color to CIE XYZ. |
| 259 | + */ |
| 260 | + vec3 xyz = srgb_to_xyz_mtx() * rgb; |
| 261 | + return xyz; |
| 262 | +} |
| 263 | + |
| 264 | + |
| 265 | +vec3 xyz_to_srgb(vec3 xyz) { |
| 266 | + /** |
| 267 | + * Transforms the given CIE XYZ color to sRGB. |
| 268 | + */ |
| 269 | + vec3 rgb = xyz_to_srgb_mtx() * xyz; |
| 270 | + return rgb; |
| 271 | +} |
| 272 | + |
| 273 | + |
| 274 | +vec3 p3_to_xyz(vec3 rgb) { |
| 275 | + /** |
| 276 | + * Transforms the given DCI-P3 color to CIE XYZ. |
| 277 | + */ |
| 278 | + vec3 xyz = p3_to_xyz_mtx() * rgb; |
| 279 | + return xyz; |
| 280 | +} |
| 281 | + |
| 282 | + |
| 283 | +vec3 xyz_to_p3(vec3 xyz) { |
| 284 | + /** |
| 285 | + * Transforms the given CIE XYZ color to DCI-P3. |
| 286 | + */ |
| 287 | + vec3 rgb = xyz_to_p3_mtx() * xyz; |
| 288 | + return rgb; |
| 289 | +} |
| 290 | + |
| 291 | + |
| 292 | +vec3 rec2020_to_xyz(vec3 rgb) { |
| 293 | + /** |
| 294 | + * Transforms the given Rec2020 color to CIE XYZ. |
| 295 | + */ |
| 296 | + vec3 xyz = rec2020_to_xyz_mtx() * rgb; |
| 297 | + return xyz; |
| 298 | +} |
| 299 | + |
| 300 | + |
| 301 | +vec3 xyz_to_rec2020(vec3 xyz) { |
| 302 | + /** |
| 303 | + * Transforms the given CIE XYZ color to Rec2020. |
| 304 | + */ |
| 305 | + vec3 rgb = xyz_to_rec2020_mtx() * xyz; |
| 306 | + return rgb; |
| 307 | +} |
| 308 | + |
| 309 | + |
| 310 | +vec3 csconv(vec3 rgb) { |
| 311 | + /** |
| 312 | + * Transforms the given color from a given input color space to a given output |
| 313 | + * color space. If the input and output color spaces are the same, the color is |
| 314 | + * unchanged. The following color spaces are available as both input & output: |
| 315 | + * |
| 316 | + * 0 - sRGB |
| 317 | + * 1 - DCI-P3 |
| 318 | + * 2 - Rec2020 |
| 319 | + * |
| 320 | + * As a concrete example, to display a P3-JPEG captured by a modern smartphone |
| 321 | + * on a high-quality wide-gamut monitor, you would set the input color space to |
| 322 | + * DCI-P3 and the output to Rec2020, while making sure that the monitor is set |
| 323 | + * to Rec2020 mode. |
| 324 | + */ |
| 325 | + if (cs_in != cs_out) { |
| 326 | + vec3 xyz; |
| 327 | + switch (cs_in) { |
| 328 | + case 0: |
| 329 | + xyz = srgb_to_xyz(rgb); |
| 330 | + break; |
| 331 | + case 1: |
| 332 | + xyz = p3_to_xyz(rgb); |
| 333 | + break; |
| 334 | + case 2: |
| 335 | + xyz = rec2020_to_xyz(rgb); |
| 336 | + break; |
| 337 | + default: |
| 338 | + xyz = rgb; |
| 339 | + break; |
| 340 | + } |
| 341 | + switch (cs_out) { |
| 342 | + case 0: |
| 343 | + rgb = xyz_to_srgb(xyz); |
| 344 | + break; |
| 345 | + case 1: |
| 346 | + rgb = xyz_to_p3(xyz); |
| 347 | + break; |
| 348 | + case 2: |
| 349 | + rgb = xyz_to_rec2020(xyz); |
| 350 | + break; |
| 351 | + default: |
| 352 | + rgb = xyz; |
| 353 | + break; |
| 354 | + } |
| 355 | + } |
| 356 | + return rgb; |
| 357 | +} |
| 358 | + |
| 359 | + |
105 | 360 | /**************************************************************************************/
|
106 | 361 | /*
|
107 | 362 | /* G A M U T C O M P R E S S I O N
|
@@ -230,6 +485,7 @@ vec3 debug_indicators(vec3 rgb) {
|
230 | 485 | void main() {
|
231 | 486 | color = texture2D(texture, rotate(texcoords, orientation));
|
232 | 487 | color.rgb = color.rgb / maxval;
|
| 488 | + color.rgb = csconv(color.rgb); |
233 | 489 | color.rgb = grayscale ? color.rrr : color.rgb;
|
234 | 490 | color.rgb = gamut.compress ? compress_gamut(color.rgb) : color.rgb;
|
235 | 491 | color.rgb = color.rgb * exp(ev); // exp(x) == 2^x
|
|
0 commit comments