Color Library

Notice: the code presented here is a bit outdated. This library was absorbed into the Javable Widget Project and the latest sources can be found there, released under the project license.

This is a Color library I whipped up for a project where it would be overkill. I got carried away. The base conversion functions are the standard equations to convert between color-spaces. However, credit goes to Harry (g6auc) for actually putting them into readable/understandable JavaScript. :) I just formatted them as objects with tons of really neat and cool properties.

Whats in here:
 * Color (RGB)
 * CMYK
 * HSV

Each Color-space object has the following:
 * Ability to convert to the other color-spaces.
 * Get a String formatted for canvas, ex: "rgba(244, 238, 224, 1)"
 * Getting its complementary color.
 * Adding and subtracting colors.
 * JSDoc formatting (mostly perfect? I may have forgotten a few here obscure things...)

It is worth mentioning Color (RGB)'s extra properties/abilities...
 * Support for alpha/transparency. (This info is lost when converted to another color-space.)
 * Ability to convert Hexadecimal colors to RGB. (Send in one String with a "#" in it, instead of 3 integers.)

To create an object, just instantiate it: var red = new Color(255, 0, 0);

Code
Here's the lib... enjoy!

/** * A Color object that does not handle transparency. (does not need to). * Credit goes to Harry (g6auc) for the HEX -> RGB and vice versa. * Operations can be done on two colors such as adding, subtracting, or comparing colors. * @param {int|String} hexR This is either a hexadecimal color string, or a integer representing the red value. * @param {int} g This is an integer representing the green value. Undefined if hex String is used as the only parameter. * @param {int} b This is an integer representing the blue value. Undefined if a hex string is used instead. */ function Color(hexR, g, b, a) { var color = this;

this.r = 0; this.g = 0; this.b = 0; this.a = 255;

if(hexR !== undefined && hexR.toString.indexOf("#") === 0) { hexR = hexR.replace("#", "");

if(hexR.length >= 3) { if(hexR.length === 3) { var sixHex = "";

sixHex += hexR.charAt(0) + "" + hexR.charAt(0); sixHex += hexR.charAt(1) + "" + hexR.charAt(1); sixHex += hexR.charAt(2) + "" + hexR.charAt(2);

hexR = sixHex; }

this.r = parseInt(hexR.substring(0, 2), 16); // split the HEX color into (r g b)			this.g = parseInt(hexR.substring(2, 4), 16); this.b = parseInt(hexR.substring(4, 6), 16); }	} else if(!isNaN(Number(hexR)) && !isNaN(Number(g)) && !isNaN(Number(b))) { if(!isNaN(hexR) && !isNaN(g) && !isNaN(b)) { this.r = Math.min(255, Math.max(0, Number(hexR))); this.g = Math.min(255, Math.max(0, Number(g))); this.b = Math.min(255, Math.max(0, Number(b))); }

if(!isNaN(Number(a))) { this.a = Math.min(255, Math.max(0, Number(a))); }	}

/**	 * This formats the Color object so it can be directly used with a canvas context. * @returns A String formatted for Canvas. */	this.getCanvasColor = function { return("rgba( " + (color.r) + ", " + (color.g) + ", " + (color.b) + ", " + (color.a/255) + " )"); };

/**	 * @see CMYK * @returns A CMYK object representing this color. */	this.getCMYK = function { var c = 1-(color.r/255);			// first convert to (c m y) values var m = 1-(color.g/255); var y = 1-(color.b/255); var k = Math.min(c, m, y);			// then convert (c m y) to (c m y k)

if (k === 1) { c = 0; m = 0; y = 0; } else { c = Math.round(100*(c - k)/(1 - k)); m = Math.round(100*(m - k)/(1 - k)); y = Math.round(100*(y - k)/(1 - k)); }

k = Math.round(100*k);

return (new CMYK(c, m, y, k)); };

/**	 * @returns A new Color object representing the complementary of the current color. */	this.getComplementary = function { var r1 = 255 - color.r;		var g1 = 255 - color.g;		var b1 = 255 - color.b;

return (new Color(r1, g1, b1)); };

/**	 * @returns A hex String representing this color. */	this.getHex = function { var hexA = "0123456789ABCDEF";

function hex2(n) { return (hexA[(n >>> 4) % 16] + hexA[n % 16]); }

return ("#" + hex2(this.r) + hex2(this.g) + hex2(this.b)); };

/**	 * @see HSV * @returns A HSV object representing this Color. */	this.getHSV = function {

var r = color.r/255; var g = color.g/255; var b = color.b/255;

var max = Math.max(r, g, b); var min = Math.min(r, g, b); var v = max; var delta = max - min; var s;		var h;

if (max === 0) { s = 0; h = undefined; } else { s = delta/max; if (r === max) { h = (g - b)/delta; 			// between yellow & magenta } else if (g === max) { h = 2 + (b - r)/delta;		// between cyan & yellow } else { h = 4 + (r - g)/delta;		// between magenta & cyan }

h *= 60;						// degrees if( h < 0 ) { h += 360; } }		if (isNaN(h)) { h = 0; } h = Math.round(h);				// This is the only value we care about; s = Math.round(100*s); v = Math.round(100*v);

return (new HSV(h, s, v)); };

/**	 * Adds the current color object to the parameter object and returns the result as a new Color. * If it's not a Color object, we return a clone of the object we are trying to add to. * @param {Object} other This the second Color object. * @returns A Color object which is the addition of two Colors. */	this.add = function(other) { if(other.toString.indexOf("Color") >= 0) { var r = Math.max(255, color.r + other.r); var g = Math.max(255, color.g + other.g); var b = Math.max(255, color.b + other.b);

return (new Color(r, g, b)); } else { return (color.clone); }	};

/**	 * We try to the given Color object from the current Color object. * If it's not a Color object, we return a clone of the object we are trying to subtract from. * @param {Object} other This the second Color object. * @returns A Color object which is the subtraction of two Colors. */	this.subtract = function(other) { if(other.toString.indexOf("Color") >= 0) { var r = Math.min(0, color.r - other.r); var g = Math.min(0, color.g - other.g); var b = Math.min(0, color.b - other.b);

return (new Color(r, g, b)); } else { return (color.clone); }	};

/**	 * @param {Object} other This another object we to see if it is equal to this object. If it is a color, it will compare colors. If not, it will always return false. * @returns A boolean. True means the two objects are equal (the same color), false means they are not. */	this.equals = function(other) { if(other.toString.indexOf("Color") >= 0) { return (color.compareTo(other) === 0); } else { return (false); }	};

/**	 * @param {Object} other This another object we wish to compare this color with. If it is not a Color object -1 will be returned. Else, they are converted to HSV and hue values are compared. * @returns An integer. A negative number means it's greater than, zero means its equal to, and a positive number means it's greater than the other Color object. */	this.compareTo = function(other) { if(other.toString.indexOf("Color") >= 0) { var hsvThis = color.getHSV; var hsvOther = other.getHSV;

return (hsvThis.h - hsvOther.h); } else { return -1; }	};

/**	 * @returns A String representation of this Color object. */	this.toString = function { return ("Color( r: " + color.r + ", g: " + color.g + ", b: " + color.b + ", a: " + color.a + " )"); };

/**	 * @returns A copy of this color object, as a new object. */	this.clone = function { return (new Color(color.r, color.g, color.b)); };

/**	 * @returns An eval-able String representing this object. */	this.toSource = function { return ("new Color(" + color.r + ", " + color.g + ", " + color.b + ", " + color.a + ")"); }; }

/** * A Color object designed to store a color in the CMYK colorspace. * Credit goes to Harry (g6auc) for the conversion code. * @param {int} c This is the cyan component. * @param {int} m This is the magenta component. * @param {int} y This is the yellow component. * @param {int} k This is the black component. */ function CMYK(c, m, y, k) { var cmyk = this;

this.c = 0; this.m = 0; this.y = 0; this.k = 0;

if(!isNaN(Number(c)) && !isNaN(Number(m)) && !isNaN(Number(y))) { this.c = Math.min(100, Math.max(0, Number(c))); this.m = Math.min(100, Math.max(0, Number(m))); this.y = Math.min(100, Math.max(0, Number(y)));

if(!isNaN(Number(k))) { this.k = Math.min(100, Math.max(0, Number(k))); }

if(k === 1) { this.c = 0; this.m = 0; this.y = 0; }	}

/**	 * This formats the CMYK object so it can be directly used with a canvas context. * @see Color#getCanvasColor * @returns A String formatted for Canvas. */	this.getCanvasColor = function { return(cmyk.getRGB.getCanvasColor); };

/**	 * @see Color#getComplementary * @returns A new CMYK object representing the complementary of the current color. */	this.getComplementary = function { return (cmyk.getRGB.getComplementary.getCMYK); };

/**	 * @see HSV * @returns A HSV object representing this color. */	this.getHSV = function { return (cmyk.getRGB.getHSV); };

/**	 * @see Color#getHex * @returns A hex String representing this color. */	this.getHex = function { return (cmyk.getRGB.getHex); };

/**	 * @see Color * @returns An RGB object representing this color. */	this.getRGB = function { var c = this.c/100;					// convert to [0,1] var m = this.m/100; var y = this.y/100; var k = this.k/100;

c = c*(1 - k) + k;					// first convert (c m y k) to (c m y) values m = m*(1 - k) + k;		y = y*(1 - k) + k;

var r = Math.round(255*(1 - c));		// then convert (c m k) to (r g b)		var g = Math.round(255*(1 - m)); var b = Math.round(255*(1 - y));

return (new Color(r, g, b)); };

/**	 * Adds the current CMYK object to the parameter object and returns the result as a new CMYK. * If it's not a CMYK object, we return a clone of the object we are trying to add to. * @param {Object} other This the second CMYK object. * @returns A CMYK object which is the addition of two CMYKs. */	this.add = function(other) { if(other.toString.indexOf("CMYK") >= 0) { var c = Math.max(100, cmyk.c + other.c); var m = Math.max(100, cmyk.m + other.m); var y = Math.max(100, cmyk.y + other.y); var k = Math.max(100, cmyk.k + other.k);

return (new CMYK(c, m, y, k)); } else { return (cmyk.clone); }	};

/**	 * We try to the given CMYK object from the current CMYK object. * If it's not a CMYK object, we return a clone of the object we are trying to subtract from. * @param {Object} other This the second CMYK object. * @returns A CMYK object which is the subtraction of two CMYKs. */	this.subtract = function(other) { if(other.toString.indexOf("CMYK") >= 0) { var c = Math.min(0, cmyk.c - other.c); var m = Math.min(0, cmyk.m - other.m); var y = Math.min(0, cmyk.y - other.y); var k = Math.min(0, cmyk.k - other.k);

return (new CMYK(c, m, y, k)); } else { return (cmyk.clone); }	};

/**	 * @see #compareTo * @param {Object} other This another object we to see if it is equal to this object. If it is a CMYK, it will compare colors. If not, it will always return false. * @returns A boolean. True means the two objects are equal (the same color), false means they are not. */	this.equals = function(other) { if(other.toString.indexOf("CMYK") >= 0) { return (cmyk.compareTo(other) === 0); } else { return (false); }	};

/**	 * @param {Object} other This another object we wish to compare this color with. If it is not a CMYK object -1 will be returned. Else, the average values will be compared. * @returns An integer. A negative number means it's greater than, zero means its equal to, and a positive number means it's greater than the other CMYK object. */	this.compareTo = function(other) { if(other.toString.indexOf("CMYK") >= 0) { var cmykAvg = (cmyk.c + cmyk.m + cmyk.y + cmyk.k)/400; var otherAvg = (other.c + other.m + other.y + other.k)/400; return (cmykAvg - otherAvg); } else { return -1; }	};

/**	 * @returns A String representation of this CMYK object. */	this.toString = function { return ("CMYK( cyan: " + cmyk.c + ", magenta: " + cmyk.m + ", yellow: " + cmyk.y + ", black: " + cmyk.k + " )"); };

/**	 * @returns A copy of this CMYK object, as a new object. */	this.clone = function { return (new CMYK(cmyk.c, cmyk.m, cmyk.y, cmyk.k)); };

/**	 * @returns An eval-able String representing this object. */	this.toSource = function { return ("new CMYK(" + cmyk.c + "," + cmyk.m + ", " + cmyk.y + ", " + cmyk.k + ")"); }; }

/** * A Color object designed to store a color in the HSV colorspace. * Credit goes to Harry (g6auc) for the conversion code. * @param {int} h This is the hue value. * @param {int} s This is the saturation value. * @param {int} v This is the value (value? ... heh, wow). */ function HSV(h, s, v) { var hsv = this;

this.h = 0; this.s = 0; this.v = 0;

if(!isNaN(Number(h)) && !isNaN(Number(s)) && !isNaN(Number(v))) { this.h = Math.min(360, Math.max(0, h)); this.s = Math.min(100, Math.max(0, s)); this.v = Math.min(100, Math.max(0, v)); }

/**	 * This formats the HSV object so it can be directly used with a canvas context. * @see Color#getCanvasColor * @returns A String formatted for Canvas. */	this.getCanvasColor = function { return(hsv.getRGB.getCanvasColor); };

/**	 * @see CMYK * @returns A CMYK object representing this color. */	this.getCMYK = function { return (hsv.getRGB.getCMYK); };

/**	 * @returns A new Color object representing the complementary of the current color. */	this.getComplementary = function { var s = hsv.s/100; var v = hsv.v/100; var h = hsv.h;

var h1 = Math.round((h + 180) % 360); var s1 = Math.round(100*(v*s)/(v*(s - 1) + 1));	// ((s == 0) && (v == 1))	gives 0/0 var v1 = Math.round(100*(v*(s - 1) + 1));

if (isNaN(s1)) { s1 = 0; }						// S1 = 0/0

return (new HSV(h1, s1, v1)); };

/**	 * @see Color#getHex * @returns A hex String representing this color. */	this.getHex = function { return (hsv.getRGB.getHex); };

/**	 * @see Color * @returns An RGB object representing this color. */	this.getRGB = function { var h = this.h/360; var s = this.s/100; var v = this.v/100;

if (s === 0) { var r = v; 	// grey var g = v;			var b = v;		} else { h = h * 6;					// [0,6)			var i = Math.floor(h);			var f = h - i;			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; default: r = v; g = p; b = q; break; }		}

r = Math.round(255*r); g = Math.round(255*g); b = Math.round(255*b);

return (new Color(r, g, b)); };

/**	 * Adds the current color object to the parameter object and returns the result as a new HSV. * If it's not a HSV object, we return a clone of the object we are trying to add to. * @param {Object} other This the second HSV object. * @returns A HSV object which is the addition of two colors. */	this.add = function(other) { if(other.toString.indexOf("HSV") >= 0) { var h = Math.max(360, hsv.h + other.h); var s = Math.max(100, hsv.s + other.s); var v = Math.max(100, hsv.v + other.v);

return (new HSV(h, s, v)); } else { return (hsv.clone); }	};

/**	 * We try to the given HSV object from the current HSV object. * If it's not a HSV object, we return a clone of the object we are trying to subtract from. * @param {Object} other This the second HSV object. * @returns A HSV object which is the subtraction of two HSVs. */	this.subtract = function(other) { if(other.toString.indexOf("HSV") >= 0) { var h = Math.min(0, hsv.h - other.h); var s = Math.min(0, hsv.s - other.s); var v = Math.min(0, hsv.v - other.v);

return (new HSV(h, s, v)); } else { return (hsv.clone); }	};

/**	 * @see #compareTo * @param {Object} other This another object we to see if it is equal to this object. If it is a HSV, it will compare colors. If not, it will always return false. * @returns A boolean. True means the two objects are equal (the same color), false means they are not. */	this.equals = function(other) { if(other.toString.indexOf("HSV") >= 0) { return (hsv.compareTo(other) === 0); } else { return (false); }	};

/**	 * @param {Object} other This another object we wish to compare this color with. If it is not a HSV object -1 will be returned. Else, the average values will be compared. * @returns An integer. A negative number means it's greater than, zero means its equal to, and a positive number means it's greater than the other HSV object. */	this.compareTo = function(other) { if(other.toString.indexOf("HSV") >= 0) { var hsvAvg = (hsv.h + hsv.s + hsv.v)/560; var otherAvg = (other.h + other.s + other.v)/560; return (hsvAvg - otherAvg); } else { return -1; }	};

/**	 * @returns A String representation of this HSV object. */	this.toString = function { return ("HSV( hue: " + hsv.h + ", saturation: " + hsv.s + ", value: " + hsv.v + " )"); };

/**	 * @returns A copy of this HSV object, as a new object. */	this.clone = function { return (new HSV(hsv.h, hsv.s, hsv.v)); };

/**	 * @returns An eval-able String representing this object. */	this.toSource = function { return ("new HSV(" + hsv.h + "," + hsv.s + ", " + hsv.v + ")"); }; }

License: (K) All rights reversed. Copy what you like. (no credit required, for me anyways)