ocanvas sprite hue change - javascript

I'm using ocanvas to design a game and I'm wondering if there is some way to change the hue of a sprite. If not, is there a way to integrate an html5 canvas way of changing the hue of a sprite to ocanvas?

I don't have information about ocanvas, but here's how to change the hue of a sprite drawn onto an html5 canvas.
This method uses context.getImageData to fetch the color data of every pixel on the canvas. Then any pixel with a blue-ish hue is changed to a green-ish hue.
Note: If your sprites have more discrete coloring (f.ex: the sprite has a specific blue color that you wish to change to a specific green color) then you won't need to convert to-and-from the HSL color format.
If necessary, you can convert this recolored html5 canvas into a sprite-image to include in ocanvas using .toDataURL.
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<230){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),
});
}
body{ background-color: ivory; }
canvas{border:1px solid red;}
<p>Example shifting color Hue with .getImageData</p>
<p>(Original: left, Recolored: right)</p>
<canvas id="canvas" width=300 height=300></canvas>

Another popular solution is to use grayscale images and color them in JS via globalCompositeOperation in canvas. Described in detail here: http://buildnewgames.com/global-composit-operations/#colored-sprite-masks-with-codesource-atopcode
I put together an example of how it could be done in conjunction with oCanvas: http://jsfiddle.net/g0tj7vrv/
HTML:
<script src="https://cdnjs.cloudflare.com/ajax/libs/ocanvas/2.7.4/ocanvas.min.js"></script>
<canvas id="canvas" width="400" height="400"></canvas>
JS:
var canvas = oCanvas.create({
canvas: '#canvas',
background: '#000'
});
canvas.display.register('colorizedImage', {
hue: '',
path: '',
width: 0,
heigth: 0,
_renderNewColor: function(tempImage) {
if (!this._tempCanvas) {
this._tempCanvas = document.createElement('canvas');
}
this._createColorizedImage(this._tempCanvas, tempImage, this.hue);
var self = this;
setTimeout(function() {
self.core.redraw();
}, 0);
},
_createColorizedImage: function(tempCanvas, imageElement, hue) {
var tempContext = tempCanvas.getContext('2d');
tempCanvas.width = imageElement.width;
tempCanvas.height = imageElement.height;
tempContext.drawImage(imageElement, 0, 0);
tempContext.fillStyle = 'hsla(' + hue + ', 50%, 50%, 0.5)';
tempContext.globalCompositeOperation = 'source-atop';
tempContext.fillRect(0, 0, tempCanvas.width, tempCanvas.height);
}
}, function(context) {
if (this._tempCanvas) {
var origin = this.getOrigin();
var x = this.abs_x - origin.x;
var y = this.abs_y - origin.y;
var w = this.width || this._tempCanvas.width;
var h = this.height || this._tempCanvas.height;
context.drawImage(this._tempCanvas, x, y, w, h);
}
if (this.path !== this._lastPathBeingLoaded) {
this._lastPathBeingLoaded = this.path;
var tempImage = new Image();
tempImage.src = this.path;
var self = this;
tempImage.onload = function() {
if (self.path === this.src) {
self._renderNewColor(tempImage);
}
};
this._lastImageElement = tempImage;
}
if (this.hue !== this._lastHueBeingLoaded) {
this._lastHueBeingLoaded = this.hue;
this._renderNewColor(this._lastImageElement);
}
});
var colorizedImage = canvas.display.colorizedImage({
hue: 0,
path: 'https://dl.dropboxusercontent.com/u/2645586/gco/cobra-primary.png',
origin: {x: -60, y: 0},
width: 120,
height: 120,
x: canvas.width / 2,
y: canvas.height / 2
});
canvas.addChild(colorizedImage);
canvas.setLoop(function() {
colorizedImage.hue = (colorizedImage.hue + 10) % 360;
colorizedImage.rotation -= 2;
}).start();

Related

Javascript canvas color not smooth change in brightness

