Automattic / Automattic/node-canvas

Rendering errors when drawImage width or height exceeds 32766

Open
#2,194 2 comments 0 reactions 0 assignees View on GitHub
Bug
Dominant language
JavaScript
Stars
10.7k
Forks
1.2k
Avg merge
4d 8h
Merged PRs (30d)
1

Description

## Issue

I tracked down a user issue in a software of mine where node-canvas did not draw anything while a standard browser Canvas implementation had no problem rendering the same scene. It was a large scene but I was able to track it down to a single drawImage call with a width larger than 32768. The canvas itself is pretty small but the intention of the user was to scale up the image drawn to it to only see a small part in the middle of it, so this resulted in a transformation matrix with scale factor 20 which together with the canvas size and the image size resulted in an actual image width of >=32768.

I simplified the example code so there isn't even a transformation taking place. Just a drawImage with a large width. The same happens when using a small width but using a scale transformation.

## Steps to Reproduce
```js
import { writeFile } from "node:fs/promises";
import { createCanvas, loadImage } from "canvas";

// Image of a green pixel. Loaded from data URL for convenience of this demo.
// Content doesn't matter, can even be a large JPEG file, same result
const green = await loadImage("data:image/gif;base64,R0lGODdhAQABAIABAAD/AP///ywAAAAAAQABAAACAkQBADs=");

// Create 100x100 Canvas and fill it with red color (So when result is red, there is something going wrong)
const canvas = createCanvas(100, 100);
const ctx = canvas.getContext("2d");
ctx.fillStyle = "red";
ctx.fillRect(0, 0, 100, 100);

// The stretch width of the drawn image:
// With width 32766 everything is rendered correctly
// With width 32767 the drawImage doesn't draw the image anymore
// With width 32768 even stuff after the drawImage is not rendered anymore. The canvas is dead.
const width = 32768;

// Stretch green pixel image over the canvas
ctx.drawImage(green, 0, 0, width, 100);

// Draw a black cross. Will not be rendered when previous image width is larger than 32767
ctx.moveTo(0, 0);
ctx.lineTo(100, 100);
ctx.moveTo(0, 100);
ctx.lineTo(100, 0);
ctx.lineWidth = 3;
ctx.strokeStyle = "#000";
ctx.stroke();

await writeFile("/tmp/out.png", canvas.toBuffer("image/png"));
```

The expected result is always this (And node-canvas renders it correctly with `width` variable set up to 32766:

![image](https://user-images.githubusercontent.com/580493/217862414-dc800250-aefe-40e1-a8d2-9275bb93e964.png)

With `width` variable set to 32767 the result is this instead (drawImage does nothing):

![image](https://user-images.githubusercontent.com/580493/217862921-a0476409-4ca7-47db-9ee8-05368c3ed76a.png)

With `width` variable set to 32768 or higher the result is this (Nothing is rendered at all anymore):

![image](https://user-images.githubusercontent.com/580493/217863093-1b9fed6b-b23e-4be6-8ba4-1a5ab8aca24d.png)

The number 32768 suggests that it has something to do with a signed 16 bit integer data type somewhere in the code which should be 32/64 bit integer or even a float/double maybe?

## Your Environment
* Node Canvas v2.11.0 (Also tried older v2.10 and master branch)
* Node v18.14.0
* Debian Linux 11

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.