Automattic / node-canvas

Node canvas is a Cairo backed Canvas implementation for NodeJS.
10.17k stars 1.17k forks source link

WebP decoding support #1258

Open josephrocca opened 6 years ago

josephrocca commented 6 years ago

Issue or Feature

I'd like to be able to draw webp images onto the canvas like I can in the browser. Note that I'm not talking about encoding (i.e. toDataURL("image/webp")), since that already has an open issue, and an extension.

Steps to Reproduce

Below is a minimal example that should work, but doesn't (It predictably throws Error: Unsupported image type). You can comment out the webp dataURL and uncomment the PNG url to test that it's working fine with PNGs (no surprise).

(async function() {

  console.log("starting");

  let Image = require("canvas").Image;
  let createCanvas = require("canvas").createCanvas;

  let img = await new Promise((resolve, reject) => {
    let img = new Image();
    img.onerror = reject;
    img.onload = resolve;
    img.src = "data:image/webp;base64,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";
    //img.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANcAAADrCAMAAADNG/NRAAAAqFBMVEX///+Hh4cpKSlZWVnw8PAAAABWVlYmJiaKiopbW1v19fXrn7ZPT0+Dg4MjIyPY2NgYGBhHR0f/rcYRERG6uroeHh5mZmZ8fHybm5v5+fl1dXUuLi5ubm43Nzd9fX3S0tJAQEClpaWSkpLFxcWurq7j4+MHGxfXkqcVIB2sd4fg4OD1pb0gJSNvUVp8WWTJyclGOT2JYW2ebn0ADAB3VmA3MDK5f5E2PDpccQLUAAAOwUlEQVR4nO2daWPiSA6Gw2WOsQkM92HC2dOT7t1sz/bu/v9/tkGvnEhYvsBMAl3vN7Ap14NtVVklyQ8PTk5OTk43padHofpH96Y8NaSGH92b8tSovmvguD69HNdtqdF812D30b0pQfspad1/13pFX21nH923SzQe9I5qS4X0VaPz0X27RMvR8ZZqtmvvaq+bx+8c12eU47otRVzQHXANybT7C4zFC9K6fftc+0H3KDHPqDbDu+CqxuS4Pq8cV0HVDZXb9bQDdza9ZC4yjoND55w+dRqGrsJxogMOZWAxV40HswHtty7YfKcRb/dv4jKIFBdPO2ho6/oFm3dcJctxncU1a3Rj+lu46tfi2g1Jfi0mf5hT2cY3+bd7yxKWwbVuDF7V3bZi2tKWbDVeMg+S3FIy1oVcPk2iF61KTK1u8jGlBjm48rXkuBzXr8rVTFTjKZsr+dcsk0u6Oa7E1Vz7iVrOs9RP/jG0NsAWIclvX5Wr305UOMpS8m9ZfeuERQe+Mld85OZLP0zsVKTE30ZNOC7HdadcveONXeX5YSaXvt2ZK/4s0O3CYPAHi8uDmEv99iKuLTwLAYB8OsiylcHVXsjjhxYrNKc/ajsW/CdYFTpu4BlNmAfOyzWlqfRoRQxoPg+X/G6deH1xS61x4qXnVcSBT65Kx+W47pvr1TK2tjm4YsYx6vWcuYxuZ3LVrAnceVyVMWm5mrxqxbPVZgZXNVRzV9U1bmmJdtUmfDVmLnxoq02Y8Ib856H1M7lg7ecYUsJR+jDy/q1Q9+R8kHgUkSfFm6ihMjowbYp2pzPkN+UxqudxQWi+HVZjsrmkTrjAEKBdxTWvxMVc6Az+hohLHcNxOa4b4doMjrfjKRfsBlqRnsvI3Fa/Cn1Xx7TMfcQl5rgndsPkop39bszzkcp12FLk2xJhcIFsPmB3BDU2gKeZlxLD9VH/++tPoR8SrEk7rNXzTMSFZpcAGy9JyVy8+xiBemFersfGMfBtgEHVU1xQC1cArztEKx70533549tvb/r236/qUiUtLC40O+HLEScvhYt3xyxfunQyuMQNk831LJ1/X/74TUhzQWEiV2WibrMsLt7dcTmu2+Siu9evGAtfaxnnkML157+/HPVdcwlzr7nmwtrHuKgzJ1ykglzdcSV4VWUsHzV8mkd3X2QMiMn1O+nbH0f99lOBIcjOvBoCHNHgwqbA+K7iFeXCE5JatOh34wsJaVww9/9UXCR+5DSvcgxjXnyLqZYnnivO5sKNVQJXyt17t1zWdXgPXDdzvr7GJqgfeX9Z9vAsrh//Ip2cL5LJhU3ZSLCRfgnj17JbiIv1jfS75Hq9GuIPIxFY8ibNTysHI3kdnDnfaI3P48JpU1ztzH5ncs3j6/aOy3HdDFeNPZeiKdtuUGJZl7kI4tt1udhXeh6XBzeDirNoG1x1uENwKr+yfwM28Fpc7Pwu6I96G5dJbe9dNWtcZrH7Bo6oKqNch6vVIze6seRc5nxDc0H/uS7XKE7kuBzXrXHF7IaXmyuaEybOD8vjkjEdeey8D2s6oYVFaGWNyxZX9ec/jvoLRH/Rh5/X4eq2MQRlr1e+jcv55hsm1/ejvv4Tg/TPr/TxSlxLXFdhbq6c8yiTC3A/viU9L5fIxXYg//lyXI7r83Bl+gFYU/i25Rz7+w8y8b8zlwzo7TFX3EmexwPA+YLMFaxeFRTkqnk5xy+WivH5/kX450dG56PRsSeQB+MssED2czCcHfVAbvoCXLWc8w1WLf5QxBpZ/zxH2sgfdZc5uZ6YixYLZo7Lcd06V81aJ7qQKxawe5wYVd6NYiBVItdYqo2YjiEVsjPpFBdnkEAL/Z9jIl1DkJ3y2fJElsk3Ulv8FoX0kKKouJr9Z1Fp76mTzOVlrsMmc+m4SrWQ0FrCYbKIx8zwg8cAJ2c667xrtsGPZQ664qo2VUL8YwpX5rp5Kpe4hk+56IY1Iq30ZTvtiIIEHeaS8w3NpeS4HNcn5mI/NndEChFyJhebbET0hmqKqbjmMPCLeMJJGtd0Rea+J+vpMVeIoEF8OULitMWlzW1lKeXB3E/j2vDfhhA6pAh6S9USuMaI2mtj/DDALK760wspFPX0QnB1hvujNmsEF1qJ7srPxgrUtJ7TYHqG+GpQQTWT+ASi1aZTPuIYSsNxZnLVYe5lzqMPLmzpHHz6bm9dSjZXfPLRTknrUjfWxLhFcb2OeFNuLiD0ResRF3SgKH3H5bg+MxdpLEtTqEjwKHuO8yg4o1DlhSv3gQ4cX5E7Ao/+gyV9WuGIg7xc0h6udzOhOtkNz+RCyabZtEqzcCM6nEs2IVS+ucYjRDSRpS8XlfjTBWtaP7yqPpwct8wPmNaiu7tBPi5q4U0bxG8zJsIAQ5+GgEeLbkpjXNe6AGWCa7MPPxeitTgvwEqfrciOdnZEu1IjzEteLqnZlFI/a1W5htlEoZoELtrD5BLHb66Zy/sYrs7UFwdWclz3ziXvr/O4hplctEOQh0sYrGpXqAAXDHe4EOojnm8p0hxP7KEyi9H52h7VYi70UNtD2mG7YS5lAhUX20Puky+nxHm5OG6566t8UB6k2PWBU6nd3xzxJy8s9ljwsYzxq1FXQ8Bk9a7JTIHBj708jqq1uUWSgwvOET/uguPBt8WXqOGj86wbJuIybg7V950cBAPFBc0os9Zb3jSXcc/dBde9nq/LuJDe5kkT8WY3VIfF5PaESxkOTKCVo/ONSyY6j1BQ/oRLTMIv43qBx0IaztfZM0mljrfh85gYXGPlFYErY05+iP2QHBW7OqbhnKNICic8BEiuYVt60y/iYvkjkTK+jk/uayqyWnF5S+X95jopyDb1aEibPKNXcuDwX4SBj7g2AzHeDkrhSnQIvnGJS/+US26Sdw+vm6+YSz0o7uLmTte57Tkux3VnXLjnQ8PAR1wcfnUsYTHq1tQmbFe1OWE35swlC3WsTS5ZqbOh54eYaLaPtev644JcC84IV1iI4ItywNmMQ9ikc9VfpPgVXty3J6lDDKtef1Y/Vj77If2jweMzqSBXiLMxVxmr6mwsZeWPiqFVR+rkfCRvytqDn7/WRd93o+qY5eCykMBl9vdi8fNyv2iddsfluD4FF9Ue0lz8YK8MfDbXLFnnEcEBgLdmLXJzcT09fk6RXN5KTnVXPi/2ZXC1+IlARVjwm7zOAetg2TLMrqKrpZ4rNZdaIFmpazOFCw2p2JroCfU8LqydOC7H5bgirpqvYvLY5mRzdeJ6oQzrfhlcE0xuVXmP3FzsnFCxe2RZK60sJ3z98BTXsHX0g1RMZ3VBrooRCJ6Xi38byBqo/nPcl2GerX28/HZ/WxAojctQbi5IrcD7j1lEzGW8X8JzXI7rF+LyZFgfyzQzMi/k2bDfhmZDeE4/wG6M48F4lRUc2GpYCGT++nKf+VIZBGJPqFxfuN8JFTXwZ3Fx4VotLnRlxBG9UWZpRefGWyOSM79bpjwuY5M1PSko3I5RooXjclx3ypWYIpibK56ud8qAMAYVAc5ca4oFbBzK5uLJqpXClJerpYI+jN2DPUL49oHcHYdfwjrW4CIpkQtD70VctYyWgj1CcSKu+O6tNdYr748LBXYd1y/H1b0SlzHtysvVPJeLHg1m2y7FJl/CNYFJM8+XmhoiJo8zTfaUXFMxzGerT7F4I7h5inrkOc7ciNUoyqWeUxTXSRzfg1iv44U6XciPm+Cihehgjpi8Ey66sYx82MJcijHW3Lukn60zRYNWE0gL5pyCleNyXL8AF78c0IhR1p0Sy2RvgmmKAtTgy5jSkptnRT7DweEhJ648rsiJobrNGeHaU2HWQ4UUli/X9Q47is5YIZcw2dmxwbsJlvgu9xw/hatmOCw2s7j/qL6Kc6kHj4hrQeF/TXA9US5OhbNSraUG9k4hD7GP4JthGVyA01yWd93igjQXZU40q8yFiQZnEad4E/s41/yWCcfluG6RS9llNZ3L5ponmnvlx64x1wOFYjySadl6mVwI6+ufxdVt4w1d/EpDUVXvJP7O5EIUnmXuK6ohTmeZU4VW9aKXFC4O64vqihTkUnUdU/xsJhf+16es1RV+UKwaqadpXLy+PPj0XIbSuND6znE5rtvlaqNOHSx8zfRgU/JclIAXj757eOR3vML4yV+xs7pVFZXxqiMV6tYxQvjkd2dyDfYc3pfsmMdBGGs4p+52Vd/gVedCU/jVFGMV58Me8ImOOGqpboCyrx/NKB1xPruIC96hbeJFtNLh3pgsGO9aHrwoLpXny/Hz4KqoblBLXRVixPVtgs6dct3r+XJcKVyJGdYqgWE2DKgMXjiKqcFc+G2Ub05O3Teu4/ypl8k1myJGoIz7a7+Ji8vaqZyRjX6zgAzSQPm9R/yWy5u84BPnooSojoRieHXJ5csCeY/cmRK4LC/DjFcGZI7PADX8G3WjwYAHJOGsjtrlD8hqWWK/neBqhtKPP3+W/u7LuAx1NuAyqlmZK8AoX5M2gaDHUK6iO9BcqsrOs+yF43Jc98m1hyOiIJe2G0pwh2iuJpmjhaz2FByED3b20jjHL5rGxS8KNp4xUrj4NblG9mS9Dgc2kkMjLuy+lwGVXmUrND3Lj53CNZv3kqoWpnCN5Lh8+k+R+MLayd/WG7Kyy0hU7mtM8gLl50p8IUYal5xHmZeBxRW9LTiuM9dTHJfj+rRcUe2fRqIsrgk2wVZvrGlnGleizotzsLg6Q079S04osRrE3jNk2oz54Ula7K2aoCuuh8QjnRGXksi1p/IeafXMU4QiL1hL7mjXfQpXeUo7X3TkM7mq9EDFXDvHVZbul4uCTW2uC+6vFC7+jg7cuBoXvA37Q1z7dXKdukwxF9cXBRfbQ7Q+ROBG0YTJgpoaQ8aZKTwQcTXXgfDPR/UP0XpQRrczNY2/cKhXBpd8UHzjwoSkjG5nynHllOO6qlBPT+kyu4E2Qhm2G0Rc1PrfYzdejNcBXDS0oImtetEMR/dhU24PzGfUQTksenCCf3SnSpDxoNisfnSnSpDjui3dL5ehj+5UCZo9Pcb10Z1ycnJy+jX0f+QhUu8NmdzKAAAAAElFTkSuQmCC";
  });

  let canvas = imageToCanvas(img);

  console.log(canvas.toDataURL());

  console.log("finished");

  function imageToCanvas(img) {
    let canvas = createCanvas();
    canvas.width = img.width;
    canvas.height = img.height;
    let ctx = canvas.getContext("2d");
    ctx.fillStyle = "#ffffff";
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.drawImage(img, 0, 0, img.width, img.height);
    return canvas;
  }

})();

