4

I was wondering if there was a way to alter the color depth of the image in an HTML5 Canvas Element, in that the color of each pixel in the image will get "rounded" to the nearest equivalent in a lesser color bit depth, for instance. Thank you.

KeithComito
  • 1,387
  • 1
  • 13
  • 24

3 Answers3

10

Yes it can be done, and isn't too difficult. See my answer here: How can I use a gradient map to tone a HTML5 canvas with an image in the canvas.

Just like in tinting, simply all you must do is go over each pixel and change the RGB values to be lesser (steps of 8 or 16 instead of steps of 1)

So for 8 steps you could do:

redValue = redValue - (redValue % 32) // 155 would become 128, etc

Which ought to work, but you should check the edge cases.

Example:

http://jsfiddle.net/gbBz7/

Community
  • 1
  • 1
Simon Sarris
  • 62,212
  • 13
  • 141
  • 171
  • Awesome; this is super helpful. Quick question though; the canvas is 32 color bit depth by default, right; so you are reducing it to to 32/4 = 8 bit depth in this example? Or am I thinking about it incorrectly? Thanks again! – KeithComito Oct 25 '11 at 21:03
3

You can iterate through each byte of each color with getImageData, so you could apply some math functions to it to e.g. represent 8-bit colors: http://jsfiddle.net/pimvdb/eGjak/191/.

var imgdata = ctx.getImageData(0, 0, 400, 400); // get data
var data = imgdata.data; // bytes

// 8-bit: rrr ggg bb
for(var i = 0; i < data.length; i += 4) {
    data[i]     = nearest(data[i],     8); // set value to nearest of 8 possibilities
    data[i + 1] = nearest(data[i + 1], 8);
    data[i + 2] = nearest(data[i + 2], 4);
}

ctx.putImageData(imgdata, 0, 0); // put image data to canvas

function nearest(x, a) { // will round down to nearest of a possibilities
                         // in the range 0 <= x <= 255
    return Math.floor(x / (255 / a)) * (255 / a);
}
pimvdb
  • 151,816
  • 78
  • 307
  • 352
  • Okay; that's what I was figuring. I was kinda hoping there was some predefined function in there somewhere like "change color depth"...guess that was wishful thinking, eh? – KeithComito Oct 25 '11 at 20:51
  • @user201926: Sorry missed something... was setting to black/white. Edit: This should be better I hope. – pimvdb Oct 25 '11 at 20:52
  • This answer is the best solution, but there is a bug on the last line. Colors should be clamped to integers. `return Math.ceil(Math.floor(c / (255 / 16)) * (255 / 16));` – Ohhh Mar 16 '21 at 16:07
3

I don't think there is built in setting for this. But you should be able to reduce each pixel channel value to a more limited set of values. Something like:

var ctx, width, height;
var factor = 8;
var pixels = ctx.getImageData(0, 0, width, height).data;

function reduce(val) {
    return Math.round(val/factor)*factor;
}

for(var i = 0, l = pixels.length; i < l; i+=4) {
    pixels[i] = reduce(pixels[i]);
    pixels[i+1] = reduce(pixels[i+1]);
    pixels[i+2] = reduce(pixels[i+2]);
}

ctx.putImageData(pixels, 0, 0)
gthmb
  • 808
  • 5
  • 10