SVG feComposite filter implementation in canvas - javascript

I'm working on a SVG filters implementation in canvas but i have two problems with the composite arithmetic operator.
My implementation for this operator is based on W3C specification and firefox9 source code.
Here my code for this filter & svg exemple : Fiddle complete code
var k1Scaled = options.k1 / 255;
var k4Scaled = options.k4 * 255;
for(v=0; v<datas.length; v+=4) {
r = datasB1[v+0];
g = datasB1[v+1];
b = datasB1[v+2];
a = datasB1[v+3];
r1 = datasB2[v+0];
g1 = datasB2[v+1];
b1 = datasB2[v+2];
a1 = datasB2[v+3];
//if (a === 0 && a1 === 0) continue;
vR = k1Scaled * r1 * r + options.k2 * r + options.k3 * r1 + k4Scaled;
vG = k1Scaled * g1 * g + options.k2 * g + options.k3 * g1 + k4Scaled;
vB = k1Scaled * b1 * b + options.k2 * b + options.k3 * b1 + k4Scaled;
vA = k1Scaled * a1 * a + options.k2 * a + options.k3 * a1 + k4Scaled;
datas[v+0] = Math.min(Math.max(0, ~~vR), 255);
datas[v+1] = Math.min(Math.max(0, ~~vG), 255);
datas[v+2] = Math.min(Math.max(0, ~~vB), 255);
datas[v+3] = Math.min(Math.max(0, ~~vA), 255);
}
My first problem : I have silver color on transparent pixel due to the formula, if i decompose the formula on transparent pixel, the result is :
result = k1*i1*i2 + k2*i1 + k3*i2 + k4
where k1 = 0.5 / 255 = 0.0019, i1 = 0, i2 =0, k2 = 0.5, k3 = 0.5, k4 = 0.5 * 255 = 127
result = 0.0019*0*0 + 0.5*0 + 0.5*0 + 127 = 127 => silver on rgba
My second problem : my result is more opaque than svg exemple, i suppose webbrowser pre-multiply the rgba result but i try this but no result.
I can't use svg filter applied on canvas, i need pure javascript/canvas implementation.
If you have an idea or solution thank in advance.
Kran.

Here the solution
this._arithmetic = function(options, ctx) {
var b1, ctxB1, ctxB2, w, h, x, y, v, imgDatas, datas, imgDatasB1, datasB1, imgDatasB2, datasB2;
b1 = document.createElement("canvas");
ctxB1 = b1.getContext("2d");
ctxB1.resize(this.width,this.height);
ctxB1.drawImage(options.in.source, options.in.options.x, options.in.options.y, options.in.options.width, options.in.options.height);
imgDatasB1 = ctxB1.getImageData(0,0,this.width,this.height);
this.premultiply(imgDatasB1);
datasB1 = imgDatasB1.data;
b2 = document.createElement("canvas");
ctxB2 = b2.getContext("2d");
ctxB2.resize(this.width,this.height);
ctxB2.drawImage(options.in2.source, options.in2.options.x, options.in2.options.y, options.in2.options.width, options.in2.options.height);
ctx.resize(this.width,this.height);
ctx.drawImage(b2, 0, 0);
imgDatas = ctx.getImageData(0,0,this.width,this.height);
this.premultiply(imgDatas);
datas = imgDatas.data;
var k1Scaled = options.k1 / 255;
var k4Scaled = options.k4 * 255;
var vR,vG,vB,vA,r,g,b,a,r1,g1,b1,a1;
for(v = 0;v < datas.length;v += 4) {
r = datasB1[v + 0];
g = datasB1[v + 1];
b = datasB1[v + 2];
a = datasB1[v + 3];
r1 = datas[v + 0];
g1 = datas[v + 1];
b1 = datas[v + 2];
a1 = datas[v + 3];
vR = k1Scaled * r1 * r + options.k2 * r + options.k3 * r1 + k4Scaled;
vG = k1Scaled * g1 * g + options.k2 * g + options.k3 * g1 + k4Scaled;
vB = k1Scaled * b1 * b + options.k2 * b + options.k3 * b1 + k4Scaled;
vA = k1Scaled * a1 * a + options.k2 * a + options.k3 * a1 + k4Scaled;
vR = vR < 0 ? 0 : vR > 255 ? 255 : vR;
vG = vG < 0 ? 0 : vG > 255 ? 255 : vG;
vB = vB < 0 ? 0 : vB > 255 ? 255 : vB;
vA = vA < 0 ? 0 : vA > 255 ? 255 : vA;
datas[v + 0] = vR;
datas[v + 1] = vG;
datas[v + 2] = vB;
datas[v + 3] = vA;
}
this.unPremultiply(imgDatas);
ctx.putImageData(imgDatas,0,0);
};
the code of premultiply/unPremultiply functions is in the fiddle link.

Related

JSON.stringify puts quotes around my float

