ComeRun ComeRun - 1 month ago 9
jQuery Question

HTML5 Canvas changing image color

I am using jquery slider to change my image color from Blue to red (with range of -100 to 100), by tinting it. It means when the slider value is 0, the image should look normal (default one) and changes based on the value of the slider from blue (-100) to (100).

In my local I am able to load the image into my canvas (for some reason the image is not loaded on jsfiddle).

The main issue is the current code does not modify the color as expected.

http://jsfiddle.net/q3qw1c0o/3/

$(function(){

var currentValue = $('#currentValue');


$( "#slider" ).slider({
range: "max",
min: -100,
max: 100,
value: 0,
slide: function( event, ui ) {
$( "#sliderValue" ).val( ui.value );
$(ui.value).val($('#sliderValue').val());
changeIt(ui.value);
}
});

$("#sliderValue").change(function() {
$("#slider").slider("value" , $(this).val())
});

});

var x; //drawing context
var width;
var height;
var fg;
var buffer

window.onload = function() {
var drawingCanvas = document.getElementById('myDrawing');
// Check the element is in the DOM and the browser supports canvas
if(drawingCanvas && drawingCanvas.getContext) {
// Initaliase a 2-dimensional drawing context
x = drawingCanvas.getContext('2d');
width = x.canvas.width;
height = x.canvas.height;

fg = new Image();
fg.src = 'http://icons.iconarchive.com/icons/iconshow/transport/256/Sportscar-car-icon.png';

// to tint the image, draw it first
x.drawImage(fg,0,0);

}
}

function changeIt(value) {


// create offscreen buffer,
buffer = document.createElement('canvas');
buffer.width = fg.width;
buffer.height = fg.height;
bx = buffer.getContext('2d');

// fill offscreen buffer with the tint color
bx.fillStyle = 'rgb(' + value + ', 0, ' + (255 - value) + ')';
bx.fillRect(0,0,buffer.width,buffer.height);

// destination atop makes a result with an alpha channel identical to fg, but with all pixels retaining their original color *as far as I can tell*
bx.globalCompositeOperation = "destination-atop";
bx.drawImage(fg,0,0);

//then set the global alpha to the amound that you want to tint it, and draw the buffer directly on top of it.
x.globalAlpha = 0.5;
x.drawImage(buffer,0,0);

}


Please have a look.

Answer

Here's one way using getImageData to manipulate the hue of each image pixel:

  • Use getImageData to fetch the RGBA color data of each pixel

  • Convert the RGBA color to HSL color. The H in HSL means Hue which is what we normally think of as "color".

  • If the Hue of an original pixel is red-ish (Hue<30 or Hue>300) then shift the hue by the amount specified in your range control. If you want to shift from red to blue, then your slider should shift the color (Hue) from 0 to -.33.

Note: getImageData requires that the image originate on the same domain as the webpage or else you will get a cross-domain security error.

Here's example code and a Demo:

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;

var imgData,data,originalData;

$myslider=$('#myslider');
$myslider.attr({min:0,max:33}).val(0);
$myslider.on('input change',function(){
  var value=parseInt($(this).val());
  HueShift(30,300,-value/100);
});

var img=new Image();
img.crossOrigin='anonymous';
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/Sportscar.png";
function start(){
  cw=canvas.width=img.width;
  ch=canvas.height=img.height;
  ctx.drawImage(img,0,0);

  imgData=ctx.getImageData(0,0,cw,ch);
  data=imgData.data;
  imgData1=ctx.getImageData(0,0,cw,ch);
  originalData=imgData1.data;

}



function HueShift(hue1,hue2,shift){

  for(var i=0;i<data.length;i+=4){
    red=originalData[i+0];
    green=originalData[i+1];
    blue=originalData[i+2];
    alpha=originalData[i+3];

    // skip transparent/semiTransparent pixels
    if(alpha<230){continue;}

    var hsl=rgbToHsl(red,green,blue);
    var hue=hsl.h*360;

    // change redish pixels to the new color
    if(hue<30 || hue>300){


      var newRgb=hslToRgb(hsl.h+shift,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,0,0);
}






////////////////////////
// Helper functions
//

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;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Change the slider to change the car color</h4>
<input id=myslider type=range min=0 max=100 value=0><br>
<canvas id="canvas" width=300 height=300></canvas>

Comments