Your Environment

LinusU commented 6 years ago

Would absolutely love to see a PR that adds this using a small WASM module 😍

https://medium.com/@kennethrohde/on-the-fly-webp-decoding-using-wasm-and-a-service-worker-33e519d8c21e

https://github.com/kenchris/wasm-webp

josephrocca commented 6 years ago

Here's a working example using @kenchris's repo as a starting point: https://gist.github.com/josephrocca/a2ec179ef24462c7c21bd494d98a8988

And here's one with the wasm file inlined: https://gist.github.com/josephrocca/3d26325f1b76b3b10cb5e7c402c6dfd8

I'm new to all this wasm stuff so it may not be the best code, but it's a start! πŸ‘

LinusU commented 6 years ago

I might be wrong, but from what I can tell, that code uses the asm.js version instead of the WASM one.

It should probably be one binary webp.wasm file, together with some small code like this:

const fs = require('fs')
const path = require('path')

const code = fs.readFileSync(path.join(__dirname, 'webp.wasm'))
const module = new WebAssembly.Module(code)
const instance = new WebAssembly.Instance(module, {})

exports.decode = function (input) {
  // Allocate memory to hand over the input data to WASM
  const inputPointer = instance.exports.allocate(input.byteLength)
  const targetView = new Uint8Array(instance.exports.memory, inputPointer, input.byteLength)

  // Copy input data into WASM readable memory
  targetView.set(input)

  // Decode input data into
  const metadataPointer = instance.exports.decode(inputPointer, input.byteLength)

  // Free the input data in WASM land
  instance.exports.free(inputPointer)

  // Read returned metadata
  const metadata = new Uint32Array(instance.exports.memory, metadataPointer, 3)
  const [width, height, outputPointer] = metadata

  // Create an empty buffer for the resulting data
  const outputSize = (width * height * 4)
  const output = new Uint8Array(outputSize)

  // Copy decoded data from WASM memory to JS
  output.set(new Uint8Array(instance.exports.memory, outputPointer + 4, outputSize))

  // Free WASM copy of decoded data
  instance.exports.free(outputPointer)

  // Return decoded image as raw data
  return { width, height, data: output }
}

