Skip to content

Commit

Permalink
getImageData fixes when rectangle is outside of canvas
Browse files Browse the repository at this point in the history
fix a crash in getImageData if the rectangle is outside the canvas

return transparent black pixels when getting image data outside the canvas

remove dead code, add comments

Fixes #2024
Fixes #1849
  • Loading branch information
Philippe Plantier authored and chearon committed Jan 11, 2025
1 parent da33bbe commit a0c8031
Show file tree
Hide file tree
Showing 4 changed files with 533 additions and 19 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ project adheres to [Semantic Versioning](http://semver.org/).
* Support for accessibility and links in PDFs

### Fixed
* Fix a crash in `getImageData` when the rectangle is entirely outside the canvas. ([#2024](https://github.com/Automattic/node-canvas/issues/2024))
* Fix `getImageData` cropping the resulting `ImageData` when the given rectangle is partly outside the canvas. ([#1849](https://github.com/Automattic/node-canvas/issues/1849))

3.0.1
==================
Expand Down
50 changes: 31 additions & 19 deletions src/CanvasRenderingContext2d.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1011,21 +1011,26 @@ Context2d::GetImageData(const Napi::CallbackInfo& info) {
sh = -sh;
}

if (sx + sw > width) sw = width - sx;
if (sy + sh > height) sh = height - sy;

// WebKit/moz functionality. node-canvas used to return in either case.
if (sw <= 0) sw = 1;
if (sh <= 0) sh = 1;

// Non-compliant. "Pixels outside the canvas must be returned as transparent
// black." This instead clips the returned array to the canvas area.
// Width and height to actually copy
int cw = sw;
int ch = sh;
// Offsets in the destination image
int ox = 0;
int oy = 0;

// Clamp the copy width and height if the copy would go outside the image
if (sx + sw > width) cw = width - sx;
if (sy + sh > height) ch = height - sy;

// Clamp the copy origin if the copy would go outside the image
if (sx < 0) {
sw += sx;
ox = -sx;
cw += sx;
sx = 0;
}
if (sy < 0) {
sh += sy;
oy = -sy;
ch += sy;
sy = 0;
}

Expand All @@ -1047,13 +1052,16 @@ Context2d::GetImageData(const Napi::CallbackInfo& info) {

uint8_t *dst = (uint8_t *)buffer.Data();

if (!(cw > 0 && ch > 0)) goto return_empty;

switch (canvas->backend()->getFormat()) {
case CAIRO_FORMAT_ARGB32: {
dst += oy * dstStride + ox * 4;
// Rearrange alpha (argb -> rgba), undo alpha pre-multiplication,
// and store in big-endian format
for (int y = 0; y < sh; ++y) {
for (int y = 0; y < ch; ++y) {
uint32_t *row = (uint32_t *)(src + srcStride * (y + sy));
for (int x = 0; x < sw; ++x) {
for (int x = 0; x < cw; ++x) {
int bx = x * 4;
uint32_t *pixel = row + x + sx;
uint8_t a = *pixel >> 24;
Expand Down Expand Up @@ -1082,10 +1090,11 @@ Context2d::GetImageData(const Napi::CallbackInfo& info) {
break;
}
case CAIRO_FORMAT_RGB24: {
dst += oy * dstStride + ox * 4;
// Rearrange alpha (argb -> rgba) and store in big-endian format
for (int y = 0; y < sh; ++y) {
for (int y = 0; y < ch; ++y) {
uint32_t *row = (uint32_t *)(src + srcStride * (y + sy));
for (int x = 0; x < sw; ++x) {
for (int x = 0; x < cw; ++x) {
int bx = x * 4;
uint32_t *pixel = row + x + sx;
uint8_t r = *pixel >> 16;
Expand All @@ -1102,9 +1111,10 @@ Context2d::GetImageData(const Napi::CallbackInfo& info) {
break;
}
case CAIRO_FORMAT_A8: {
for (int y = 0; y < sh; ++y) {
dst += oy * dstStride + ox;
for (int y = 0; y < ch; ++y) {
uint8_t *row = (uint8_t *)(src + srcStride * (y + sy));
memcpy(dst, row + sx, dstStride);
memcpy(dst, row + sx, cw);
dst += dstStride;
}
break;
Expand All @@ -1116,9 +1126,10 @@ Context2d::GetImageData(const Napi::CallbackInfo& info) {
break;
}
case CAIRO_FORMAT_RGB16_565: {
for (int y = 0; y < sh; ++y) {
dst += oy * dstStride + ox * 2;
for (int y = 0; y < ch; ++y) {
uint16_t *row = (uint16_t *)(src + srcStride * (y + sy));
memcpy(dst, row + sx, dstStride);
memcpy(dst, row + sx, cw * 2);
dst += dstStride;
}
break;
Expand All @@ -1138,6 +1149,7 @@ Context2d::GetImageData(const Napi::CallbackInfo& info) {
}
}

return_empty:
Napi::Number swHandle = Napi::Number::New(env, sw);
Napi::Number shHandle = Napi::Number::New(env, sh);
Napi::Function ctor = env.GetInstanceData<InstanceData>()->ImageDataCtor.Value();
Expand Down
Loading

0 comments on commit a0c8031

Please sign in to comment.