Full demo of what I'm talking about is here:
https://ggodfrey.github.io/ComplexColor/index.html
I'm trying to make a website that can attempt to plot a complex-valued function using color. The hue is determined using the angle of the complex answer, and the brightness is determined by taking the log of the magnitude of the complex answer and then finding the fractional part. From there, I use a function to convert HSL to RGB, then putting that into an Image object that I draw onto the canvas, allowing me to draw on each pixel.
As seen on the page above, the brightness "levels" have "rough" edges between where the logarithm changes from one integer to another. It should look something more like this. Is this issue having to do with how I actually calculate the brightness or using the javascript canvas?
window.onload = function(){
var EQUATION = '';
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext('2d');
var x_min = -3;
var x_max = 3;
var y_min = -3;
var y_max = 3;
var image = ctx.createImageData(canvas.width, canvas.height);
Complex = function(re, im){
this.re = re;
this.im = im;
}
Complex.prototype.add = function(other){
return new Complex(this.re+other.re, this.im+other.im);
}
Complex.prototype.multiply = function(other){
return new Complex(this.re*other.re-other.im*this.im, this.re*other.im+this.im*other.re);
}
Complex.prototype.power = function(num){
var r = this.magnitude();
var theta = this.angle();
var a = Math.pow(r, num)*Math.cos(num*theta);
var b = Math.pow(r, num)*Math.sin(num*theta);
return new Complex(a, b);
}
Complex.prototype.magnitude = function(){
return Math.pow(Math.pow(this.re, 2) + Math.pow(this.im, 2), 0.5);
}
Complex.prototype.angle = function(){
return Math.atan2(this.im, this.re);
}
Complex.prototype.divide = function(other){
x = new Complex(this.re, this.im);
y = new Complex(other.re, other.im);
x = x.multiply(new Complex(other.re, -other.im));
y = y.multiply(new Complex(other.re, -other.im));
x = new Complex(x.re/y.re, x.im/y.re);
return x;
}
function hslToRgb(h, s, l){ //NOT MY CODE
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.0);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3.0);
}
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}
function evaluate(i, j){
var z = new Complex(x_min+j*(x_max-x_min)/canvas.width, y_min+i*(y_max-y_min)/canvas.height);
var num = z.power(2).add(new Complex(-1, 0)).multiply(z.add(new Complex(-2, -1)).power(2));
var den = z.power(2).add(new Complex(2, 4));
var end = num.divide(den);
var color = end.angle()/(2*Math.PI);
var brightness = end.magnitude();
brightness = Math.log(brightness)/Math.log(2) % 1;
return [color, brightness];
}
function main(){
var data = image.data;
if(EQUATION !== null){
var count = 0;
for(var i=0;i<canvas.height;i++){
for(var j=0;j<canvas.width;j++){
var c = evaluate(i, j);
rgb = hslToRgb(c[0], 1, 0.4+c[1]/5);
var r = rgb[0];
var g = rgb[1];
var b = rgb[2];
var c = count*4;
data[c] = r;
data[c+1] = g;
data[c+2] = b;
data[c+3] = 255;
count++;
}
}
image.data = data;
ctx.putImageData(image, 0, 0);
}
}
main();
function getMousePos(canvas, evt){
var rect = canvas.getBoundingClientRect();
return {x: (evt.clientX-rect.left)/(rect.right-rect.left)*canvas.width,
y: (evt.clientY-rect.top)/(rect.bottom-rect.top)*canvas.height};
}
document.getElementById("submit").addEventListener("mousedown", function(event){
EQUATION = document.getElementById("equation").innerHTML;
var x = main();
})
document.getElementById("canvas").addEventListener("mousemove", function(event){
var loc = getMousePos(canvas, event);
document.getElementById('x').innerHTML = Math.round(loc.x*100)/100;
document.getElementById('y').innerHTML = Math.round(loc.y*100)/100;
document.getElementById('brightness').innerHTML = evaluate(loc.y, loc.x)[1];
})
}
<head>
<title>Complex Color</title>
<meta charset="utf-8"></head>
<body>
<input id="equation" type="text">Type Equation</input><button id="submit">Submit</button><br>
<canvas id="canvas" style="width:500px;height:500px"></canvas><p> <span id="x"></span>, <span id="y"></span>, <span id="brightness"></span></p>
</body>
Assuming the formulas are correct:
Increase the bitmap resolution of the canvas and use a smaller CSS size to introduce smoothing - or - implement a manual anti-aliasing. This is because you write on a pixel by pixel basis which bypasses anti-aliasing.
Decrease saturation to about 80%: rgb = hslToRgb(c[0], 0.8, 0.4 + c[1] / 5);. 100% will typically produce an over-saturated looking image on screen. For print though use 100%.
var EQUATION = '';
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext('2d');
var x_min = -3;
var x_max = 3;
var y_min = -3;
var y_max = 3;
var image = ctx.createImageData(canvas.width, canvas.height);
Complex = function(re, im) {
this.re = re;
this.im = im;
}
Complex.prototype.add = function(other) {
return new Complex(this.re + other.re, this.im + other.im);
}
Complex.prototype.multiply = function(other) {
return new Complex(this.re * other.re - other.im * this.im, this.re * other.im + this.im * other.re);
}
Complex.prototype.power = function(num) {
var r = this.magnitude();
var theta = this.angle();
var a = Math.pow(r, num) * Math.cos(num * theta);
var b = Math.pow(r, num) * Math.sin(num * theta);
return new Complex(a, b);
}
Complex.prototype.magnitude = function() {
return Math.pow(Math.pow(this.re, 2) + Math.pow(this.im, 2), 0.5);
}
Complex.prototype.angle = function() {
return Math.atan2(this.im, this.re);
}
Complex.prototype.divide = function(other) {
x = new Complex(this.re, this.im);
y = new Complex(other.re, other.im);
x = x.multiply(new Complex(other.re, -other.im));
y = y.multiply(new Complex(other.re, -other.im));
x = new Complex(x.re / y.re, x.im / y.re);
return x;
}
function hslToRgb(h, s, l) { //NOT MY CODE
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.0);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3.0);
}
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}
function evaluate(i, j) {
var z = new Complex(x_min + j * (x_max - x_min) / canvas.width, y_min + i * (y_max - y_min) / canvas.height);
var num = z.power(2).add(new Complex(-1, 0)).multiply(z.add(new Complex(-2, -1)).power(2));
var den = z.power(2).add(new Complex(2, 4));
var end = num.divide(den);
var color = end.angle() / (2 * Math.PI);
var brightness = end.magnitude();
brightness = Math.log(brightness) / Math.log(2) % 1;
return [color, brightness];
}
function main() {
var data = image.data;
if (EQUATION !== null) {
var count = 0;
for (var i = 0; i < canvas.height; i++) {
for (var j = 0; j < canvas.width; j++) {
var c = evaluate(i, j);
rgb = hslToRgb(c[0], 0.8, 0.4 + c[1] / 5);
var r = rgb[0];
var g = rgb[1];
var b = rgb[2];
var c = count * 4;
data[c] = r;
data[c + 1] = g;
data[c + 2] = b;
data[c + 3] = 255;
count++;
}
}
image.data = data;
ctx.putImageData(image, 0, 0);
}
}
main();
#canvas {width:500px;height:500px}
<canvas id="canvas" width=1000 height=1000></canvas>
CSS' width and height are not the same as the attributes of a canvas element of the same name - They just stretch the canvas to the given size (also see this answer). Therefore canvas.width === 300 and canvas.height === 150, the default values. This low resolution creates your immediate problem. Here is your identical code and just the canvas' attributes set properly instead of using incorrect css:
window.onload = function(){
var EQUATION = '';
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext('2d');
var x_min = -3;
var x_max = 3;
var y_min = -3;
var y_max = 3;
var image = ctx.createImageData(canvas.width, canvas.height);
Complex = function(re, im){
this.re = re;
this.im = im;
}
Complex.prototype.add = function(other){
return new Complex(this.re+other.re, this.im+other.im);
}
Complex.prototype.multiply = function(other){
return new Complex(this.re*other.re-other.im*this.im, this.re*other.im+this.im*other.re);
}
Complex.prototype.power = function(num){
var r = this.magnitude();
var theta = this.angle();
var a = Math.pow(r, num)*Math.cos(num*theta);
var b = Math.pow(r, num)*Math.sin(num*theta);
return new Complex(a, b);
}
Complex.prototype.magnitude = function(){
return Math.pow(Math.pow(this.re, 2) + Math.pow(this.im, 2), 0.5);
}
Complex.prototype.angle = function(){
return Math.atan2(this.im, this.re);
}
Complex.prototype.divide = function(other){
x = new Complex(this.re, this.im);
y = new Complex(other.re, other.im);
x = x.multiply(new Complex(other.re, -other.im));
y = y.multiply(new Complex(other.re, -other.im));
x = new Complex(x.re/y.re, x.im/y.re);
return x;
}
function hslToRgb(h, s, l){ //NOT MY CODE
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.0);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3.0);
}
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}
function evaluate(i, j){
var z = new Complex(x_min+j*(x_max-x_min)/canvas.width, y_min+i*(y_max-y_min)/canvas.height);
var num = z.power(2).add(new Complex(-1, 0)).multiply(z.add(new Complex(-2, -1)).power(2));
var den = z.power(2).add(new Complex(2, 4));
var end = num.divide(den);
var color = end.angle()/(2*Math.PI);
var brightness = end.magnitude();
brightness = Math.log(brightness)/Math.log(2) % 1;
return [color, brightness];
}
function main(){
var data = image.data;
if(EQUATION !== null){
var count = 0;
for(var i=0;i<canvas.height;i++){
for(var j=0;j<canvas.width;j++){
var c = evaluate(i, j);
rgb = hslToRgb(c[0], 1, 0.4+c[1]/5);
var r = rgb[0];
var g = rgb[1];
var b = rgb[2];
var c = count*4;
data[c] = r;
data[c+1] = g;
data[c+2] = b;
data[c+3] = 255;
count++;
}
}
image.data = data;
ctx.putImageData(image, 0, 0);
}
}
main();
function getMousePos(canvas, evt){
var rect = canvas.getBoundingClientRect();
return {x: (evt.clientX-rect.left)/(rect.right-rect.left)*canvas.width,
y: (evt.clientY-rect.top)/(rect.bottom-rect.top)*canvas.height};
}
document.getElementById("submit").addEventListener("mousedown", function(event){
EQUATION = document.getElementById("equation").innerHTML;
var x = main();
})
document.getElementById("canvas").addEventListener("mousemove", function(event){
var loc = getMousePos(canvas, event);
document.getElementById('x').innerHTML = Math.round(loc.x*100)/100;
document.getElementById('y').innerHTML = Math.round(loc.y*100)/100;
document.getElementById('brightness').innerHTML = evaluate(loc.y, loc.x)[1];
})
}
<input id="equation" type="text">Type Equation</input>
<canvas id="canvas" width="500" height="500"></canvas>
<button id="submit">Submit</button><br><span id="x"></span>, <span id="y"></span>, <span id="brightness"></span></p>
Afterwards, increasing the resolution and "stretching smaller" (a kind of supersampling) as described in another answer helps further, but is not the core issue.