edit: added support for returning width & height

kenchris commented 6 years ago

I am definitely using WASM :-) https://github.com/kenchris/wasm-webp

https://docs.google.com/presentation/d/1fzFU_1xnltIq0yWNfY1WKUZc7VjGE0JLHLOtk09AV1g/edit?usp=sharing

LinusU commented 6 years ago

Yeah, I was referring to the Gist made by @josephrocca ☺️

I'm going to try and plug your compiled wasm into my glue code and see if I can get it working πŸ™Œ

josephrocca commented 6 years ago

The gists I posted are just rearranged versions of kenchris's code so it works in node. I don't know if the webp.c file would support the standalone stuff, but it might: https://github.com/kripken/emscripten/wiki/WebAssembly-Standalone

In any case, looking forward to testing out webp stuff with node-canvas!

LinusU commented 6 years ago

Hmm, the Emscripten shim is 30 KiB πŸ˜… would be nice to not use that.

I didn't know that you had to provide your own malloc and free πŸ€”

Going to try and write some glue using wasmception and see how it goes πŸ˜„

edit: super much work in progress: https://github.com/LinusU/js-webp

asturur commented 6 years ago

but why not the official C lib? wouldn't be more straight forward?

LinusU commented 6 years ago

The advantage would be that we could add a small .wasm file which will work in all versions of Node.js on all platforms (x86, arm, etc) without the end user having to compile anything. Seeing how many installation problems we have now, I would love to see this for gif, jpeg, etc. as well, potentially even cairo & pango.

