blob: aef32ce1ff816f33f2eeac0ddd88ad61f2576f6e [file] [log] [blame]
/*
Copyright (c) 2004-2005, The Dojo Foundation
All Rights Reserved.
Licensed under the Academic Free License version 2.1 or above OR the
modified BSD license. For more information on Dojo licensing, see:
http://dojotoolkit.org/community/licensing.shtml
*/
dojo.provide("dojo.graphics.color");
dojo.require("dojo.lang");
dojo.require("dojo.string");
dojo.require("dojo.math");
// TODO: rewrite the "x2y" methods to take advantage of the parsing
// abilities of the Color object. Also, beef up the Color
// object (as possible) to parse most common formats
// takes an r, g, b, a(lpha) value, [r, g, b, a] array, "rgb(...)" string, hex string (#aaa, #aaaaaa, aaaaaaa)
dojo.graphics.color.Color = function(r, g, b, a) {
// dojo.debug("r:", r[0], "g:", r[1], "b:", r[2]);
if(dojo.lang.isArray(r)) {
this.r = r[0];
this.g = r[1];
this.b = r[2];
this.a = r[3]||1.0;
} else if(dojo.lang.isString(r)) {
var rgb = dojo.graphics.color.extractRGB(r);
this.r = rgb[0];
this.g = rgb[1];
this.b = rgb[2];
this.a = g||1.0;
} else if(r instanceof dojo.graphics.color.Color) {
this.r = r.r;
this.b = r.b;
this.g = r.g;
this.a = r.a;
} else {
this.r = r;
this.g = g;
this.b = b;
this.a = a;
}
}
dojo.lang.extend(dojo.graphics.color.Color, {
toRgb: function(includeAlpha) {
if(includeAlpha) {
return this.toRgba();
} else {
return [this.r, this.g, this.b];
}
},
toRgba: function() {
return [this.r, this.g, this.b, this.a];
},
toHex: function() {
return dojo.graphics.color.rgb2hex(this.toRgb());
},
toCss: function() {
return "rgb(" + this.toRgb().join() + ")";
},
toString: function() {
return this.toHex(); // decent default?
},
toHsv: function() {
return dojo.graphics.color.rgb2hsv(this.toRgb());
},
toHsl: function() {
return dojo.graphics.color.rgb2hsl(this.toRgb());
},
blend: function(color, weight) {
return dojo.graphics.color.blend(this.toRgb(), new Color(color).toRgb(), weight);
}
});
dojo.graphics.color.named = {
white: [255,255,255],
black: [0,0,0],
red: [255,0,0],
green: [0,255,0],
blue: [0,0,255],
navy: [0,0,128],
gray: [128,128,128],
silver: [192,192,192]
};
// blend colors a and b (both as RGB array or hex strings) with weight from -1 to +1, 0 being a 50/50 blend
dojo.graphics.color.blend = function(a, b, weight) {
if(typeof a == "string") { return dojo.graphics.color.blendHex(a, b, weight); }
if(!weight) { weight = 0; }
else if(weight > 1) { weight = 1; }
else if(weight < -1) { weight = -1; }
var c = new Array(3);
for(var i = 0; i < 3; i++) {
var half = Math.abs(a[i] - b[i])/2;
c[i] = Math.floor(Math.min(a[i], b[i]) + half + (half * weight));
}
return c;
}
// very convenient blend that takes and returns hex values
// (will get called automatically by blend when blend gets strings)
dojo.graphics.color.blendHex = function(a, b, weight) {
return dojo.graphics.color.rgb2hex(dojo.graphics.color.blend(dojo.graphics.color.hex2rgb(a), dojo.graphics.color.hex2rgb(b), weight));
}
// get RGB array from css-style color declarations
dojo.graphics.color.extractRGB = function(color) {
var hex = "0123456789abcdef";
color = color.toLowerCase();
if( color.indexOf("rgb") == 0 ) {
var matches = color.match(/rgba*\((\d+), *(\d+), *(\d+)/i);
var ret = matches.splice(1, 3);
return ret;
} else {
var colors = dojo.graphics.color.hex2rgb(color);
if(colors) {
return colors;
} else {
// named color (how many do we support?)
return dojo.graphics.color.named[color] || [255, 255, 255];
}
}
}
dojo.graphics.color.hex2rgb = function(hex) {
var hexNum = "0123456789ABCDEF";
var rgb = new Array(3);
if( hex.indexOf("#") == 0 ) { hex = hex.substring(1); }
hex = hex.toUpperCase();
if(hex.replace(new RegExp("["+hexNum+"]", "g"), "") != "") {
return null;
}
if( hex.length == 3 ) {
rgb[0] = hex.charAt(0) + hex.charAt(0)
rgb[1] = hex.charAt(1) + hex.charAt(1)
rgb[2] = hex.charAt(2) + hex.charAt(2);
} else {
rgb[0] = hex.substring(0, 2);
rgb[1] = hex.substring(2, 4);
rgb[2] = hex.substring(4);
}
for(var i = 0; i < rgb.length; i++) {
rgb[i] = hexNum.indexOf(rgb[i].charAt(0)) * 16 + hexNum.indexOf(rgb[i].charAt(1));
}
return rgb;
}
dojo.graphics.color.rgb2hex = function(r, g, b) {
if(dojo.lang.isArray(r)) {
g = r[1] || 0;
b = r[2] || 0;
r = r[0] || 0;
}
return ["#",
dojo.string.pad(r.toString(16), 2),
dojo.string.pad(g.toString(16), 2),
dojo.string.pad(b.toString(16), 2)].join("");
}
dojo.graphics.color.rgb2hsv = function(r, g, b){
if (dojo.lang.isArray(r)) {
b = r[2] || 0;
g = r[1] || 0;
r = r[0] || 0;
}
// r,g,b, each 0 to 255, to HSV.
// h = 0.0 to 360.0 (corresponding to 0..360.0 degrees around hexcone)
// s = 0.0 (shade of gray) to 1.0 (pure color)
// v = 0.0 (black) to 1.0 {white)
//
// Based on C Code in "Computer Graphics -- Principles and Practice,"
// Foley et al, 1996, p. 592.
//
// our calculatuions are based on 'regular' values (0-360, 0-1, 0-1)
// but we return bytes values (0-255, 0-255, 0-255)
var h = null;
var s = null;
var v = null;
var min = Math.min(r, g, b);
v = Math.max(r, g, b);
var delta = v - min;
// calculate saturation (0 if r, g and b are all 0)
s = (v == 0) ? 0 : delta/v;
if (s == 0){
// achromatic: when saturation is, hue is undefined
h = 0;
}else{
// chromatic
if (r == v){
// between yellow and magenta
h = 60 * (g - b) / delta;
}else{
if (g == v){
// between cyan and yellow
h = 120 + 60 * (b - r) / delta;
}else{
if (b == v){
// between magenta and cyan
h = 240 + 60 * (r - g) / delta;
}
}
}
if (h < 0){
h += 360;
}
}
h = (h == 0) ? 360 : Math.ceil((h / 360) * 255);
s = Math.ceil(s * 255);
return [h, s, v];
}
dojo.graphics.color.hsv2rgb = function(h, s, v){
if (dojo.lang.isArray(h)) {
v = h[2] || 0;
s = h[1] || 0;
h = h[0] || 0;
}
h = (h / 255) * 360;
if (h == 360){ h = 0;}
s = s / 255;
v = v / 255;
// Based on C Code in "Computer Graphics -- Principles and Practice,"
// Foley et al, 1996, p. 593.
//
// H = 0.0 to 360.0 (corresponding to 0..360 degrees around hexcone) 0 for S = 0
// S = 0.0 (shade of gray) to 1.0 (pure color)
// V = 0.0 (black) to 1.0 (white)
var r = null;
var g = null;
var b = null;
if (s == 0){
// color is on black-and-white center line
// achromatic: shades of gray
r = v;
g = v;
b = v;
}else{
// chromatic color
var hTemp = h / 60; // h is now IN [0,6]
var i = Math.floor(hTemp); // largest integer <= h
var f = hTemp - i; // fractional part of h
var p = v * (1 - s);
var q = v * (1 - (s * f));
var t = v * (1 - (s * (1 - f)));
switch(i){
case 0: r = v; g = t; b = p; break;
case 1: r = q; g = v; b = p; break;
case 2: r = p; g = v; b = t; break;
case 3: r = p; g = q; b = v; break;
case 4: r = t; g = p; b = v; break;
case 5: r = v; g = p; b = q; break;
}
}
r = Math.ceil(r * 255);
g = Math.ceil(g * 255);
b = Math.ceil(b * 255);
return [r, g, b];
}
dojo.graphics.color.rgb2hsl = function(r, g, b){
if (dojo.lang.isArray(r)) {
b = r[2] || 0;
g = r[1] || 0;
r = r[0] || 0;
}
r /= 255;
g /= 255;
b /= 255;
//
// based on C code from http://astronomy.swin.edu.au/~pbourke/colour/hsl/
//
var h = null;
var s = null;
var l = null;
var min = Math.min(r, g, b);
var max = Math.max(r, g, b);
var delta = max - min;
l = (min + max) / 2;
s = 0;
if ((l > 0) && (l < 1)){
s = delta / ((l < 0.5) ? (2 * l) : (2 - 2 * l));
}
h = 0;
if (delta > 0) {
if ((max == r) && (max != g)){
h += (g - b) / delta;
}
if ((max == g) && (max != b)){
h += (2 + (b - r) / delta);
}
if ((max == b) && (max != r)){
h += (4 + (r - g) / delta);
}
h *= 60;
}
h = (h == 0) ? 360 : Math.ceil((h / 360) * 255);
s = Math.ceil(s * 255);
l = Math.ceil(l * 255);
return [h, s, l];
}
dojo.graphics.color.hsl2rgb = function(h, s, l){
if (dojo.lang.isArray(h)) {
l = h[2] || 0;
s = h[1] || 0;
h = h[0] || 0;
}
h = (h / 255) * 360;
if (h == 360){ h = 0;}
s = s / 255;
l = l / 255;
//
// based on C code from http://astronomy.swin.edu.au/~pbourke/colour/hsl/
//
while (h < 0){ h += 360; }
while (h > 360){ h -= 360; }
if (h < 120){
r = (120 - h) / 60;
g = h / 60;
b = 0;
}else if (h < 240){
r = 0;
g = (240 - h) / 60;
b = (h - 120) / 60;
}else{
r = (h - 240) / 60;
g = 0;
b = (360 - h) / 60;
}
r = Math.min(r, 1);
g = Math.min(g, 1);
b = Math.min(b, 1);
r = 2 * s * r + (1 - s);
g = 2 * s * g + (1 - s);
b = 2 * s * b + (1 - s);
if (l < 0.5){
r = l * r;
g = l * g;
b = l * b;
}else{
r = (1 - l) * r + 2 * l - 1;
g = (1 - l) * g + 2 * l - 1;
b = (1 - l) * b + 2 * l - 1;
}
r = Math.ceil(r * 255);
g = Math.ceil(g * 255);
b = Math.ceil(b * 255);
return [r, g, b];
}
dojo.graphics.color.hsl2hex = function(h, s, l){
var rgb = dojo.graphics.color.hsl2rgb(h, s, l);
return dojo.graphics.color.rgb2hex(rgb[0], rgb[1], rgb[2]);
}
dojo.graphics.color.hex2hsl = function(hex){
var rgb = dojo.graphics.color.hex2rgb(hex);
return dojo.graphics.color.rgb2hsl(rgb[0], rgb[1], rgb[2]);
}