-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
213 lines (168 loc) · 5.74 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
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
100
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
<!DOCTYPE html>
<head>
<title>Chromaticity Diagram</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="colorspace.js" type="text/javascript"></script>
<script src="rgb_spaces.js" type="text/javascript"></script>
<script src="form.js" type="text/javascript"></script>
<script src="main.js" type="text/javascript"></script>
<script id="vertex" type="x-shader/x-vertex">#version 300 es
precision highp float;
in vec4 vertex;
void main()
{
gl_Position = vertex;
}
</script>
<script id="fragment" type="x-shader/x-fragment">#version 300 es
precision highp float;
/*
* Structures
*/
// Parameters for transfer characteristics (gamma curves)
struct transfer {
// Exponent used to linearize the signal
float power;
// Offset from 0.0 for the exponential curve
float off;
// Slope of linear segment near 0
float slope;
// Values below this are divided by slope during linearization
float cutoffToLinear;
// Values below this are multiplied by slope during gamma correction
float cutoffToGamma;
// Gamma-corrected values should be in the range 16-235
bool tvRange;
};
// Parameters for a colorspace
struct rgb_space {
// Chromaticity coordinates (xyz) for Red, Green, and Blue primaries
mat3 primaries;
// Chromaticity coordinates (xyz) for white point
vec3 white;
// Linearization and gamma correction parameters
transfer trc;
};
out vec4 fragColor;
uniform vec2 bounds;
uniform vec2 dispRes;
uniform sampler2D lines;
uniform rgb_space from;
uniform rgb_space to;
#define diag(v)\
mat3(\
v.x, 0.0, 0.0,\
0.0, v.y, 0.0,\
0.0, 0.0, v.z)
// Calculate XYZ values for a color at full brightness
#define Bright(white)\
(white)/(white.y)
// Creates a conversion matrix that turns RGB colors into XYZ colors
#define rgbToXyz(space)\
(space).primaries*diag((inverse((space).primaries)*Bright(space.white)))
// Creates a conversion matrix that turns XYZ colors into RGB colors
#define xyzToRgb(space)\
inverse(rgbToXyz(space))
// Creates a conversion matrix converts linear RGB colors from one colorspace to another
#define conversionMatrix(f, t)\
xyzToRgb(t)*rgbToXyz(f)
/*
* Conversion Functions
*/
// Converts RGB colors to a linear light scale
void toLinear(inout vec4 color, const transfer trc)
{
if (trc.tvRange) {
color = color*85.0/73.0 - 16.0/219.0;
}
bvec4 cutoff = lessThan(color, vec4(trc.cutoffToLinear));
bvec4 negCutoff = lessThanEqual(color, vec4(-1.0*trc.cutoffToLinear));
vec4 higher = pow((color + trc.off)/(1.0 + trc.off), vec4(trc.power));
vec4 lower = color/trc.slope;
vec4 neg = -1.0*pow((color - trc.off)/(-1.0 - trc.off), vec4(trc.power));
color = mix(higher, lower, cutoff);
color = mix(color, neg, negCutoff);
}
// Gamma-corrects RGB colors to be sent to a display
void toGamma(inout vec4 color, const transfer trc)
{
bvec4 cutoff = lessThan(color, vec4(trc.cutoffToGamma));
bvec4 negCutoff = lessThanEqual(color, vec4(-1.0*trc.cutoffToGamma));
vec4 higher = (1.0 + trc.off)*pow(color, vec4(1.0/trc.power)) - trc.off;
vec4 lower = color*trc.slope;
vec4 neg = (-1.0 - trc.off)*pow(-1.0*color, vec4(1.0/trc.power)) + trc.off;
color = mix(higher, lower, cutoff);
color = mix(color, neg, negCutoff);
if (trc.tvRange) {
color = color*73.0/85.0 + 16.0/255.0;
}
}
// Scales a color to the closest in-gamut representation of that color
void gamutScale(inout vec4 color, const float luma)
{
float low = min(color.r, min(color.g, min(color.b, 0.0)));
float high = max(color.r, max(color.g, max(color.b, 1.0)));
float lowScale = low/(low - luma);
float highScale = (high - 1.0)/(high - luma);
float scale = max(lowScale, highScale);
color.rgb += scale*(luma - color.rgb);
}
void convert(inout vec4 color)
{
float luma = 0.9999;
mat3 toRGB = xyzToRgb(from);
mat3 toXYZ = rgbToXyz(from);
mat3 convert = conversionMatrix(from, to);
// Convert from xyY to XYZ, then RGB
color.xyz *= luma/color.y;
color.rgb = toRGB*color.xyz;
// Max normalization
color.rgb /= max(color.r, max(color.g, color.b));
// Standard Euclidian normalization (equivalent to 'norm = 2.0' below)
//color.rgb = normalize(color.rgb);
// Custom normalization for brighter white point
// Wikipedia's chart roughly uses 'norm = 5.0'
//const float norm = 4.0;
//color.rgb /= pow(dot(pow(abs(color.rgb), vec3(norm)), vec3(1.0)), 1.0/norm);
// Wikipedia's description for one of their chromaticity diagrams claims
// that the top left should have more cyan than most charts do. This
// is solved by scaling out-of-gamut colors to be in-gamut
gamutScale(color, luma);
// Rescale colors to avoid overly bright hues (which are desaturated by
// gamut scaling)
mat3 newPrims = convert*mat3(1);
vec3 newWhite = convert*vec3(1);
color.rgb /=
max(newPrims[0].r, max(newPrims[0].g, max(newPrims[0].b,
max(newPrims[1].r, max(newPrims[1].g, max(newPrims[1].b,
max(newPrims[2].r, max(newPrims[2].g, max(newPrims[2].b,
max(newWhite.r, max(newWhite.g, newWhite.b)))))))))));
// Grab new luma values to emulate display brightness more accurately
luma = (toXYZ*color.rgb).y;
// Convert from one colorspace to another, to show how one display
// might show the colorspace of another
color.rgb = convert*color.rgb;
gamutScale(color, luma);
}
void main()
{
fragColor.xy = gl_FragCoord.xy*bounds.y/dispRes.y;
fragColor.x -= (bounds.y*dispRes.x/dispRes.y - bounds.x)/2.0;
fragColor.z = 1.0 - fragColor.x - fragColor.y;
fragColor.a = 1.0;
convert(fragColor);
vec4 texColor = texture(lines, gl_FragCoord.xy/dispRes);
fragColor.rgb = texColor.a*texColor.rgb + (1.0 - texColor.a)*fragColor.rgb;
toGamma(fragColor, to.trc);
}
</script>
<link rel="stylesheet" href="main.css" type="text/css">
<body>
<main>
<canvas id="glCanvas" width="400" height="450"></canvas>
</main>
<footer>
<p>© Colin Griffith
</footer>
</body>