It is the official C lib that I'm compiling btw. ☺️ so it should support all webp files and behave exactly as the normal lib

asturur commented 6 years ago

can you share some compilation detail? i need the webp muxer and encoder!

kenchris commented 6 years ago

Check this out: https://medium.com/@kennethrohde/on-the-fly-webp-decoding-using-wasm-and-a-service-worker-33e519d8c21e

zbjornson commented 6 years ago

Once the WASM version is working, I'd love to benchmark it against the C version. I'm happy to make the binding for that.

kenchris commented 6 years ago

Remember, wasm will be faster with time. Also future versions will support SIMD and threads, which emscripten then need to build for

asturur commented 6 years ago

Also that would make node canvas browserifiable? i mean transitioning all C to wasm. ( cairo included )

zbjornson commented 6 years ago

@asturur I suppose, but it seems redundant given that node-canvas exists to emulate the Canvas API that's already in browsers.

asturur commented 6 years ago

well i would never do that. But for people really caring about the same output OR just because till now to people that asked me how to browserify canvas i said that it could not be done.

I was just exploring.

LinusU commented 6 years ago

Okay, a status update, I'm soooo close to getting it working. It can decode some webp files currently, but I'm running into problems with YUV-images. To be honest I'm not exactly sure where the problem lies, but I've filed a bug report on libwebp here:

