1/* 2 * This file houses functions that deal with color. 3 */ 4 5// Constructs a Color with the same API as CSS's rgba(), that is 6// r,g,b are 0-255, and a is 0.0 to 1.0. 7// if a is omitted, it will be assumed to be 1.0 8// Internally, Colors are a TypedArray of four unpremultiplied 32-bit floats: a, r, g, b 9// In order to construct one with more precision or in a wider gamut, use 10// CanvasKit.Color4f 11CanvasKit.Color = function(r, g, b, a) { 12 if (a === undefined) { 13 a = 1; 14 } 15 return CanvasKit.Color4f(clamp(r)/255, clamp(g)/255, clamp(b)/255, a); 16}; 17 18// Constructs a Color as a 32 bit unsigned integer, with 8 bits assigned to each channel. 19// Channels are expected to be between 0 and 255 and will be clamped as such. 20CanvasKit.ColorAsInt = function(r, g, b, a) { 21 // default to opaque 22 if (a === undefined) { 23 a = 255; 24 } 25 // This is consistent with how Skia represents colors in C++, as an unsigned int. 26 // This is also consistent with how Flutter represents colors: 27 // https://github.com/flutter/engine/blob/243bb59c7179a7e701ce478080d6ce990710ae73/lib/web_ui/lib/src/ui/painting.dart#L50 28 return (((clamp(a) << 24) | (clamp(r) << 16) | (clamp(g) << 8) | (clamp(b) << 0) 29 & 0xFFFFFFF) // This truncates the unsigned to 32 bits and signals to JS engines they can 30 // represent the number with an int instead of a double. 31 >>> 0); // This makes the value an unsigned int. 32}; 33// Construct a 4-float color. 34// Opaque if opacity is omitted. 35CanvasKit.Color4f = function(r, g, b, a) { 36 if (a === undefined) { 37 a = 1; 38 } 39 return Float32Array.of(r, g, b, a); 40}; 41 42// Color constants use property getters to prevent other code from accidentally 43// changing them. 44Object.defineProperty(CanvasKit, 'TRANSPARENT', { 45 get: function() { return CanvasKit.Color4f(0, 0, 0, 0); } 46}); 47Object.defineProperty(CanvasKit, 'BLACK', { 48 get: function() { return CanvasKit.Color4f(0, 0, 0, 1); } 49}); 50Object.defineProperty(CanvasKit, 'WHITE', { 51 get: function() { return CanvasKit.Color4f(1, 1, 1, 1); } 52}); 53Object.defineProperty(CanvasKit, 'RED', { 54 get: function() { return CanvasKit.Color4f(1, 0, 0, 1); } 55}); 56Object.defineProperty(CanvasKit, 'GREEN', { 57 get: function() { return CanvasKit.Color4f(0, 1, 0, 1); } 58}); 59Object.defineProperty(CanvasKit, 'BLUE', { 60 get: function() { return CanvasKit.Color4f(0, 0, 1, 1); } 61}); 62Object.defineProperty(CanvasKit, 'YELLOW', { 63 get: function() { return CanvasKit.Color4f(1, 1, 0, 1); } 64}); 65Object.defineProperty(CanvasKit, 'CYAN', { 66 get: function() { return CanvasKit.Color4f(0, 1, 1, 1); } 67}); 68Object.defineProperty(CanvasKit, 'MAGENTA', { 69 get: function() { return CanvasKit.Color4f(1, 0, 1, 1); } 70}); 71 72// returns a css style [r, g, b, a] from a CanvasKit.Color 73// where r, g, b are returned as ints in the range [0, 255] 74// where a is scaled between 0 and 1.0 75CanvasKit.getColorComponents = function(color) { 76 return [ 77 Math.floor(color[0]*255), 78 Math.floor(color[1]*255), 79 Math.floor(color[2]*255), 80 color[3] 81 ]; 82}; 83 84// parseColorString takes in a CSS color value and returns a CanvasKit.Color 85// (which is an array of 4 floats in RGBA order). An optional colorMap 86// may be provided which maps custom strings to values. 87// In the CanvasKit canvas2d shim layer, we provide this map for processing 88// canvas2d calls, but not here for code size reasons. 89CanvasKit.parseColorString = function(colorStr, colorMap) { 90 colorStr = colorStr.toLowerCase(); 91 // See https://drafts.csswg.org/css-color/#typedef-hex-color 92 if (colorStr.startsWith('#')) { 93 var r, g, b, a = 255; 94 switch (colorStr.length) { 95 case 9: // 8 hex chars #RRGGBBAA 96 a = parseInt(colorStr.slice(7, 9), 16); 97 case 7: // 6 hex chars #RRGGBB 98 r = parseInt(colorStr.slice(1, 3), 16); 99 g = parseInt(colorStr.slice(3, 5), 16); 100 b = parseInt(colorStr.slice(5, 7), 16); 101 break; 102 case 5: // 4 hex chars #RGBA 103 // multiplying by 17 is the same effect as 104 // appending another character of the same value 105 // e.g. e => ee == 14 => 238 106 a = parseInt(colorStr.slice(4, 5), 16) * 17; 107 case 4: // 6 hex chars #RGB 108 r = parseInt(colorStr.slice(1, 2), 16) * 17; 109 g = parseInt(colorStr.slice(2, 3), 16) * 17; 110 b = parseInt(colorStr.slice(3, 4), 16) * 17; 111 break; 112 } 113 return CanvasKit.Color(r, g, b, a/255); 114 115 } else if (colorStr.startsWith('rgba')) { 116 // Trim off rgba( and the closing ) 117 colorStr = colorStr.slice(5, -1); 118 var nums = colorStr.split(','); 119 return CanvasKit.Color(+nums[0], +nums[1], +nums[2], 120 valueOrPercent(nums[3])); 121 } else if (colorStr.startsWith('rgb')) { 122 // Trim off rgba( and the closing ) 123 colorStr = colorStr.slice(4, -1); 124 var nums = colorStr.split(','); 125 // rgb can take 3 or 4 arguments 126 return CanvasKit.Color(+nums[0], +nums[1], +nums[2], 127 valueOrPercent(nums[3])); 128 } else if (colorStr.startsWith('gray(')) { 129 // TODO(kjlubick) 130 } else if (colorStr.startsWith('hsl')) { 131 // TODO(kjlubick) 132 } else if (colorMap) { 133 // Try for named color 134 var nc = colorMap[colorStr]; 135 if (nc !== undefined) { 136 return nc; 137 } 138 } 139 Debug('unrecognized color ' + colorStr); 140 return CanvasKit.BLACK; 141}; 142 143function isCanvasKitColor(ob) { 144 if (!ob) { 145 return false; 146 } 147 return (ob.constructor === Float32Array && ob.length === 4); 148} 149 150// Warning information is lost by this conversion 151function toUint32Color(c) { 152 return ((clamp(c[3]*255) << 24) | (clamp(c[0]*255) << 16) | (clamp(c[1]*255) << 8) | (clamp(c[2]*255) << 0)) >>> 0; 153} 154// Accepts various colors representations and converts them to an array of int colors. 155// Does not handle builders. 156function assureIntColors(arr) { 157 if (wasMalloced(arr)) { 158 return arr; // Assume if the memory was malloced that the user has done it correctly. 159 } else if (arr instanceof Float32Array) { 160 var count = Math.floor(arr.length / 4); 161 var result = new Uint32Array(count); 162 for (var i = 0; i < count; i ++) { 163 result[i] = toUint32Color(arr.slice(i*4, (i+1)*4)); 164 } 165 return result; 166 } else if (arr instanceof Uint32Array) { 167 return arr; 168 } else if (arr instanceof Array && arr[0] instanceof Float32Array) { 169 return arr.map(toUint32Color); 170 } 171} 172 173function uIntColorToCanvasKitColor(c) { 174 return CanvasKit.Color( 175 (c >> 16) & 0xFF, 176 (c >> 8) & 0xFF, 177 (c >> 0) & 0xFF, 178 ((c >> 24) & 0xFF) / 255 179 ); 180} 181 182function valueOrPercent(aStr) { 183 if (aStr === undefined) { 184 return 1; // default to opaque. 185 } 186 var a = parseFloat(aStr); 187 if (aStr && aStr.indexOf('%') !== -1) { 188 return a / 100; 189 } 190 return a; 191} 192 193function clamp(c) { 194 return Math.round(Math.max(0, Math.min(c || 0, 255))); 195} 196 197// TODO(kjlubick) delete this, as it is now trivial with 4f colors 198CanvasKit.multiplyByAlpha = function(color, alpha) { 199 // make a copy of the color so the function remains pure. 200 var result = color.slice(); 201 result[3] = Math.max(0, Math.min(result[3] * alpha, 1)); 202 return result; 203};