最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Export canvas as PNG without transparency (alpha channel) - Stack Overflow

programmeradmin2浏览0评论

** EDIT ** I have to export a big canvas (5Mo < my_export < 40Mo) and POST it with ajax. I have to POST it using the fewer Content-Length bytes.

When I export my canvas (with toDataUrl), it always return me a base64 representation of a .png file with an alpha channel. Alpha channel weight is about 25% of image's data and I don't need it.

Suggested answer :

  1. PNG pression is good. If the alpha channel is flat (all the same value) then it will not take up much space. Even with fully opaque alpha channel, the (pressed) .png file is larger. (about +10% on a few tests).
  2. You should fill the canvas (using the color you want for the background) before drawing on it. I already founded this solution on stackoverflow but it seems it doesnt works
  3. Base64 encode add 34% more bytes. Already solved. Using this solution : convert-data-uri-to-file-then-append-to-formdata (A Uint8Array into a Blob into a FormData)

Is there a way to export my canvas as .png but without alpha channel ? If "no", is there a way (lib) in javascript to trim alpha channel ? Export as .jpeg is not a solution, I need a .png file.

Thanks ! : )

** EDIT ** I have to export a big canvas (5Mo < my_export < 40Mo) and POST it with ajax. I have to POST it using the fewer Content-Length bytes.

When I export my canvas (with toDataUrl), it always return me a base64 representation of a .png file with an alpha channel. Alpha channel weight is about 25% of image's data and I don't need it.

Suggested answer :

  1. PNG pression is good. If the alpha channel is flat (all the same value) then it will not take up much space. Even with fully opaque alpha channel, the (pressed) .png file is larger. (about +10% on a few tests).
  2. You should fill the canvas (using the color you want for the background) before drawing on it. I already founded this solution on stackoverflow but it seems it doesnt works
  3. Base64 encode add 34% more bytes. Already solved. Using this solution : convert-data-uri-to-file-then-append-to-formdata (A Uint8Array into a Blob into a FormData)

Is there a way to export my canvas as .png but without alpha channel ? If "no", is there a way (lib) in javascript to trim alpha channel ? Export as .jpeg is not a solution, I need a .png file.

Thanks ! : )

Share Improve this question edited Jun 4, 2018 at 15:17 LeoLolCat asked May 25, 2018 at 12:37 LeoLolCatLeoLolCat 211 silver badge3 bronze badges 3
  • I'll only post this as a ment, as I'm not a 100% certain, but I'd say don't hold your breath. I checked as far as I could on MDN and there's no indication of such an option. You could try using getImageData(), extracting the color values to a new ArrayBuffer and then pressing that with a client-side library (NPM might have something useful). I have no idea what size that would yield pared to an actual .png pression, though considering that data URL's are base64 encoded, which is always larger than its binary form, it might be parable, or even better, at such sizes as yours. – Máté Safranka Commented May 25, 2018 at 13:11
  • Obviously, though, in this case the backend would have to be prepared to receive the image in this nonstandard format. – Máté Safranka Commented May 25, 2018 at 13:13
  • I didn't try this solution yet. I hope to find another solution than parsing the whole png file, extract RGB value and then re-create a png without alpha channel – LeoLolCat Commented Jun 4, 2018 at 15:24
Add a ment  | 

4 Answers 4

Reset to default 4

PNG pression is good. If the alpha channel is flat (all the same value) then it will not take up much space.

You can fill in the transparent and semi transparent pixels using posite operation "destination-over".

The following function will set all pixels alpha value to 1.

function fillAlpha(ctx, bgColor){  // bgColor is a valid CSS color ctx is 2d context
   // save state
   ctx.save();
   // make sure defaults are set
   ctx.globalAlpha = 1;
   ctx.setTransform(1, 0, 0, 1, 0, 0);
   ctx.filter = "none";

   // fill transparent pixels with bgColor
   ctx.globalCompositeOperation = "destination-over";
   ctx.fillStyle = bgColor;
   ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);

   // cleanup
   ctx.restore();
}

You can then save the image as PNG with the Alpha channel as small as possible.

My goal is similar to yours: exporting a canvas in PNG format, and removing the alpha channel information from its dataURL representation before sending it to a server. My reasons are different from yours, however. I wanted to bine the resulting image files as a PDF at the other end, and the library I was using img2pdf couldn't handle alpha channel information. The library I was using for the canvas was fabric.js, but the problem should arise when exporting any HTML5 canvas via toDataUrl().

Fortunately, there is a nice javascript library called pngjs which can manipulate PNG files in JavaScript. It es with asynchronous and synchronous routines. This is how I converted dataURLs from Alpha to non-Alpha. Your step 3 (dataURL to Blog) should be enough to finish the job.

let PNG = require('pngjs').PNG;

/* thehref: DataURL form of a png file. */
function removeAlphaFromDataUrl(thehref) {
  let dataurlstring = "data:image/png;base64,"
  let buff = new Buffer(thehref.slice(22), 'base64')
  let png = PNG.sync.read(buff);
  let options = { colorType: 2 };
  let buffer = PNG.sync.write(png, options);
  let output = buffer.toString('base64');
  let newhref = dataurlstring + output
  return newhref;
}

I hope this helps.

When I export my canvas (with toDataUrl), it always return me a .png file with an alpha channel.

No it doesn't return a ".png file", toDataURL returns a data URL which is is a string made of a header and of a base64 representation of your file. This base64 representation will itself take 34% more bytes than the represented binary file.

So to fix the X of your XY problem send your png image as a binary Blob..


Now to provide some content about the Y question, I didn't checked in every browsers, but I don't think any has implemented anything else than 32bits png output. This might change in the future, moreover since most of UAs now support the {alpha: false} 2d context parameter, and that we will also probavly have more control on the bit-depth in use on the context in an hopefully near-future.

But for the time being, you would have to do it yourself, by running your own png encoder.

You should fill the canvas (using the color you want for the background) before drawing on it.

For example, before:

var ctx = canvas.getContext('2d');
drawEverythingOnMyContext(ctx);
image = canvas.toDataURL();

After:

var ctx = canvas.getContext('2d');
// draw a background
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// draw then
drawEverythingOnMyContext(ctx);
image = canvas.toDataURL();
发布评论

评论列表(0)

  1. 暂无评论