https://bugs.chromium.org/p/webp/issues/detail?id=403

The current code can be found here:

https://github.com/LinusU/cwasm-webp

I have published the first version of it on npm πŸŽ‰

Currently, only Node.js is supported, but browser support should be easy to add. Just want to research how to best do the loading. Also, only decoding is supported, but it should be trivial to add encoding as well.

I would absolutely love some help in tracking down the memory out of bounds issue ❀️

LinusU commented 4 years ago

Update! During the weekend I wrote a Node.js compatible Image class with support for png, gif, bmp, jpeg & webp!

It uses WebAssembly to decode all the image formats πŸŽ‰

@canvas/image - https://github.com/node-gfx/image

Should be trivial to start using that Image class here, started trying it out but ran out of time, hope to look at it soon again :)

chearon commented 4 years ago

Amazing work!! Looks like you were able to get different C libs compiled with the latest LLVM's WASM support?

I've actually been experimenting with this too. In an unrelated project, I'm working on a JS implementation of (parts of) Pango. It also relies on some WebAssembly-compiled C libraries. If we were to use it in node-canvas that would be another native dependency gone, plus it would get us perfect font matching.

It would be interesting to experiment with a fork that only uses Cairo/pixman. I'm going to bet though that performance will suffer enough that we might still need to maintain the native version somehow πŸ˜‘

LinusU commented 4 years ago

Looks like you were able to get different C libs compiled with the latest LLVM's WASM support?

Yeah πŸ™Œ the WASI SDK made it really easy to get going!

I was thinking of trying out to do the same with Cairo and see if I could get a nice @cwasm/cairo with a somewhat nice, but still low level, api

I don't know if there is a way to link a bunch of different wasm files together into one file, it might be expensive to go thru JavaScript every time the different part needs to talk to each other (on the other hand, maybe it doesn't matter that much, since it will probably only be when loading in new images πŸ€”)

It's going to be very interesting to see how the performance will be. Even if it's not stellar, it would be cool if we could have the natively compiled parts as an optionalDependency so that npm will try to install it, but if it fails everything will still work, just not as fast.