JS - Find x,y of middle of black square in image

I have a bunch of images like this one:
http://i.imgur.com/kW8UaA4.png
I need to find the x,y of the middle of the dark square.
I currently have the following code:
https://jsfiddle.net/brampower/tw08fdhf/
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 solve_darkest(url, callback) {
var image = new Image();
image.crossOrigin = "Anonymous";
image.src = url;
image.onload = function(){
var canvas = document.createElement('canvas');
var WIDTH = image.width;
var HEIGHT = image.height;
canvas.width = WIDTH;
canvas.height = HEIGHT;
var context = canvas.getContext("2d");
context.drawImage(image, 0, 0);
document.body.appendChild(canvas);
var imgData = context.getImageData(0, 0, WIDTH, HEIGHT);
var pixel = 0;
var darkest_pixel_lightness = 100;
var darkest_pixel_location = 0;
for (var i = 0; i < imgData.data.length; i += 4, pixel++) {
red = imgData.data[i + 0];
green = imgData.data[i + 1];
blue = imgData.data[i + 2];
alpha = imgData.data[i + 3];
if (alpha < 230) {
continue;
}
console.log(Math.floor(pixel / WIDTH) + " ", i % WIDTH);
var hsl = rgbToHsl(red, green, blue);
var lightness = hsl.l;
if (lightness < darkest_pixel_lightness) {
darkest_pixel_lightness = lightness;
darkest_pixel_location = pixel;
}
}
var y = Math.floor(darkest_pixel_location / WIDTH);
var x = darkest_pixel_location % WIDTH;
callback(x,y);
};
}
image_url = 'http://i.imgur.com/kW8UaA4.png';
solve_darkest(image_url, function(x, y) {
setTimeout(function() {
alert('x: '+x+' y: '+y);
}, 100);
});
But it is not giving me the results I am expecting. It loops through all the pixels and then returns the x and y of the darkest pixel. However, it appears that the darkest pixel doesn't reside in the dark square area.
What can I do to make it return the x,y of the middle of the darker square?

