Skip to content

Commit 04fefa4

Browse files
Philippe Plantierchearon
authored andcommitted
getImageData fixes when rectangle is outside of canvas
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
1 parent da33bbe commit 04fefa4

File tree

4 files changed

+533
-19
lines changed

4 files changed

+533
-19
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ project adheres to [Semantic Versioning](http://semver.org/).
1515
* Support for accessibility and links in PDFs
1616

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

1921
3.0.1
2022
==================

src/CanvasRenderingContext2d.cc

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,21 +1011,26 @@ Context2d::GetImageData(const Napi::CallbackInfo& info) {
10111011
sh = -sh;
10121012
}
10131013

1014-
if (sx + sw > width) sw = width - sx;
1015-
if (sy + sh > height) sh = height - sy;
1016-
1017-
// WebKit/moz functionality. node-canvas used to return in either case.
1018-
if (sw <= 0) sw = 1;
1019-
if (sh <= 0) sh = 1;
1020-
1021-
// Non-compliant. "Pixels outside the canvas must be returned as transparent
1022-
// black." This instead clips the returned array to the canvas area.
1014+
// Width and height to actually copy
1015+
int cw = sw;
1016+
int ch = sh;
1017+
// Offsets in the destination image
1018+
int ox = 0;
1019+
int oy = 0;
1020+
1021+
// Clamp the copy width and height if the copy would go outside the image
1022+
if (sx + sw > width) cw = width - sx;
1023+
if (sy + sh > height) ch = height - sy;
1024+
1025+
// Clamp the copy origin if the copy would go outside the image
10231026
if (sx < 0) {
1024-
sw += sx;
1027+
ox = -sx;
1028+
cw += sx;
10251029
sx = 0;
10261030
}
10271031
if (sy < 0) {
1028-
sh += sy;
1032+
oy = -sy;
1033+
ch += sy;
10291034
sy = 0;
10301035
}
10311036

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

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

1055+
if (!(cw > 0 && ch > 0)) goto return_empty;
1056+
10501057
switch (canvas->backend()->getFormat()) {
10511058
case CAIRO_FORMAT_ARGB32: {
1059+
dst += oy * dstStride + ox * 4;
10521060
// Rearrange alpha (argb -> rgba), undo alpha pre-multiplication,
10531061
// and store in big-endian format
1054-
for (int y = 0; y < sh; ++y) {
1062+
for (int y = 0; y < ch; ++y) {
10551063
uint32_t *row = (uint32_t *)(src + srcStride * (y + sy));
1056-
for (int x = 0; x < sw; ++x) {
1064+
for (int x = 0; x < cw; ++x) {
10571065
int bx = x * 4;
10581066
uint32_t *pixel = row + x + sx;
10591067
uint8_t a = *pixel >> 24;
@@ -1082,10 +1090,11 @@ Context2d::GetImageData(const Napi::CallbackInfo& info) {
10821090
break;
10831091
}
10841092
case CAIRO_FORMAT_RGB24: {
1093+
dst += oy * dstStride + ox * 4;
10851094
// Rearrange alpha (argb -> rgba) and store in big-endian format
1086-
for (int y = 0; y < sh; ++y) {
1095+
for (int y = 0; y < ch; ++y) {
10871096
uint32_t *row = (uint32_t *)(src + srcStride * (y + sy));
1088-
for (int x = 0; x < sw; ++x) {
1097+
for (int x = 0; x < cw; ++x) {
10891098
int bx = x * 4;
10901099
uint32_t *pixel = row + x + sx;
10911100
uint8_t r = *pixel >> 16;
@@ -1102,9 +1111,10 @@ Context2d::GetImageData(const Napi::CallbackInfo& info) {
11021111
break;
11031112
}
11041113
case CAIRO_FORMAT_A8: {
1105-
for (int y = 0; y < sh; ++y) {
1114+
dst += oy * dstStride + ox;
1115+
for (int y = 0; y < ch; ++y) {
11061116
uint8_t *row = (uint8_t *)(src + srcStride * (y + sy));
1107-
memcpy(dst, row + sx, dstStride);
1117+
memcpy(dst, row + sx, cw);
11081118
dst += dstStride;
11091119
}
11101120
break;
@@ -1116,9 +1126,10 @@ Context2d::GetImageData(const Napi::CallbackInfo& info) {
11161126
break;
11171127
}
11181128
case CAIRO_FORMAT_RGB16_565: {
1119-
for (int y = 0; y < sh; ++y) {
1129+
dst += oy * dstStride + ox * 2;
1130+
for (int y = 0; y < ch; ++y) {
11201131
uint16_t *row = (uint16_t *)(src + srcStride * (y + sy));
1121-
memcpy(dst, row + sx, dstStride);
1132+
memcpy(dst, row + sx, cw * 2);
11221133
dst += dstStride;
11231134
}
11241135
break;
@@ -1138,6 +1149,7 @@ Context2d::GetImageData(const Napi::CallbackInfo& info) {
11381149
}
11391150
}
11401151

1152+
return_empty:
11411153
Napi::Number swHandle = Napi::Number::New(env, sw);
11421154
Napi::Number shHandle = Napi::Number::New(env, sh);
11431155
Napi::Function ctor = env.GetInstanceData<InstanceData>()->ImageDataCtor.Value();

0 commit comments

Comments
 (0)