I was wondering if it is possible to have a coloured canvas in HTML5 and then save it as a black and white (grayscale) PNG, while you click on the save button. I managed to save the canvas as a PNG already, which is great! But I'd like to add the grayscale feature without having to change it myself with a program as Illustrator etc. The canvas has moving particles btw. (I'm not sure if that has any effect though)
Thanks~!
function download(){
var dt = c.toDataURL('image/png');
this.href = dt;
};
downloadlink.addEventListener('click', download, false);
It's possible by extracting the RGB data from the canvas and calculate their luma values.
There are a couple of requisites that needs to be fulfilled though, like CORS (which does not seem to be a problem in this case as you can already save out an image) and if you wish to keep the original data after save you can either copy the current image on canvas to a temporary one, or keep a backup of the pixel data that needs to be extracted.
The formula for converting the RGB data into a luma value (grey) is as follows based on the REC 709 (or BT.709) formula:
luma = Red x 0.2126 + Green x 0.7152 + Blue x 0.0722
Alternatively you could use the REC 601 (BT.601) formula instead (which is more common for SD video footage):
luma = Red x 0.299 + Green x 0.587 + Blue x 0.114
To use this, simply iterate over all pixels, obtain the luma value using one of these formulas and replace all channels in target with the resulting luma value.
For temporary data we can do either (when save button is clicked):
drawImage()
getImageData()
or
getImageData()
- this will act as our backupImageData
the same size using createImageData()
ImageData
ImageData
, save out imageIf the latter step is necessary depends really on how you update the canvas. If you are animating a loop there may not be need to put back the backup (or keep one) if everything gets updated anyways, but if so and you see flash of grey or some areas left grey, then this step would be needed.
ImageData
approachvar ctx = c.getContext("2d"), img = new Image;
img.onload = setup; img.crossOrigin = "";
img.src = "//i.imgur.com/OrYVGI8.jpg";
function setup() {
c.width = this.naturalWidth; c.height = this.naturalHeight;
ctx.drawImage(this, 0, 0); btn.disabled = false
}
// Main code for demo
btn.onclick = function() {
var idataSrc = ctx.getImageData(0, 0, c.width, c.height), // original
idataTrg = ctx.createImageData(c.width, c.height), // empty data
dataSrc = idataSrc.data, // reference the data itself
dataTrg = idataTrg.data,
len = dataSrc.length, i = 0, luma;
// convert by iterating over each pixel each representing RGBA
for(; i < len; i += 4) {
// calculate luma, here using Rec 709
luma = dataSrc[i] * 0.2126 + dataSrc[i+1] * 0.7152 + dataSrc[i+2] * 0.0722;
// update target's RGB using the same luma value for all channels
dataTrg[i] = dataTrg[i+1] = dataTrg[i+2] = luma;
dataTrg[i+3] = dataSrc[i+3]; // copy alpha
}
// put back luma data so we can save it as image
ctx.putImageData(idataTrg, 0, 0);
demo.src = c.toDataURL(); // set demo result's src url
// restore backup data
ctx.putImageData(idataSrc, 0, 0);
};
<button disabled id=btn>SAVE TO GREY</button> (click then scroll down to see result)<br>
<canvas id=c></canvas><br><img id=demo>