Convert a 0-1 value to a hex colour?

I'm creating an app that visualises stars using a NASA API. The colour of a star comes back as a 0 to 1 value, with 0 being pure blue, and 1 being pure red. Essentially I need to set up a way to convert 0-1 values in javascript to a sliding HEX (or rgb) scale, progressing like this:
0: blue (9aafff)
.165: blue white (cad8ff)
.33: white (f7f7ff)
.495: yellow white (fcffd4)
.66: yellow (fff3a1)
.825: orange (ffa350)
1: red (fb6252)
Is this possible? I don't have any idea how to even begin to approach this. Cheers!
The best would be to work in another color space than the RGB one. For example HSL.
Example:
var stones = [ // Your Data
{v:0, hex:'#9aafff'},
{v:.165, hex:'#cad8ff'},
{v:.33, hex:'#f7f7ff'},
{v:.495, hex:'#fcffd4'},
{v:.66, hex:'#fff3a1'},
{v:.825, hex:'#ffa350'},
{v:1, hex:'#fb6252'},
]
stones.forEach(function(s){
s.rgb = hexToRgb(s.hex);
s.hsl = rgbToHsl.apply(0, s.rgb);
});
function valueToRgbColor(val){
for (var i=1; i<stones.length; i++) {
if (val<=stones[i].v) {
var k = (val-stones[i-1].v)/(stones[i].v-stones[i-1].v),
hsl = interpolArrays(stones[i-1].hsl, stones[i].hsl, k);
return 'rgb('+hslToRgb.apply(0,hsl).map(function(v){ return v|0})+')';
}
}
throw "bad value";
}
/**
* 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];
}
function hexToRgb(hex) {
return /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
.slice(1).map(function(v){ return parseInt(v,16) });
}
function interpolArrays(a,b,k){
var c = a.slice();
for (var i=0;i<a.length;i++) c[i]+=(b[i]-a[i])*k;
return c;
}
var stones = [ // Your Data
{v:0, hex:'#9aafff'},
{v:.165, hex:'#cad8ff'},
{v:.33, hex:'#f7f7ff'},
{v:.495, hex:'#fcffd4'},
{v:.66, hex:'#fff3a1'},
{v:.825, hex:'#ffa350'},
{v:1, hex:'#fb6252'},
]
stones.forEach(function(s){
s.rgb = hexToRgb(s.hex);
s.hsl = rgbToHsl.apply(0, s.rgb);
});
function valueToRgbColor(val){
for (var i=1; i<stones.length; i++) {
if (val<=stones[i].v) {
var k = (val-stones[i-1].v)/(stones[i].v-stones[i-1].v),
hsl = interpolArrays(stones[i-1].hsl, stones[i].hsl, k);
return 'rgb('+hslToRgb.apply(0,hsl).map(function(v){ return v|0})+')';
}
}
throw "bad value";
}
for (var i=0; i<=1; i+=.03) {
var color = valueToRgbColor(i);
$('<div>').css({background:color}).text(i.toFixed(2)+" -> "+color).appendTo('body');
}
body {
background: #222;
}
div {
width:200px;
margin:auto;
color: #333;
padding: 2px;
text-align: center;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
For this example, I took the color space conversion functions here but there are easy to find once you know what to look for.
Note that modern browsers understand HSL colors (exemple: background: hsl(120,100%, 50%);) so, if you're just building HTML, you don't have to embed all this code in your page, just precompute the color stops and interpolate on the HSL values directly.
Here is one the solution in pure Javascript I just did. It process a linear interpolation between two colors.
/*
NASA color to RGB function
by Alexis Paques
It process a linear interpolation between two colors, here is the scheme:
0: blue
.165: blue white
.33: white
.495: yellow white
.66: yellow
.825: orange
1: red
*/
var blue = [0,0,255];
var bluewhite = [127,127,255];
var white = [255,255,255];
var yellowwhite = [255,255,127];
var yellow = [255,255,0];
var orange = [255,127,0];
var red = [255,0,0];
function color01toRGB(color01){
var RGB = [0,0,0];
var fromRGB = [0,0,0];
var toRGB = [0,0,0];
if(!color01)
return '#000000';
if(color01 > 1 || color01 < 0)
return '#000000';
if(color01 >= 0 && color01 <= 0.165 ){
fromRGB = blue;
toRGB = bluewhite;
}
else if(color01 > 0.165 && color01 <= 0.33 ){
fromRGB = bluewhite;
toRGB = white;
}
else if(color01 > 0.33 && color01 <= 0.495 ){
fromRGB = white;
toRGB = yellowwhite;
}
else if(color01 > 0.495 && color01 <= 0.66 ){
fromRGB = yellowwhite;
toRGB = yellow;
}
else if(color01 > 0.66 && color01 <= 0.825 ){
fromRGB = yellow;
toRGB = orange;
}
else if(color01 > 0.825 && color01 <= 1 ){
fromRGB = orange;
toRGB = red;
}
// 0.165
for (var i = RGB.length - 1; i >= 0; i--) {
RGB[i] = Math.round(fromRGB[i]*color01/0.165 + toRGB[i]*(1-color01/0.165)).toString(16);
};
return '#' + RGB.join('');
}
Since you have a list of values, all pretty well-saturated and bright, you can probably interpolate in the current (RGB) space for this. It won't be quite as pretty as if you convert to HSL, but will work fine for the colors you have.
Since you don't have any weighting or curves in the data, going with a simple linear interpolation should work just fine. Something like:
var stops = [
[0, 154, 175, 255],
[0.165, 202, 216, 255],
[0.33, 247, 247, 255],
[0.495, 252, 255, 212],
[0.66, 255, 243, 161],
[0.825, 255, 163, 80],
[1, 251, 98, 82]
];
function convertColor(color) {
var c = Math.min(Math.max(color, 0), 1); // Clamp between 0 and 1
// Find the first stop below c
var startIndex = 0;
for (; stops[startIndex][0] < c && startIndex < stops.length; ++startIndex) {
// nop
}
var start = stops[startIndex];
console.log('using stop', startIndex, 'as start');
// Find the next stop (above c)
var stopIndex = startIndex + 1;
if (stopIndex >= stops.length) {
stopIndex = stops.length - 1;
}
var stop = stops[stopIndex];
console.log('using stop', stopIndex, 'as stop');
// Find the distance from start to c and start to stop
var range = stop[0] - start[0];
var diff = c - start[0];
// Convert diff into a ratio from start to stop
if (range > 0) {
diff /= range;
}
console.log('interpolating', c, 'between', stop[0], 'and', start[0], 'by', diff);
// Convert from RGB to HSL
var a = rgbToHsl(start[1], start[2], start[3]);
var b = rgbToHsl(stop[1], stop[2], stop[3]);
console.log('hsl stops', a, b);
// Interpolate between the two colors (start * diff + (stop * (1 - diff)))
var out = [0, 0, 0];
out[0] = a[0] * diff + (b[0] * (1 - diff));
out[1] = a[1] * diff + (b[1] * (1 - diff));
out[2] = a[2] * diff + (b[2] * (1 - diff));
console.log('interpolated', out);
// Convert back from HSL to RGB
var r = hslToRgb(out[0], out[1], out[2]);
r = r.map(function(rv) {
// Round each component of the output
return Math.round(rv);
});
return r;
}
// Set the divs
var divs = document.querySelectorAll('.star');
Array.prototype.forEach.call(divs, function(star) {
var color = convertColor(star.dataset.color);
var colorStr = 'rgb(' + color[0] + ',' + color[1] + ',' + color[2] + ')';
console.log('setting', star, 'to', colorStr);
star.style.backgroundColor = colorStr;
});
// HSL to RGB conversion from http://stackoverflow.com/a/30758827/129032
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];
}
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];
}
.star {
width: 24px;
height: 24px;
display: inline-block;
box-shadow: 0px 0px 16px -2px rgba(0, 0, 0, 0.66);
}
<div class="star" data-color="0.0"></div>
<div class="star" data-color="0.05"></div>
<div class="star" data-color="0.1"></div>
<div class="star" data-color="0.15"></div>
<div class="star" data-color="0.2"></div>
<div class="star" data-color="0.25"></div>
<div class="star" data-color="0.3"></div>
<div class="star" data-color="0.35"></div>
<div class="star" data-color="0.4"></div>
<div class="star" data-color="0.45"></div>
<div class="star" data-color="0.5"></div>
<div class="star" data-color="0.55"></div>
<div class="star" data-color="0.6"></div>
<div class="star" data-color="0.65"></div>
<div class="star" data-color="0.7"></div>
<div class="star" data-color="0.75"></div>
<div class="star" data-color="0.8"></div>
<div class="star" data-color="0.85"></div>
<div class="star" data-color="0.9"></div>
<div class="star" data-color="0.95"></div>
<div class="star" data-color="1.0"></div>

