-1

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?

Jeff
  • 19
  • 1
  • 1
  • 6
  • why is my question getting down-voted? There's nothing on ocanvas' documentation about this at all, and I don't know how to integrate canvas' abilities into ocanvas – Jeff May 29 '15 at 01:23

2 Answers2

1

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.

enter image description here

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>
markE
  • 102,905
  • 11
  • 164
  • 176
  • That's an awesome answer, thank you! but I can't use it because i need to know how to change a sprite, not an area on the canvas. Is there a way to change only the pixels on the sprite and not the pixels on the canvas? – Jeff May 30 '15 at 10:51
  • Sure, you just draw the desired sprite onto a separate canvas element and do you magic there. Since another canvas can be the image source for `drawImage`, you just drawImage the sprite canvas to your main canvas: `context.drawImage(aSpriteCanvas,x,y);` ;-) – markE May 30 '15 at 14:30
1

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();