@@ -20,95 +20,106 @@ const CENTER_X = 0.5;
2020 */
2121const CENTER_Y = 0.5 ;
2222
23- // color conversions grabbed from https://gist.github.com/mjackson/5311256
23+ /**
24+ * Reused memory location for storing an HSV color value.
25+ * @type {Array<number> }
26+ */
27+ const __hsv = [ 0 , 0 , 0 ] ;
2428
2529/**
26- * Converts an RGB color value to HSL . Conversion formula
27- * adapted from http://en.wikipedia.org/wiki/HSL_color_space .
28- * Assumes r, g, and b are contained in the set [0, 255] and
29- * returns h, s, and l in the set [0, 1].
30+ * Converts an RGB color value to HSV . Conversion formula
31+ * adapted from http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv .
32+ * Assumes r, g, and b are in the range [0, 255] and
33+ * returns h, s, and v in the range [0, 1].
3034 *
31- * @param {number } r The red color value
32- * @param {number } g The green color value
33- * @param {number } b The blue color value
34- * @return {Array } The HSL representation
35+ * @param {Array<number> } rgb The RGB color value
36+ * @param {number } rgb.r The red color value
37+ * @param {number } rgb.g The green color value
38+ * @param {number } rgb.b The blue color value
39+ * @param {Array<number> } dst The array to store the RGB values in
40+ * @return {Array<number> } The `dst` array passed in
3541 */
36- const rgbToHsl = ( [ r , g , b ] ) => {
42+ const rgbToHsv = ( [ r , g , b ] , dst ) => {
43+ let K = 0.0 ;
44+
3745 r /= 255 ;
3846 g /= 255 ;
3947 b /= 255 ;
48+ let tmp = 0 ;
4049
41- const max = Math . max ( r , g , b ) ;
42- const min = Math . min ( r , g , b ) ;
43- let h ;
44- let s ;
45- const l = ( max + min ) / 2 ;
46-
47- if ( max === min ) {
48- h = s = 0 ; // achromatic
49- } else {
50- const d = max - min ;
51- s = l > 0.5 ? d / ( 2 - max - min ) : d / ( max + min ) ;
52-
53- switch ( max ) {
54- case r : h = ( ( g - b ) / d ) + ( g < b ? 6 : 0 ) ; break ;
55- case g : h = ( ( b - r ) / d ) + 2 ; break ;
56- case b : h = ( ( r - g ) / d ) + 4 ; break ;
57- }
50+ if ( g < b ) {
51+ tmp = g ;
52+ g = b ;
53+ b = tmp ;
5854
59- h /= 6 ;
55+ K = - 1 ;
6056 }
6157
62- return [ h , s , l ] ;
63- } ;
58+ if ( r < g ) {
59+ tmp = r ;
60+ r = g ;
61+ g = tmp ;
6462
65- /**
66- * Helper function for hslToRgb is called with varying 't' values to get
67- * red green and blue values from the p/q/t color space calculations
68- * @param {number } p vector coordinates
69- * @param {number } q vector coordinates
70- * @param {number } t vector coordinates
71- * @return {number } amount of r/g/b byte
72- */
73- const hue2rgb = ( p , q , t ) => {
74- if ( t < 0 ) t += 1 ;
75- if ( t > 1 ) t -= 1 ;
76- if ( t < 1 / 6 ) return p + ( ( q - p ) * 6 * t ) ;
77- if ( t < 1 / 2 ) return q ;
78- if ( t < 2 / 3 ) return p + ( ( q - p ) * ( ( 2 / 3 ) - t ) * 6 ) ;
79- return p ;
80- } ;
63+ K = ( - 2 / 6 ) - K ;
64+ }
65+
66+ const chroma = r - Math . min ( g , b ) ;
67+ const h = Math . abs ( K + ( ( g - b ) / ( ( 6 * chroma ) + Number . EPSILON ) ) ) ;
68+ const s = chroma / ( r + Number . EPSILON ) ;
69+ const v = r ;
8170
71+ dst [ 0 ] = h ;
72+ dst [ 1 ] = s ;
73+ dst [ 2 ] = v ;
74+
75+ return dst ;
76+ } ;
8277
8378/**
84- * Converts an HSL color value to RGB. Conversion formula
85- * adapted from http ://en.wikipedia.org/wiki/HSL_color_space .
86- * Assumes h, s, and l are contained in the set [0, 1] and
79+ * Converts an HSV color value to RGB. Conversion formula
80+ * adapted from https ://gist.github.com/mjackson/5311256 .
81+ * Assumes h, s, and v are contained in the set [0, 1] and
8782 * returns r, g, and b in the set [0, 255].
8883 *
89- * @param {number } h The hue
90- * @param {number } s The saturation
91- * @param {number } l The lightness
92- * @return {Array } The RGB representation
84+ * @param {Array<number> } hsv The HSV color value
85+ * @param {number } hsv.h The hue
86+ * @param {number } hsv.s The saturation
87+ * @param {number } hsv.v The value
88+ * @param {Uint8Array|Uint8ClampedArray } dst The array to store the RGB values in
89+ * @return {Uint8Array|Uint8ClampedArray } The `dst` array passed in
9390 */
94- const hslToRgb = ( [ h , s , l ] ) => {
95- let r ;
96- let g ;
97- let b ;
98-
91+ const hsvToRgb = ( [ h , s , v ] , dst ) => {
9992 if ( s === 0 ) {
100- r = g = b = l ; // achromatic
101- } else {
102-
103- const q = l < 0.5 ? l * ( 1 + s ) : l + s - ( l * s ) ;
104- const p = ( 2 * l ) - q ;
93+ dst [ 0 ] = dst [ 1 ] = dst [ 2 ] = ( v * 255 ) + 0.5 ;
94+ return dst ;
95+ }
10596
106- r = hue2rgb ( p , q , h + ( 1 / 3 ) ) ;
107- g = hue2rgb ( p , q , h ) ;
108- b = hue2rgb ( p , q , h - ( 1 / 3 ) ) ;
97+ // keep hue in [0,1) so the `switch(i)` below only needs 6 cases (0-5)
98+ h %= 1 ;
99+ const i = ( h * 6 ) | 0 ;
100+ const f = ( h * 6 ) - i ;
101+ const p = v * ( 1 - s ) ;
102+ const q = v * ( 1 - ( s * f ) ) ;
103+ const t = v * ( 1 - ( s * ( 1 - f ) ) ) ;
104+
105+ let r = 0 ;
106+ let g = 0 ;
107+ let b = 0 ;
108+
109+ switch ( i ) {
110+ case 0 : r = v ; g = t ; b = p ; break ;
111+ case 1 : r = q ; g = v ; b = p ; break ;
112+ case 2 : r = p ; g = v ; b = t ; break ;
113+ case 3 : r = p ; g = q ; b = v ; break ;
114+ case 4 : r = t ; g = p ; b = v ; break ;
115+ case 5 : r = v ; g = p ; b = q ; break ;
109116 }
110117
111- return [ r * 255 , g * 255 , b * 255 ] ;
118+ // Add 0.5 in order to round. Setting integer TypedArray elements implicitly floors.
119+ dst [ 0 ] = ( r * 255 ) + 0.5 ;
120+ dst [ 1 ] = ( g * 255 ) + 0.5 ;
121+ dst [ 2 ] = ( b * 255 ) + 0.5 ;
122+ return dst ;
112123} ;
113124
114125class EffectTransform {
@@ -146,38 +157,43 @@ class EffectTransform {
146157 inOutColor [ 1 ] /= alpha ;
147158 inOutColor [ 2 ] /= alpha ;
148159
149- // vec3 hsl = convertRGB2HSL(gl_FragColor.xyz);
150- const hsl = rgbToHsl ( inOutColor ) ;
151-
152160 if ( enableColor ) {
161+ // vec3 hsv = convertRGB2HSV(gl_FragColor.xyz);
162+ const hsv = rgbToHsv ( inOutColor , __hsv ) ;
163+
153164 // this code forces grayscale values to be slightly saturated
154165 // so that some slight change of hue will be visible
155166 // const float minLightness = 0.11 / 2.0;
156- const minL = 0.11 / 2.0 ;
167+ const minV = 0.11 / 2.0 ;
157168 // const float minSaturation = 0.09;
158169 const minS = 0.09 ;
159- // if (hsl .z < minLightness) hsl = vec3(0.0, 1.0, minLightness);
160- if ( hsl [ 2 ] < minL ) {
161- hsl [ 0 ] = 0 ;
162- hsl [ 1 ] = 1 ;
163- hsl [ 2 ] = minL ;
164- // else if (hsl .y < minSaturation) hsl = vec3(0.0, minSaturation, hsl .z);
165- } else if ( hsl [ 1 ] < minS ) {
166- hsl [ 0 ] = 0 ;
167- hsl [ 1 ] = minS ;
170+ // if (hsv .z < minLightness) hsv = vec3(0.0, 1.0, minLightness);
171+ if ( hsv [ 2 ] < minV ) {
172+ hsv [ 0 ] = 0 ;
173+ hsv [ 1 ] = 1 ;
174+ hsv [ 2 ] = minV ;
175+ // else if (hsv .y < minSaturation) hsv = vec3(0.0, minSaturation, hsv .z);
176+ } else if ( hsv [ 1 ] < minS ) {
177+ hsv [ 0 ] = 0 ;
178+ hsv [ 1 ] = minS ;
168179 }
169180
170- // hsl.x = mod(hsl.x + u_color, 1.0);
171- // if (hsl.x < 0.0) hsl.x += 1.0;
172- hsl [ 0 ] = ( uniforms . u_color + hsl [ 0 ] + 1 ) % 1 ;
181+ // hsv.x = mod(hsv.x + u_color, 1.0);
182+ // if (hsv.x < 0.0) hsv.x += 1.0;
183+ hsv [ 0 ] = ( uniforms . u_color + hsv [ 0 ] + 1 ) ;
184+
185+ // gl_FragColor.rgb = convertHSV2RGB(hsl);
186+ hsvToRgb ( hsv , inOutColor ) ;
173187 }
174188
175189 if ( enableBrightness ) {
176- // hsl.z = clamp(hsl.z + u_brightness, 0.0, 1.0);
177- hsl [ 2 ] = Math . min ( 1 , hsl [ 2 ] + uniforms . u_brightness ) ;
190+ const brightness = uniforms . u_brightness * 255 ;
191+ // gl_FragColor.rgb = clamp(gl_FragColor.rgb + vec3(u_brightness), vec3(0), vec3(1));
192+ // We don't need to clamp because the Uint8ClampedArray does that for us
193+ inOutColor [ 0 ] += brightness ;
194+ inOutColor [ 1 ] += brightness ;
195+ inOutColor [ 2 ] += brightness ;
178196 }
179- // gl_FragColor.rgb = convertHSL2RGB(hsl);
180- inOutColor . set ( hslToRgb ( hsl ) ) ;
181197
182198 // gl_FragColor.rgb *= gl_FragColor.a + epsilon;
183199 // Now we're doing the reverse, premultiplying by the alpha once again.
0 commit comments