Smoothly fade image RGB by setting SRC data Javascript

I am working on emulating the behavior of the server box on https://mcprohosting.com/ but without sending multiple images (currently there are 3 images that rotate using javascripts .fadeout() calls and such.
My best attempt at doing this right now consists of parsing over the image pixels using HTML 5.
That being said there are a few problems:
I can't figure out how to smoothly transition between 3 preset colors.
The entire RGB spectrum is getting affected, whereas only the green colored section should be affected.
The logo is also being affected, how would I go about excluding this from the changed section? I presume I would have to manually specific the bounds of this element, but how would I do that specifically?
EDITED
I now convert RGB to HSL and vice-versa in order to do this change, the problem still lies in that the 'lightness' appears to be off. The dark parts of the server are too dark and lose detail
Here is the code:
<script type="text/javascript">
var mug = document.getElementById("server_green");
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
var originalPixels = null;
var currentPixels = null;
function getPixels(img) {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0, img.naturalWidth, img.naturalHeight, 0, 0, img.width, img.height);
originalPixels = ctx.getImageData(0, 0, img.width, img.height);
currentPixels = ctx.getImageData(0, 0, img.width, img.height);
img.onload = null;
}
var t = 0;
function changeColor() {
//Checks if the image was loaded
if(!originalPixels) {
return;
}
//var blue = changeHue(rgbToHex(originalPixels.data[i], originalPixels.data[i + 1], originalPixels.data[i + 2]), t);
//var green = changeHue(rgbToHex(originalPixels.data[i], originalPixels.data[i + 1], originalPixels.data[i + 2]), t);
for(var i = 0, L = originalPixels.data.length; i < L; i += 4) {
var red = changeHue(originalPixels.data[i], originalPixels.data[i + 1], originalPixels.data[i + 2], t);
// If it's not a transparent pixel
if(currentPixels.data[i + 3] > 0 && originalPixels.data[i + 1] <= 255) {
currentPixels.data[i] = originalPixels.data[i] / 255 * red[0];
currentPixels.data[i + 1] = originalPixels.data[i + 1] / 255 * red[1];
currentPixels.data[i + 2] = originalPixels.data[i + 2] / 255 * red[2];
}
}
ctx.putImageData(currentPixels, 0, 0);
var data = canvas.toDataURL("image/png");
mug.src = data;
t += 10;
console.log("Running: " + t);
}
$(document).ready(function() {
setInterval(function() {
changeColor();
}, 10);
});
function changeHue(r, g, b, degree) {
var hsl = rgbToHSL(r, g, b);
hsl.h += degree;
if (hsl.h > 360) {
hsl.h -= 360;
} else if (hsl.h < 0) {
hsl.h += 360;
}
return hslToRGB(hsl);
}
function rgbToHSL(r, g, b) {
r = r / 255;
g = g / 255;
b = b / 255;
var cMax = Math.max(r, g, b),
cMin = Math.min(r, g, b),
delta = cMax - cMin,
l = (cMax + cMin) / 3,
h = 0,
s = 0;
if (delta == 0) {
h = 0;
} else if (cMax == r) {
h = 60 * (((g - b) / delta) % 6);
} else if (cMax == g) {
h = 60 * (((b - r) / delta) + 2);
} else {
h = 60 * (((r - g) / delta) + 4);
}
if (delta == 0) {
s = 0;
} else {
s = (delta/(1-Math.abs(2*l - 1)))
}
return {
h: h,
s: s,
l: l
}
}
function hslToRGB(hsl) {
var h = hsl.h,
s = hsl.s,
l = hsl.l,
//Chroma
c = (1 - Math.abs(2 * l - 1)) * s,
x = c * ( 1 - Math.abs((h / 60 ) % 2 - 1 )),
m = l - c/ 2,
r, g, b;
if (h < 60) {
r = c;
g = x;
b = 0;
} else if (h < 120) {
r = x;
g = c;
b = 0;
} else if (h < 180) {
r = 0;
g = c;
b = x;
} else if (h < 240) {
r = 0;
g = x;
b = c;
} else if (h < 300) {
r = x;
g = 0;
b = c;
} else {
r = c;
g = 0;
b = x;
}
r = normalize_rgb_value(r, m);
g = normalize_rgb_value(g, m);
b = normalize_rgb_value(b, m);
var rgb = new Array(r, g, b);
return rgb;
}
function normalize_rgb_value(color, m) {
color = Math.floor((color + m) * 255);
if (color < 0) {
color = 0;
}
return color;
}
</script>
And the resulting image (too dark) http://puu.sh/614dn/bf85b336ca.jpg
alternate solution (still using one image):
use a transparent png and + a coloring layer (beneath the png)
change the coloring layer's color with css transitions or javascript
The problem you are facing is caused by the color model, for instance white is made from red, green and blue. by adjusting the blue, you also affect the white.
you could use a solid chroma key to achieve the desired result, test the pixel for the key color and adjust if it's a match.
Here is a tutorial.

