Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / src / draw / Color.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * Represents an RGB color and provides helper functions get
17  * color components in HSL color space.
18  */
19 Ext.define('Ext.draw.Color', {
20
21     /* Begin Definitions */
22
23     /* End Definitions */
24
25     colorToHexRe: /(.*?)rgb\((\d+),\s*(\d+),\s*(\d+)\)/,
26     rgbRe: /\s*rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)\s*/,
27     hexRe: /\s*#([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)([0-9a-fA-F][0-9a-fA-F]?)\s*/,
28
29     /**
30      * @cfg {Number} lightnessFactor
31      *
32      * The default factor to compute the lighter or darker color. Defaults to 0.2.
33      */
34     lightnessFactor: 0.2,
35
36     /**
37      * Creates new Color.
38      * @param {Number} red Red component (0..255)
39      * @param {Number} green Green component (0..255)
40      * @param {Number} blue Blue component (0..255)
41      */
42     constructor : function(red, green, blue) {
43         var me = this,
44             clamp = Ext.Number.constrain;
45         me.r = clamp(red, 0, 255);
46         me.g = clamp(green, 0, 255);
47         me.b = clamp(blue, 0, 255);
48     },
49
50     /**
51      * Get the red component of the color, in the range 0..255.
52      * @return {Number}
53      */
54     getRed: function() {
55         return this.r;
56     },
57
58     /**
59      * Get the green component of the color, in the range 0..255.
60      * @return {Number}
61      */
62     getGreen: function() {
63         return this.g;
64     },
65
66     /**
67      * Get the blue component of the color, in the range 0..255.
68      * @return {Number}
69      */
70     getBlue: function() {
71         return this.b;
72     },
73
74     /**
75      * Get the RGB values.
76      * @return {Number[]}
77      */
78     getRGB: function() {
79         var me = this;
80         return [me.r, me.g, me.b];
81     },
82
83     /**
84      * Get the equivalent HSL components of the color.
85      * @return {Number[]}
86      */
87     getHSL: function() {
88         var me = this,
89             r = me.r / 255,
90             g = me.g / 255,
91             b = me.b / 255,
92             max = Math.max(r, g, b),
93             min = Math.min(r, g, b),
94             delta = max - min,
95             h,
96             s = 0,
97             l = 0.5 * (max + min);
98
99         // min==max means achromatic (hue is undefined)
100         if (min != max) {
101             s = (l < 0.5) ? delta / (max + min) : delta / (2 - max - min);
102             if (r == max) {
103                 h = 60 * (g - b) / delta;
104             } else if (g == max) {
105                 h = 120 + 60 * (b - r) / delta;
106             } else {
107                 h = 240 + 60 * (r - g) / delta;
108             }
109             if (h < 0) {
110                 h += 360;
111             }
112             if (h >= 360) {
113                 h -= 360;
114             }
115         }
116         return [h, s, l];
117     },
118
119     /**
120      * Return a new color that is lighter than this color.
121      * @param {Number} factor Lighter factor (0..1), default to 0.2
122      * @return Ext.draw.Color
123      */
124     getLighter: function(factor) {
125         var hsl = this.getHSL();
126         factor = factor || this.lightnessFactor;
127         hsl[2] = Ext.Number.constrain(hsl[2] + factor, 0, 1);
128         return this.fromHSL(hsl[0], hsl[1], hsl[2]);
129     },
130
131     /**
132      * Return a new color that is darker than this color.
133      * @param {Number} factor Darker factor (0..1), default to 0.2
134      * @return Ext.draw.Color
135      */
136     getDarker: function(factor) {
137         factor = factor || this.lightnessFactor;
138         return this.getLighter(-factor);
139     },
140
141     /**
142      * Return the color in the hex format, i.e. '#rrggbb'.
143      * @return {String}
144      */
145     toString: function() {
146         var me = this,
147             round = Math.round,
148             r = round(me.r).toString(16),
149             g = round(me.g).toString(16),
150             b = round(me.b).toString(16);
151         r = (r.length == 1) ? '0' + r : r;
152         g = (g.length == 1) ? '0' + g : g;
153         b = (b.length == 1) ? '0' + b : b;
154         return ['#', r, g, b].join('');
155     },
156
157     /**
158      * Convert a color to hexadecimal format.
159      *
160      * **Note:** This method is both static and instance.
161      *
162      * @param {String/String[]} color The color value (i.e 'rgb(255, 255, 255)', 'color: #ffffff').
163      * Can also be an Array, in this case the function handles the first member.
164      * @returns {String} The color in hexadecimal format.
165      * @static
166      */
167     toHex: function(color) {
168         if (Ext.isArray(color)) {
169             color = color[0];
170         }
171         if (!Ext.isString(color)) {
172             return '';
173         }
174         if (color.substr(0, 1) === '#') {
175             return color;
176         }
177         var digits = this.colorToHexRe.exec(color);
178
179         if (Ext.isArray(digits)) {
180             var red = parseInt(digits[2], 10),
181                 green = parseInt(digits[3], 10),
182                 blue = parseInt(digits[4], 10),
183                 rgb = blue | (green << 8) | (red << 16);
184             return digits[1] + '#' + ("000000" + rgb.toString(16)).slice(-6);
185         }
186         else {
187             return '';
188         }
189     },
190
191     /**
192      * Parse the string and create a new color.
193      *
194      * Supported formats: '#rrggbb', '#rgb', and 'rgb(r,g,b)'.
195      *
196      * If the string is not recognized, an undefined will be returned instead.
197      *
198      * **Note:** This method is both static and instance.
199      *
200      * @param {String} str Color in string.
201      * @returns Ext.draw.Color
202      * @static
203      */
204     fromString: function(str) {
205         var values, r, g, b,
206             parse = parseInt;
207
208         if ((str.length == 4 || str.length == 7) && str.substr(0, 1) === '#') {
209             values = str.match(this.hexRe);
210             if (values) {
211                 r = parse(values[1], 16) >> 0;
212                 g = parse(values[2], 16) >> 0;
213                 b = parse(values[3], 16) >> 0;
214                 if (str.length == 4) {
215                     r += (r * 16);
216                     g += (g * 16);
217                     b += (b * 16);
218                 }
219             }
220         }
221         else {
222             values = str.match(this.rgbRe);
223             if (values) {
224                 r = values[1];
225                 g = values[2];
226                 b = values[3];
227             }
228         }
229
230         return (typeof r == 'undefined') ? undefined : Ext.create('Ext.draw.Color', r, g, b);
231     },
232
233     /**
234      * Returns the gray value (0 to 255) of the color.
235      *
236      * The gray value is calculated using the formula r*0.3 + g*0.59 + b*0.11.
237      *
238      * @returns {Number}
239      */
240     getGrayscale: function() {
241         // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
242         return this.r * 0.3 + this.g * 0.59 + this.b * 0.11;
243     },
244
245     /**
246      * Create a new color based on the specified HSL values.
247      *
248      * **Note:** This method is both static and instance.
249      *
250      * @param {Number} h Hue component (0..359)
251      * @param {Number} s Saturation component (0..1)
252      * @param {Number} l Lightness component (0..1)
253      * @returns Ext.draw.Color
254      * @static
255      */
256     fromHSL: function(h, s, l) {
257         var C, X, m, i, rgb = [],
258             abs = Math.abs,
259             floor = Math.floor;
260
261         if (s == 0 || h == null) {
262             // achromatic
263             rgb = [l, l, l];
264         }
265         else {
266             // http://en.wikipedia.org/wiki/HSL_and_HSV#From_HSL
267             // C is the chroma
268             // X is the second largest component
269             // m is the lightness adjustment
270             h /= 60;
271             C = s * (1 - abs(2 * l - 1));
272             X = C * (1 - abs(h - 2 * floor(h / 2) - 1));
273             m = l - C / 2;
274             switch (floor(h)) {
275                 case 0:
276                     rgb = [C, X, 0];
277                     break;
278                 case 1:
279                     rgb = [X, C, 0];
280                     break;
281                 case 2:
282                     rgb = [0, C, X];
283                     break;
284                 case 3:
285                     rgb = [0, X, C];
286                     break;
287                 case 4:
288                     rgb = [X, 0, C];
289                     break;
290                 case 5:
291                     rgb = [C, 0, X];
292                     break;
293             }
294             rgb = [rgb[0] + m, rgb[1] + m, rgb[2] + m];
295         }
296         return Ext.create('Ext.draw.Color', rgb[0] * 255, rgb[1] * 255, rgb[2] * 255);
297     }
298 }, function() {
299     var prototype = this.prototype;
300
301     //These functions are both static and instance. TODO: find a more elegant way of copying them
302     this.addStatics({
303         fromHSL: function() {
304             return prototype.fromHSL.apply(prototype, arguments);
305         },
306         fromString: function() {
307             return prototype.fromString.apply(prototype, arguments);
308         },
309         toHex: function() {
310             return prototype.toHex.apply(prototype, arguments);
311         }
312     });
313 });
314