Brooooooklyn / canvas

High performance skia binding to Node.js. Zero system dependencies and pure npm packages without any postinstall scripts nor node-gyp.
https://vercel.skia.rs
MIT License
1.76k stars 77 forks source link

Image src is null after loading in 0.1.54 #882

Closed gomander closed 1 month ago

gomander commented 2 months ago

After upgrading to 0.1.54, I can't get images to draw.

app.get('/test-image', (_, res) => {
  const canvas = createCanvas(4, 4)
  const context = canvas.getContext('2d')
  const testImage = new Image()
  testImage.src = Buffer.from('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAIAAAAmkwkpAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAA1SURBVBhXY/j//z8DAwMQ/88C0Yo+/xm2/Wf4fwPEmQCUZZjyn0ENKMCQ95/hziaG/x4M/wFfsBfivq1KhgAAAABJRU5ErkJggg==')
  function drawImage() {
    console.log('Image loaded', testImage.width, testImage.height, testImage.src)
    context.drawImage(testImage, 0, 0)
    const buffer = canvas.toBuffer('image/png')
    res.setHeader('Content-Type', 'image/png')
    res.setHeader('Content-Disposition', 'inline')
    console.log(`Returning an image of ${buffer.byteLength} bytes.`)
    res.send(buffer)
  }
  // 0.1.54
  testImage.onload = () => { // onload isn't called in 0.1.53, but is in 0.1.54
    drawImage() // prints "Image loaded 4 4 null"
  }
  // 0.1.53
  // drawImage() // prints "Image loaded 4 4 <Buffer ...>"
})

This code correctly draws testImage in 0.1.53 and sends the correct image in the response. In 0.1.54, however, the image that is sent in the response is always just blank.

I think this has something to do with the src not being set correctly in 0.1.54. The console.log in the drawImage function prints "Image loaded 4 4 null" in 0.1.54, but in 0.1.53 it prints "Image loaded 4 4 <Buffer 64 61 74 ...>"

It's very possible that I'm doing something wrong, but I'm unable to figure out why I can't get the image source to stick.

leonlarsson commented 1 month ago

There might still be some lingering issues. I just upgraded to 0.1.56

const qr64Buffer = await QRCode.toBuffer("Hello", {
  margin: 1,
  scale: 10,
  color: {
    light: "#1c1111",
    dark: "#5e5c5c",
  },
});

const qrImg = new Canvas.Image();
qrImg.src = qr64Buffer;

console.log(qr64Buffer, qrImg.src);
// <Buffer 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 00 e6 00 00 00 e6 08 06 00 00 00 c1 6a f9 bb 00 00 00 02 49 44 41 54 78 01 ec 1a 7e d2 00 00 04 ... 1264 more bytes> null
gomander commented 1 month ago

@leonlarsson In 0.1.55 onwards, you must wait for the image to load before trying to perform operations on or with it. The easiest way to do this is to use const qrImg = await loadImage(qr64Buffer) instead of the two lines you have there that create the image. The loadImage function can be imported the same way the Canvas and Image classes are.

Another option is to add the line await new Promise(r => qrImg.onload = r) after setting the src.

I hope this helps.

leonlarsson commented 1 month ago

@gomander Ahh, thank you. I'll check that out later