My question probably has an easy answer but I can't seem to find a solution for my problem.
I have some code to do a put request to the phillips hue api and it requires:
{"xy":[0.300,0.300]} but JSON.stringify() returns {"xy":["0.300","0.300"]}
how can I get it to return the correct values?
Here is my code:
function AllRed() {
var url = 'http://192.168.168.124/api/husm18zj4JGeeHxoUKwDrCVfKRw6nicB25dLnYHX/groups/*/action';
var r = 255;
var g = 0;
var b = 0;
var model = 'LCT001';
var data = JSON.stringify({'xy': RGBtoXY(r, g, b, model)});//RGBtoXY(r, g, b, model)
$.put(url, data);
}
And here is the code for RGBtoXY which I got from another thread here on stackoverflow, unfortunately I can't find the specific thread anymore.
function XYPoint(x, y)
{
if (this instanceof XYPoint)
{
this.x = x;
this.y = y;
} else
{
return new XYPoint(x, y);
}
}
/**
* Get Color points according to light model
* #param model string Ex: LLC010
* #returns {Array}
*/
function colorPointsForModel(model)
{
var colorPoints = [];
if (model === 'LCT001')
{
colorPoints.push(XYPoint(0.500, 0.322));
colorPoints.push(XYPoint(0.4091, 0.518));
colorPoints.push(XYPoint(0.167, 0.04));
} else if (model === 'LLC006' || model === 'LLC007')
{
colorPoints.push(XYPoint(0.704, 0.296));
colorPoints.push(XYPoint(0.2151, 0.7106));
colorPoints.push(XYPoint(0.138, 0.08));
} else
{
// Default construct triangle wich contains all values
colorPoints.push(XYPoint(1.0, 0.0));
colorPoints.push(XYPoint(0.0, 1.0));
colorPoints.push(XYPoint(0.0, 0.0));
}
return colorPoints;
}
/**
* Method to see if the given XY value is within the reach of the lamps.
*
* #param p the point containing the X,Y value
* #param colorPoints color points array containing RGB XYPoints
* #return true if within reach, false otherwise.
*/
function checkPointInLampsReach(p, colorPoints)
{
var red = colorPoints[0];
var green = colorPoints[1];
var blue = colorPoints[2];
var v1 = XYPoint(green.x - red.x, green.y - red.y);
var v2 = XYPoint(blue.x - red.x, blue.y - red.y);
var q = XYPoint(p.x - red.x, p.y - red.y);
var s = crossProduct(q, v2) / crossProduct(v1, v2);
var t = crossProduct(v1, q) / crossProduct(v1, v2);
return ((s >= 0.0) && (t >= 0.0) && (s + t <= 1.0));
}
/**
* Is Not a number?
* Note: NaN is the only JavaScript value that is treated as unequal to itself
* #param val
* #returns {boolean}
*/
function isNaN(val)
{
return val !== val;
}
/**
* Calculates crossProduct of two 2D vectors / points.
*
* #param p1 first point used as vector
* #param p2 second point used as vector
* #return crossProduct of vectors
*/
function crossProduct(p1, p2)
{
return (p1.x * p2.y - p1.y * p2.x);
}
/**
* Converts RGB to XY and Brightness
* #param r integer 0-255
* #param g integer 0-255
* #param b integer 0-255
* #param model string
*/
function RGBtoXY(red, green, blue, model)
{
if (red > 1 || green > 1 || blue > 1)
{
red /= 255;
green /= 255;
blue /= 255;
}
red = (red > 0.04045) ? Math.pow((red + 0.055) / (1.0 + 0.055), 2.4) : (red / 12.92);
green = (green > 0.04045) ? Math.pow((green + 0.055) / (1.0 + 0.055), 2.4) : (green / 12.92);
blue = (blue > 0.04045) ? Math.pow((blue + 0.055) / (1.0 + 0.055), 2.4) : (blue / 12.92);
var X = red * 0.649926 + green * 0.103455 + blue * 0.197109;
var Y = red * 0.234327 + green * 0.743075 + blue * 0.022598;
var Z = red * 0.0000000 + green * 0.053077 + blue * 1.035763;
var cx = X / (X + Y + Z);
var cy = Y / (X + Y + Z);
if (isNaN(cx)) {
cx = 0.0;
}
if (isNaN(cy)) {
cy = 0.0;
}
//Check if the given XY value is within the colourreach of our lamps.
var xyPoint = XYPoint(cx, cy);
var colorPoints = colorPointsForModel(model);
var inReachOfLamps = checkPointInLampsReach(xyPoint, colorPoints);
if (!inReachOfLamps)
{
//It seems the colour is out of reach
//let's find the closest colour we can produce with our lamp and send this XY value out.
//Find the closest point on each line in the triangle.
var pAB = getClosestPointToPoints(colorPoints[cptRED], colorPoints[cptGREEN], xyPoint);
var pAC = getClosestPointToPoints(colorPoints[cptBLUE], colorPoints[cptRED], xyPoint);
var pBC = getClosestPointToPoints(colorPoints[cptGREEN], colorPoints[cptBLUE], xyPoint);
//Get the distances per point and see which point is closer to our Point.
var dAB = getDistanceBetweenTwoPoints(xyPoint, pAB);
var dAC = getDistanceBetweenTwoPoints(xyPoint, pAC);
var dBC = getDistanceBetweenTwoPoints(xyPoint, pBC);
var lowest = dAB;
var closestPoint = pAB;
if (dAC < lowest) {
lowest = dAC;
closestPoint = pAC;
}
if (dBC < lowest) {
lowest = dBC;
closestPoint = pBC;
}
//Change the xy value to a value which is within the reach of the lamp.
cx = closestPoint.x;
cy = closestPoint.y;
}
retval = [cx.toPrecision(3), cy.toPrecision(3)];
return retval;
}
/**
* Find the closest point on a line.
* This point will be within reach of the lamp.
*
* #param A the point where the line starts
* #param B the point where the line ends
* #param P the point which is close to a line.
* #return the point which is on the line.
*/
function getClosestPointToPoints(A, B, P)
{
var AP = XYPoint(P.x - A.x, P.y - A.y);
var AB = XYPoint(B.x - A.x, B.y - A.y);
var ab2 = AB.x * AB.x + AB.y * AB.y;
var ap_ab = AP.x * AB.x + AP.y * AB.y;
var t = ap_ab / ab2;
if (t < 0.0) {
t = 0.0;
} else if (t > 1.0) {
t = 1.0;
}
return XYPoint(A.x + AB.x * t, A.y + AB.y * t);
}
/**
* Find the distance between two points.
*
* #param one
* #param two
* #return the distance between point one and two
*/
// + (float)getDistanceBetweenTwoPoints:(CGPoint)one point2:(CGPoint)two {
function getDistanceBetweenTwoPoints(one, two)
{
var dx = one.x - two.x; // horizontal difference
var dy = one.y - two.y; // vertical difference
return Math.sqrt(dx * dx + dy * dy);
}
function XYtoRGB(x, y, brightness, model)
{
var xy = XYPoint(x, y);
var colorPoints = colorPointsForModel(model);
var inReachOfLamps = checkPointInLampsReach(xy, colorPoints);
console.log('inReachOfLamps', inReachOfLamps);
if (!inReachOfLamps) {
//It seems the colour is out of reach
//let's find the closest colour we can produce with our lamp and send this XY value out.
//Find the closest point on each line in the triangle.
var pAB = getClosestPointToPoints(colorPoints[cptRED], colorPoints[cptGREEN], xy);
var pAC = getClosestPointToPoints(colorPoints[cptBLUE], colorPoints[cptRED], xy);
var pBC = getClosestPointToPoints(colorPoints[cptGREEN], colorPoints[cptBLUE], xy);
//Get the distances per point and see which point is closer to our Point.
var dAB = getDistanceBetweenTwoPoints(xy, pAB);
var dAC = getDistanceBetweenTwoPoints(xy, pAC);
var dBC = getDistanceBetweenTwoPoints(xy, pBC);
var lowest = dAB;
var closestPoint = pAB;
if (dAC < lowest) {
lowest = dAC;
closestPoint = pAC;
}
if (dBC < lowest) {
lowest = dBC;
closestPoint = pBC;
}
//Change the xy value to a value which is within the reach of the lamp.
xy.x = closestPoint.x;
xy.y = closestPoint.y;
}
var x = xy.x;
var y = xy.y;
var z = 1.0 - x - y;
var Y = brightness;
var X = (Y / y) * x;
var Z = (Y / y) * z;
var r = X * 3.2410 - Y * 1.5374 - Z * 0.4986;
var g = -X * 0.9692 + Y * 1.8760 + Z * 0.0416;
var b = X * 0.0556 - Y * 0.2040 + Z * 1.0570;
r = r <= 0.0031308 ? 12.92 * r : (1.0 + 0.055) * Math.pow(r, (1.0 / 2.4)) - 0.055;
g = g <= 0.0031308 ? 12.92 * g : (1.0 + 0.055) * Math.pow(g, (1.0 / 2.4)) - 0.055;
b = b <= 0.0031308 ? 12.92 * b : (1.0 + 0.055) * Math.pow(b, (1.0 / 2.4)) - 0.055;
if (r < 0)
{
r = 0;
}
if (g < 0)
{
g = 0;
}
if (b < 0)
{
b = 0;
}
if (r > 1 || g > 1 || b > 1)
{
var max = Math.max(r, g, b);
r /= max;
g /= max;
b /= max;
}
r *= 255;
g *= 255;
b *= 255;
r = Math.round(r);
g = Math.round(g);
b = Math.round(b);
return {
r: r,
g: g,
b: b
};
}
Your "problem" is this line
retval = [cx.toPrecision(3), cy.toPrecision(3)];
As already noted in comments toPrecision() returns a string, which when serialized will result in the ".
So either drop the toPrecision() calls here, or convert to a number again later:
var xy = RGBtoXY(r, g, b, model);
// convert to numbers again
xy = xy.map( Number );
var data = JSON.stringify({'xy': xy });//RGBtoXY(r, g, b, model)