Also, potentially it's faster to call between JavaScript and WASM then JavaScript and the C++ functions. I don't have any data to back this up 😁, but I think that JS and WASM can be JIT compiled into the same VM, and then the calls are very cheap. This could potentially be a win when calling into Cairo which is mostly calling functions with a few integers and it does a lot of pixel manipulation internally. Anyhow, the only way to find out is to try πŸ˜„

Jytesh commented 3 years ago

Is this issue fixed, how do I import remote webp images from urls to node canvas to use the ctx.drawImage(webp_image) function

Xetera commented 3 years ago

The @canvas/image package doesn't work for me as the libraries I'm using, namely face-api.js have their own image instanceof Image prototype checks that fail when passing in the custom Image class over the canvas image. It's very frustrating to have spent all this time converting images to webp only to find that I probably need to convert them back to jpg before I can do anything with them in node.

LinusU commented 3 years ago

@Xetera if they are doing instanceof Image than it probably wouldn't work with the Image from node-canvas either? πŸ€” It seems like that library is intended to be run inside a browser?

Xetera commented 3 years ago

After playing around with it for a bit I realized face-api.js has a monkeyPatch function that allows me to pass a custom Image implementation but node-canvas still doesn't seem to like that very much. It fails with TypeError: Image or Canvas expected. I feel like this has something to do with the check in

if (Nan::New(Image::constructor)->HasInstance(obj)) { ... }

Where node-canvas internally relies on its own definition of Image. This behavior is reproducible with just node-canvas and no external libraries using

const imageResponse = await axios.get(image.rawUrl, {
  responseType: "arraybuffer",
});
const ca = canvas.createCanvas(500, 500);
const ctx = ca.getContext("2d");
const imageElem = await imageFromBuffer(imageResponse.data);
ctx.drawImage(imageElem, 0, 0, image.width, image.height);

I wanted to keep the conversation related to the solution here but I'm happy with moving over to the @canvas/image repo if you like

EDIT: I tried patching the node-canvas instance check to see if that's the issue but I didn't have much luck. I either get a black canvas output or runtime errors.

EDIT2: I was able to solve my issue by combining @canvas/image with @tensorflow/tfjs-node without canvas although I don't know how useful that solution might be for others. Check out the linked issue for more context if relevant.

ringcrl commented 2 years ago

After playing around with it for a bit I realized face-api.js has a monkeyPatch function that allows me to pass a custom Image implementation but node-canvas still doesn't seem to like that very much. It fails with TypeError: Image or Canvas expected. I feel like this has something to do with the check in

if (Nan::New(Image::constructor)->HasInstance(obj)) { ... }

Where node-canvas internally relies on its own definition of Image. This behavior is reproducible with just node-canvas and no external libraries using

const imageResponse = await axios.get(image.rawUrl, {
  responseType: "arraybuffer",
});
const ca = canvas.createCanvas(500, 500);
const ctx = ca.getContext("2d");
const imageElem = await imageFromBuffer(imageResponse.data);
ctx.drawImage(imageElem, 0, 0, image.width, image.height);

I wanted to keep the conversation related to the solution here but I'm happy with moving over to the @canvas/image repo if you like

EDIT: I tried patching the node-canvas instance check to see if that's the issue but I didn't have much luck. I either get a black canvas output or runtime errors.

EDIT2: I was able to solve my issue by combining @canvas/image with @tensorflow/tfjs-node without canvas although I don't know how useful that solution might be for others. Check out the linked issue for more context if relevant.

The same issue, I can convert webp to png before I use it on node-canvas, It's an extraordinary and inefficient way...

LegendaryEmoji commented 1 year ago

For those who want to decode WebP images so they can use them in their canvas. Here you go:

const webp = require("@cwasm/webp");

const source = fs.readFileSync("./image.webp");

// Decoding the WebP file and putting it on the canvas.

const image = webp.decode(source);
const imageData = ctx.createImageData(image.width, image.height);
imageData.data.set(image.data);

ctx.putImageData(imageData, 0, 0);

// Converting the canvas to buffer and saving it.

writeFileSync("./result.png", canvas.toBuffer());

This is a synchronous solution (Bundlephobia: @cwasm/webp).