• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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};