How do I make image face mouse in javascript?

I'm trying to use javascript to make an image face the mouse of the user, I'm close but my calculations are a bit off (the rotation on the x-axis seems to be inverted). I based my code of this link: rotate3d shorthand
javascript:
$(document).mousemove(function(e){
pageheight = $(document).height();
pagewidth = $(document).width();
widthpercentage = e.pageX / pagewidth;
heightpercentage = e.pageY / pageheight;
specialW = (widthpercentage * 180 - 90);
specialH = (heightpercentage * 180 - 90);
function toRadians(degrees) {
radians = degrees * (Math.PI /180);
return radians;
}
function matrix(x, y, z, rads) {
var sc = Math.sin(rads / 2) * Math.cos(rads / 2);
var sq = Math.pow(Math.sin(rads / 2), 2);
var array = [];
var a1 = 1 - 2 * (Math.pow(y, 2) + Math.pow(z, 2)) * sq,
a2 = 2 * (x * y * sq - z * sc),
a3 = 2 * (x * z * sq + y * sc),
a4 = 0,
b1 = 2 * (x * y * sq + z * sc),
b2 = 1 - 2 * (Math.pow(x, 2) + Math.pow(z, 2)) * sq,
b3 = 2 * (y * z * sq - x * sc),
b4 = 0,
c1 = 2 * (x * z * sq - y * sc),
c2 = 2 * (y * z * sq + x * sc),
c3 = 1 - 2 * (Math.pow(x, 2) + Math.pow(y, 2)) * sq,
c4 = 0,
d1 = 0,
d2 = 0,
d3 = 0,
d4 = 1;
array.push(a1, b1, c1, d1, a2, b2, c2, d2, a3, b3, c3, d3, a4, b4, c4, d4);
return array;
}
xmatrix = matrix(1, 0, 0, toRadians(specialH));
ymatrix = matrix(0, 1, 0, toRadians(specialW));
function multiply(xarray, yarray) {
var newarray = [];
var a1 = (xarray[0] * yarray[0]) + (xarray[4] * yarray[1]) + (xarray[8] * yarray[2]) + (xarray[12] * yarray[3]),
a2 = (xarray[0] * yarray[4]) + (xarray[4] * yarray[5]) + (xarray[8] * yarray[6]) + (xarray[12] * yarray[7]),
a3 = (xarray[0] * yarray[8]) + (xarray[4] * yarray[9]) + (xarray[8] * yarray[10]) + (xarray[12] * yarray[11]),
a4 = (xarray[0] * yarray[12]) + (xarray[4] * yarray[13]) + (xarray[8] * yarray[14]) + (xarray[12] * yarray[15]),
b1 = (xarray[1] * yarray[0]) + (xarray[5] * yarray[1]) + (xarray[9] * yarray[2]) + (xarray[13] * yarray[3]),
b2 = (xarray[1] * yarray[4]) + (xarray[5] * yarray[5]) + (xarray[9] * yarray[6]) + (xarray[13] * yarray[7]),
b3 = (xarray[1] * yarray[8]) + (xarray[5] * yarray[9]) + (xarray[9] * yarray[10]) + (xarray[13] * yarray[11]),
b4 = (xarray[1] * yarray[12]) + (xarray[5] * yarray[13]) + (xarray[9] * yarray[14]) + (xarray[13] * yarray[15]),
c1 = (xarray[2] * yarray[0]) + (xarray[6] * yarray[1]) + (xarray[10] * yarray[2]) + (xarray[14] * yarray[3]),
c2 = (xarray[2] * yarray[4]) + (xarray[6] * yarray[5]) + (xarray[10] * yarray[6]) + (xarray[14] * yarray[7]),
c3 = (xarray[2] * yarray[8]) + (xarray[6] * yarray[9]) + (xarray[10] * yarray[10]) + (xarray[14] * yarray[11]),
c4 = (xarray[2] * yarray[12]) + (xarray[6] * yarray[13]) + (xarray[10] * yarray[14]) + (xarray[14] * yarray[15]),
d1 = (xarray[3] * yarray[0]) + (xarray[7] * yarray[1]) + (xarray[11] * yarray[2]) + (xarray[15] * yarray[3]),
d2 = (xarray[3] * yarray[4]) + (xarray[7] * yarray[5]) + (xarray[11] * yarray[6]) + (xarray[15] * yarray[7]),
d3 = (xarray[3] * yarray[8]) + (xarray[7] * yarray[9]) + (xarray[11] * yarray[10]) + (xarray[15] * yarray[11]),
d4 = (xarray[3] * yarray[12]) + (xarray[7] * yarray[13]) + (xarray[11] * yarray[14]) + (xarray[15] * yarray[15]);
newarray.push(a1, b1, c1, d1, a2, b2, c2, d2, a3, b3, c3, d3, a4, b4, c4, d4);
return newarray;
}
var newmatrix = multiply(xmatrix, ymatrix);
$('#page1 img').css('transform', 'matrix3d(' + newmatrix[0] + ',' + newmatrix[1] + ',' + newmatrix[2] + ',' + newmatrix[3] + ',' + newmatrix[4] + ',' + newmatrix[5] + ',' + newmatrix[6] + ',' + newmatrix[7] + ',' + newmatrix[8] + ',' + newmatrix[9] + ',' + newmatrix[10] + ',' + newmatrix[11] + ',' + newmatrix[12] + ',' + newmatrix[13] + ',' + newmatrix[14] + ',' + newmatrix[15] + ')');
});
I know it's a lot of code, but if I could get a second a opinion, It would be greatly appreciated. Thanks
No plugins please.
I actually just edited the matrix() function and added this right before array.push:
if ( y = 1 ) {
a3 = a3 * -1;
c1 = c1 * -1;
}
And this fixed the issue.