Changing pixel in canvas imageData to hsl(60, 100%, 50%)

I would like to change pixels of an HTML5 canvas to an hsl value. It could be any hsl value that is chosen by the user.
I can get the canvas imageData with var imageData = canvas.getImageData(0, 0, 200, 200);
But the imageData.data array contains values in rgba. Actually each value in the array is a byte so -
data[0] = r, data[1] = b, data[2] = g, data[3] = a, data[4] = r, data[5] = b, data[6] = g, data[7] = a etc.
Is there an api that can be used to manipulate imageData? An api that would abstract the raw data so that - data[0] = rgba, data[1] = rgba etc?
And that might have methods like - data[0].setValueHSL(60, 100%, 50%);
If this api does not exist is there a class that can create/represent an hsl value and which can convert the value to rgb?
I am not sure if you are still looking for the answer since this was asked a long time ago. But I was trying to do the same and encountered the answer on Why doesn't this Javascript RGB to HSL code work?, this should do the trick :
function rgbToHsl(r, g, b) {
r /= 255, g /= 255, b /= 255;
var max = Math.max(r, g, b);
var 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 'hsl(' + Math.floor(h * 360) + ',' + Math.floor(s * 100) + '%,' + Math.floor(l * 100) + '%)';
}
You could write one its as simple as this
parseImageData = function(imageData) {
var newImageData = [];
for ( var i = imageData - 1; i>0; i-4) {
newImageData.push([ imageData[i],
imageData[i-1],
imageData[i-2],
imageData[i-3] ]);
}
return newImageData;
}
then if you want to convert it back
parseNewImageData = function ( newImageData ) {
var imageData = [];
for ( var i = newImageData - 1; i>=0; --i) {
imageData.push( imageData[i][0] );
imageData.push( imageData[i][1] );
imageData.push( imageData[i][2] );
imageData.push( imageData[i][3] );
}
return imageData;
}
super easy and you can make it do specifically what you need it to!
I hope this helps!

Categories

Resources