Canvas change image background color but keep it's "effects"

I have an image where I need to change it's background color, but keep the "effects" on it (on the image the black dots, white lines etc.)
Here's the orginal image:
I managed to change the color, but also I keep removing those "effects". Preview:
Here's the code:
//let's say I want it to be red
var r = 255;
var g = 0;
var b = 0;
var imgElement = document.getElementById('img');
var canvas = document.getElementById('canvas');
canvas.width = imgElement.width;
canvas.height = imgElement.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(imgElement, 0, 0);
var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data = imageData.data;
for (var i = 0; i < data.length; i += 4) {
if (data[i + 3] !== 0) {
data[i] = r;
data[i + 1] = g;
data[i + 2] = b;
data[i + 3] = data[i + 3];
}
}
ctx.putImageData(imageData, 0, 0);
<img src="foo" id="img" />
<canvas id="canvas"></canvas>
How to prevent that?
For modern browsers except Internet Explorer, you can use compositing to change the hue of your original image while leaving the saturation & lightness unchanged. This will "recolor" your original image while leaving the contours intact.
Example code that works in modern browsers except Internet Explorer
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/M449a.png";
function start(){
// create an overlay with solid #00d9c6 color
var tempCanvas=document.createElement('canvas');
var tempctx=tempCanvas.getContext('2d');
canvas.width=tempCanvas.width=img.width;
canvas.height=tempCanvas.height=img.height;
tempctx.drawImage(img,0,0);
tempctx.globalCompositeOperation='source-atop';
tempctx.fillStyle='#00d9c6';
tempctx.fillRect(0,0,tempCanvas.width,tempCanvas.height);
//
canvas.width=img.width;
canvas.height=img.height;
// use compositing to change the hue of the original image
ctx.drawImage(img,0,0);
ctx.globalCompositeOperation='hue';
ctx.drawImage(tempCanvas,0,0);
// always clean up: reset compositing to its default
ctx.globalCompositeOperation='source-over';
}
#canvas{border:1px solid red; }
<canvas id="canvas" width=300 height=300></canvas>
Since Internet Explorer does not support Blend Compositing, you will have to do it manually.
Read the RGBA value of each pixel.
Convert that RGBA value to HSL.
Shift the hue value (the "H" in HSL) by the difference between your blue hue and your desired green hue.
Convert the hue-shifted HSL value to RGBA value.
Write the hue-shifted RGBA value back to the pixel.
Here's example code of manually shifting the hue:
Important note: This manual method works by manipulating pixels with .getImageData. Therefore you must make sure the original image is hosted on the same domain as the webpage. Otherwise, the canvas will become tainted for security reasons and you will not be able to use .getImageData.
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var img = new Image();
img.crossOrigin = "anonymous";
img.onload = start;
img.src = "https://dl.dropboxusercontent.com/u/139992952/multple/marioStanding.png";
function start() {
ctx.drawImage(img, 0, 0);
ctx.drawImage(img, 150, 0);
// shift blueish colors to greenish colors
recolorPants(-.33);
}
function recolorPants(colorshift) {
var imgData = ctx.getImageData(150, 0, canvas.width, canvas.height);
var data = imgData.data;
for (var i = 0; i < data.length; i += 4) {
red = data[i + 0];
green = data[i + 1];
blue = data[i + 2];
alpha = data[i + 3];
// skip transparent/semiTransparent pixels
if (alpha < 200) {
continue;
}
var hsl = rgbToHsl(red, green, blue);
var hue = hsl.h * 360;
// change blueish pixels to the new color
if (hue > 200 && hue < 300) {
var newRgb = hslToRgb(hsl.h + colorshift, hsl.s, hsl.l);
data[i + 0] = newRgb.r;
data[i + 1] = newRgb.g;
data[i + 2] = newRgb.b;
data[i + 3] = 255;
}
}
ctx.putImageData(imgData, 150, 0);
}
function rgbToHsl(r, g, b) {
r /= 255, g /= 255, b /= 255;
var max = Math.max(r, g, b),
min = Math.min(r, g, b);
var h, s, l = (max + min) / 2;
if (max == min) {
h = s = 0; // achromatic
} else {
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
return ({
h: h,
s: s,
l: l,
});
}
function hslToRgb(h, s, l) {
var r, g, b;
if (s == 0) {
r = g = b = l; // achromatic
} else {
function hue2rgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
}
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
return ({
r: Math.round(r * 255),
g: Math.round(g * 255),
b: Math.round(b * 255),
});
}
<p>Example shifting color Hue with .getImageData</p>
<p>(Original: left, Recolored: right)</p>
<canvas id="canvas" width=300 height=300></canvas>
You need to convert each pixel to the LSH colour space (lightness/luminance, hue, saturation). Then you set the Hue to the colour you want and keep the calculated luminance and saturation, then convert back to RGB and set the imageData to the new RGB value.
I have added my own code for conversions. There may be faster versions out there.
// returns RGB in an array on 3 numbers 0-255
var lshToRGB = function(ll,ss,hh){ //ll 0-255,ss 0-255, hh 0-360
var l = ll/255;
var s = ss/255;
var hhh = (hh/255)*360;
var C = (1 - Math.abs(2*l - 1)) * s;
var X = C*(1 - Math.abs(((hhh / 60)%2) - 1));
var m = l - C/2;
if(hhh < 60){
var r = C;
var g = X;
var b = 0;
}else
if(hhh < 120){
var r = X;
var g = C;
var b = 0;
}else
if(hhh < 180){
var r = 0;
var g = C;
var b = X;
}else
if(hhh < 240){
var r = 0;
var g = X;
var b = C;
}else
if(hhh < 300){
var r = X;
var g = 0;
var b = C;
}else{
var r = C;
var g = 0;
var b = X;
}
r += m;
g += m;
b += m;
// is there a need to clamp these ????)
r = Math.round(Math.min(255,Math.max(0,r*255)));
g = Math.round(Math.min(255,Math.max(0,g*255)));
b = Math.round(Math.min(255,Math.max(0,b*255)));
return [r,g,b];
}
// returns array of 3 numbers 0-255,0-255,0-360
var rgbToLSH = function(rr,gg,bb){ // could do without the conversion from 360 to 255 on hue
var r,
g,
b,
h,
s,
l,
min,
max,
d;
r = rr / 255;
g = gg / 255;
b = bb / 255;
max = Math.max(r, g, b);
min = Math.min(r, g, b);
l = (max + min) / 2;
if (max == min) {
h = 0;
s = 0; // achromatic
} else {
d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r:
h = (g - b) / d;
break;
case g:
h = 2 + ((b - r) / d);
break;
case b:
h = 4 + ((r - g) / d);
break;
}
h *= 60;
if (h < 0) {
h += 360;
}
h = Math.round(h);
}
return [
Math.min(Math.round(l*255),255),
Math.min(Math.round(s*255),255),
Math.min(Math.round((h/360)*255),255)
];
}

c.beginPath not a function?? it was a minute ago

i am getting c.beginPath is not a function, while i was working on this code earlier it was but after some small changes it is not longer a recognized function, anyone know why this is?
var canvas=document.getElementById('canvas');
var c = canvas.getContext('2d');
var x = 0,
y = 0,
a = 0,
b = 0,
c = 0,
d = 0,
e = 0,
f = 0;
setInterval(function(){
x = 0 * x + 0 * y + 50;
y = 0 * x + 16 * y + 0;
a = -15 * a + 26 * b + 57.5;
b = 28.3 * a + 23.7 * b - 8.4;
c = 19.7 * c + 22.6 + d + 40;
d = -22.6 * c + 19.7 * d + 4.9;
e = 84.9 * e - 3.7 * f + 7.5;
f = 3.7 * e + 84.9 * f + 18.3;
c.beginPath();
c.strokeStyle = "green";
c.lineWidth = .5;
c.moveTo(50.05, 0);
c.lineTo(x, y);
c.moveTo(51.7, 6.6);
c.lineTo(a, b);
c.moveTo(44.55, 18.7);
c.lineTo(c, d);
c.moveTo(74.8, 102.85);
c.lineTo(e, f);
c.stroke();
console.log(x);
}, 50)
var c = canvas.getContext('2d');
:
var ... c = 0. ...;
:
c = 19.7 * c + 22.6 + d + 40;
It may have once been a function but that middle line above put paid to that idea :-)
A classic argument, if any were needed, that names of objects in code should be meaningful, the only exception being very localised copies of i, of course. As a C coder, you'll have to prise that one from my cold, dead hands.

JavaScript Calculate brighter colour

I have a colour value in JS as a string
#ff0000
How would I go about programatically calculating a brighter/lighter version of this colour, for example #ff4848, and be able to calculate the brightness via a percentage, e.g.
increase_brightness('#ff0000', 50); // would make it 50% brighter
function increase_brightness(hex, percent){
// strip the leading # if it's there
hex = hex.replace(/^\s*#|\s*$/g, '');
// convert 3 char codes --> 6, e.g. `E0F` --> `EE00FF`
if(hex.length == 3){
hex = hex.replace(/(.)/g, '$1$1');
}
var r = parseInt(hex.substr(0, 2), 16),
g = parseInt(hex.substr(2, 2), 16),
b = parseInt(hex.substr(4, 2), 16);
return '#' +
((0|(1<<8) + r + (256 - r) * percent / 100).toString(16)).substr(1) +
((0|(1<<8) + g + (256 - g) * percent / 100).toString(16)).substr(1) +
((0|(1<<8) + b + (256 - b) * percent / 100).toString(16)).substr(1);
}
/**
* ('#000000', 50) --> #808080
* ('#EEEEEE', 25) --> #F2F2F2
* ('EEE , 25) --> #F2F2F2
**/
Update
#zyklus's answer is simpler and has the same effect. Please refer to this answer only if you are interested in converting between RGB and HSL.
To set the brightness of RGB:
Convert RGB to HSL
Set the brightness of HSL
Convert back from HSL to RGB
This link used to have code to convert RGB to HSL and reverse:
http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
/**
* Converts an RGB color value to HSL. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes r, g, and b are contained in the set [0, 255] and
* returns h, s, and l in the set [0, 1].
*
* #param Number r The red color value
* #param Number g The green color value
* #param Number b The blue color value
* #return Array The HSL representation
*/
function rgbToHsl(r, g, b){
r /= 255, g /= 255, b /= 255;
var max = Math.max(r, g, b), min = Math.min(r, g, b);
var h, s, l = (max + min) / 2;
if(max == min){
h = s = 0; // achromatic
}else{
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch(max){
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return [h, s, l];
}
/**
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes h, s, and l are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255].
*
* #param Number h The hue
* #param Number s The saturation
* #param Number l The lightness
* #return Array The RGB representation
*/
function hslToRgb(h, s, l){
var r, g, b;
if(s == 0){
r = g = b = l; // achromatic
}else{
function hue2rgb(p, q, t){
if(t < 0) t += 1;
if(t > 1) t -= 1;
if(t < 1/6) return p + (q - p) * 6 * t;
if(t < 1/2) return q;
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
}
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return [r * 255, g * 255, b * 255];
}
I made some example with it. Check this link: http://jsfiddle.net/sangdol/euSLy/4/
And this is the increase_brightness() function:
function increase_brightness(rgbcode, percent) {
var r = parseInt(rgbcode.slice(1, 3), 16),
g = parseInt(rgbcode.slice(3, 5), 16),
b = parseInt(rgbcode.slice(5, 7), 16),
HSL = rgbToHsl(r, g, b),
newBrightness = HSL[2] + HSL[2] * (percent / 100),
RGB;
RGB = hslToRgb(HSL[0], HSL[1], newBrightness);
rgbcode = '#'
+ convertToTwoDigitHexCodeFromDecimal(RGB[0])
+ convertToTwoDigitHexCodeFromDecimal(RGB[1])
+ convertToTwoDigitHexCodeFromDecimal(RGB[2]);
return rgbcode;
}
function convertToTwoDigitHexCodeFromDecimal(decimal){
var code = Math.round(decimal).toString(16);
(code.length > 1) || (code = '0' + code);
return code;
}
You can pass a negative value as a percent argument to make it darken.
In case anyone needs it, I converted the color brightness JavaScript code to ASP / VBScript for a project and thought I would share it with you:
'::Color Brightness (0-100)
'ex. ColorBrightness("#FF0000",25) 'Darker
'ex. ColorBrightness("#FF0000",50) 'Mid
'ex. ColorBrightness("#FF0000",75) 'Lighter
Function ColorBrightness(strRGB,intBrite)
strRGB = Replace(strRGB,"#","")
r = CInt("&h" & Mid(strRGB,1,2))
g = CInt("&h" & Mid(strRGB,3,2))
b = CInt("&h" & Mid(strRGB,5,2))
arrHSL = RGBtoHSL(r, g, b)
dblOrigBrite = CDbl(arrHSL(2) * 100)
arrRGB = HSLtoRGB(arrHSL(0), arrHSL(1), intBrite/100)
newRGB = "#" & HEXtoDEC(arrRGB(0)) & HEXtoDEC(arrRGB(1)) & HEXtoDEC(arrRGB(2))
ColorBrightness = newRGB
End Function
'::RGB to HSL Function
Function RGBtoHSL(r,g,b)
r = CDbl(r/255)
g = CDbl(g/255)
b = CDbl(b/255)
max = CDbl(MaxCalc(r & "," & g & "," & b))
min = CDbl(MinCalc(r & "," & g & "," & b))
h = CDbl((max + min) / 2)
s = CDbl((max + min) / 2)
l = CDbl((max + min) / 2)
If max = min Then
h = 0
s = 0
Else
d = max - min
s = IIf(l > 0.5, d / (2 - max - min), d / (max + min))
Select Case CStr(max)
Case CStr(r)
h = (g - b) / d + (IIf(g < b, 6, 0))
Case CStr(g)
h = (b - r) / d + 2
Case CStr(b)
h = (r - g) / d + 4
End Select
h = h / 6
End If
RGBtoHSL = Split(h & "," & s & "," & l, ",")
End Function
'::HSL to RGB Function
Function HSLtoRGB(h,s,l)
If s = 0 Then
r = l
g = l
b = l
Else
q = IIf(l < 0.5, l * (1 + s), l + s - l * s)
p = 2 * l - q
r = HUEtoRGB(p, q, h + 1/3)
g = HUEtoRGB(p, q, h)
b = HUEtoRGB(p, q, h - 1/3)
End If
HSLtoRGB = Split(r * 255 & "," & g * 255 & "," & b * 255, ",")
End Function
'::Hue to RGB Function
Function HUEtoRGB(p,q,t)
If CDbl(t) < 0 Then t = t + 1
If CDbl(t) > 1 Then t = t - 1
If CDbl(t) < (1/6) Then
HUEtoRGB = p + (q - p) * 6 * t
Exit Function
End If
If CDbl(t) < (1/2) Then
HUEtoRGB = q
Exit Function
End If
If CDbl(t) < (2/3) Then
HUEtoRGB = p + (q - p) * (2/3 - t) * 6
Exit Function
End If
HUEtoRGB = p
End Function
'::Hex to Decimal Function
Function HEXtoDEC(d)
h = Hex(Round(d,0))
h = Right(String(2,"0") & h,2)
HEXtoDEC = h
End Function
'::Max Function
Function MaxCalc(valList)
valList = Split(valList,",")
b = 0
For v = 0 To UBound(valList)
a = valList(v)
If CDbl(a) > CDbl(b) Then b = a
Next
MaxCalc = b
End Function
'::Min Function
Function MinCalc(valList)
valList = Split(valList,",")
For v = 0 To UBound(valList)
a = valList(v)
If b = "" Then b = a
If CDbl(a) < CDbl(b) AND b <> "" Then b = a
Next
MinCalc = b
End Function
'::IIf Emulation Function
Function IIf(condition,conTrue,conFalse)
If (condition) Then
IIf = conTrue
Else
IIf = conFalse
End If
End Function
That way you won't need any conversion of the source color.
check out this fiddle : https://jsfiddle.net/4c47otou/
increase_brightness = function(color,percent){
var ctx = document.createElement('canvas').getContext('2d');
ctx.fillStyle = color;
ctx.fillRect(0,0,1,1);
var color = ctx.getImageData(0,0,1,1);
var r = color.data[0] + Math.floor( percent / 100 * 255 );
var g = color.data[1] + Math.floor( percent / 100 * 255 );
var b = color.data[2] + Math.floor( percent / 100 * 255 );
return 'rgb('+r+','+g+','+b+')';
}
Example usage :
increase_brightness('#0000ff',20);
increase_brightness('khaki',20);
increase_brightness('rgb(12, 7, 54)',20);
// color is a hex color like #aaaaaa and percent is a float, 1.00=100%
// increasing a color by 50% means a percent value of 1.5
function brighten(color, percent) {
var r=parseInt(color.substr(1,2),16);
var g=parseInt(color.substr(3,2),16);
var b=parseInt(color.substr(5,2),16);
return '#'+
Math.min(255,Math.floor(r*percent)).toString(16)+
Math.min(255,Math.floor(g*percent)).toString(16)+
Math.min(255,Math.floor(b*percent)).toString(16);
}
Live sample: http://jsfiddle.net/emM55/
Here is the increaseBrightness function with the RGB->HSL->RGB method. "amount" should be in percent.
HSL<->RGB conversion functions taken from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
function increaseBrightness( color, amount ) {
var r = parseInt(color.substr(1, 2), 16);
var g = parseInt(color.substr(3, 2), 16);
var b = parseInt(color.substr(5, 2), 16);
hsl = rgbToHsl( r, g, b );
hsl.l += hsl.l + (amount / 100);
if( hsl.l > 1 ) hsl.l = 1;
rgb = hslToRgb( hsl.h, hsl.s, hsl.l );
var v = rgb.b | (rgb.g << 8) | (rgb.r << 16);
return '#' + v.toString(16);
}
function rgbToHsl(r, g, b){
r /= 255, g /= 255, b /= 255;
var max = Math.max(r, g, b), min = Math.min(r, g, b);
var h, s, l = (max + min) / 2;
if(max == min){
h = s = 0; // achromatic
}else{
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch(max){
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return {'h':h, 's':s, 'l':l};
}
function hslToRgb(h, s, l){
var r, g, b;
if(s == 0){
r = g = b = l; // achromatic
}else{
function hue2rgb(p, q, t){
if(t < 0) t += 1;
if(t > 1) t -= 1;
if(t < 1/6) return p + (q - p) * 6 * t;
if(t < 1/2) return q;
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
}
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return { 'r':r * 255, 'g':g * 255, 'b':b * 255 };
}
I found a variation of Sanghyun Lee's reply generates the best result.
Convert RGB to HSL
Set the brightness of HSL
Convert back from HSLto RGB
The difference/variation is how you increase/decrease the brightness.
newBrightness = HSL[2] + HSL[2] * (percent / 100) // Original code
Instead of applying a percentage on the current brightness, it works better if it is treated as absolute increment/decrement. Since the luminosity range is 0 to 1, the percent can be applied on the whole range (1 - 0) * percent/100.
newBrightness = HSL[2] + (percent / 100);
newBrightness = Math.max(0, Math.min(1, newBrightness));
Another nice property of this approach is increment & decrement negate each other.
Image below shows darker and lighter colors with 5% increment. Note, how the palette is reasonably smooth and often ends with black and white.
Palette with original approach - gets stuck at certain colors.
I know this an old question, but I found no answer that simply manipulates css hsl color. I found the old answers here to be too complex and slow, even producing poor results, so a different approach seems warranted. The following alternative is much more performant and less complex.
Of course, this answer requires you to use hsl colors throughout your app, otherwise you still have to do a bunch of conversions! Though, if you need to manipulate brightness eg in a game loop, you should be using hsl values anyway as they are much better suited for programmatic manipulation. The only drawback with hsl from rgb as far as I can tell, is that it's harder to "read" what hue you're seeing like you can with rgb strings.
function toHslArray(hslCss) {
let sep = hslCss.indexOf(",") > -1 ? "," : " "
return hslCss.substr(4).split(")")[0].split(sep)
}
function adjustHslBrightness(color, percent) {
let hsl = toHslArray(color)
return "hsl(" + hsl[0] + "," + hsl[1] + ", " + (percent + "%") + ")"
}
let hsl = "hsl(200, 40%, 40%)"
let hsl2 = adjustHslBrightness(hsl, 80)
function brighten(color, c) {
const calc = (sub1,sub2)=> Math.min(255,Math.floor(parseInt(color.substr(sub1,sub2),16)*c)).toString(16).padStart(2,"0")
return `#${calc(1,2)}${calc(3,2)}${calc(5,2)}`
}
const res = brighten("#23DA4C", .5) // "#116d26"
console.log(res)
What I use:
//hex can be string or number
//rate: 1 keeps the color same. < 1 darken. > 1 lighten.
//to_string: set to true if you want the return value in string
function change_brightness(hex, rate, to_string = false) {
if (typeof hex === 'string') {
hex = hex.replace(/^\s*#|\s*$/g, '');
} else {
hex = hex.toString(16);
}
if (hex.length == 3) {
hex = hex.replace(/(.)/g, '$1$1');
} else {
hex = ("000000" + hex).slice(-6);
}
let r = parseInt(hex.substr(0, 2), 16);
let g = parseInt(hex.substr(2, 2), 16);
let b = parseInt(hex.substr(4, 2), 16);
let h, s, v;
[h, s, v] = rgb2hsv(r, g, b);
v = parseInt(v * rate);
[r, g, b] = hsv2rgb(h, s, v);
hex = ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
if (to_string) return "#" + hex;
return parseInt(hex, 16);
}
function rgb2hsv(r,g,b) {
let v = Math.max(r,g,b), n = v-Math.min(r,g,b);
let h = n && ((v === r) ? (g-b)/n : ((v === g) ? 2+(b-r)/n : 4+(r-g)/n));
return [60*(h<0?h+6:h), v&&n/v, v];
}
function hsv2rgb(h,s,v) {
let f = (n,k=(n+h/60)%6) => v - v*s*Math.max( Math.min(k,4-k,1), 0);
return [f(5),f(3),f(1)];
}
A variant with lodash:
// color('#EBEDF0', 30)
color(hex, percent) {
return '#' + _(hex.replace('#', '')).chunk(2)
.map(v => parseInt(v.join(''), 16))
.map(v => ((0 | (1 << 8) + v + (256 - v) * percent / 100).toString(16))
.substr(1)).join('');
}
First get a quick understanding of hex color codes.
Then it should be pretty easy to break down your color value into RGB, make the adjustments and then return the new color code.

Categories