@@ -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,95 +1052,98 @@ Context2d::GetImageData(const Napi::CallbackInfo& info) {
10471052
10481053 uint8_t *dst = (uint8_t *)buffer.Data ();
10491054
1050- switch (canvas->backend ()->getFormat ()) {
1051- case CAIRO_FORMAT_ARGB32: {
1052- // Rearrange alpha (argb -> rgba), undo alpha pre-multiplication,
1053- // and store in big-endian format
1054- for (int y = 0 ; y < sh; ++y) {
1055- uint32_t *row = (uint32_t *)(src + srcStride * (y + sy));
1056- for (int x = 0 ; x < sw; ++x) {
1057- int bx = x * 4 ;
1058- uint32_t *pixel = row + x + sx;
1059- uint8_t a = *pixel >> 24 ;
1060- uint8_t r = *pixel >> 16 ;
1061- uint8_t g = *pixel >> 8 ;
1062- uint8_t b = *pixel;
1063- dst[bx + 3 ] = a;
1064-
1065- // Performance optimization: fully transparent/opaque pixels can be
1066- // processed more efficiently.
1067- if (a == 0 || a == 255 ) {
1055+ if (cw > 0 && ch > 0 ) {
1056+ switch (canvas->backend ()->getFormat ()) {
1057+ case CAIRO_FORMAT_ARGB32: {
1058+ dst += oy * dstStride + ox * 4 ;
1059+ // Rearrange alpha (argb -> rgba), undo alpha pre-multiplication,
1060+ // and store in big-endian format
1061+ for (int y = 0 ; y < ch; ++y) {
1062+ uint32_t *row = (uint32_t *)(src + srcStride * (y + sy));
1063+ for (int x = 0 ; x < cw; ++x) {
1064+ int bx = x * 4 ;
1065+ uint32_t *pixel = row + x + sx;
1066+ uint8_t a = *pixel >> 24 ;
1067+ uint8_t r = *pixel >> 16 ;
1068+ uint8_t g = *pixel >> 8 ;
1069+ uint8_t b = *pixel;
1070+ dst[bx + 3 ] = a;
1071+
1072+ // Performance optimization: fully transparent/opaque pixels can be
1073+ // processed more efficiently.
1074+ if (a == 0 || a == 255 ) {
1075+ dst[bx + 0 ] = r;
1076+ dst[bx + 1 ] = g;
1077+ dst[bx + 2 ] = b;
1078+ } else {
1079+ // Undo alpha pre-multiplication
1080+ float alphaR = (float )255 / a;
1081+ dst[bx + 0 ] = (int )((float )r * alphaR);
1082+ dst[bx + 1 ] = (int )((float )g * alphaR);
1083+ dst[bx + 2 ] = (int )((float )b * alphaR);
1084+ }
1085+ }
1086+ dst += dstStride;
1087+ }
1088+ break ;
1089+ }
1090+ case CAIRO_FORMAT_RGB24: {
1091+ dst += oy * dstStride + ox * 4 ;
1092+ // Rearrange alpha (argb -> rgba) and store in big-endian format
1093+ for (int y = 0 ; y < ch; ++y) {
1094+ uint32_t *row = (uint32_t *)(src + srcStride * (y + sy));
1095+ for (int x = 0 ; x < cw; ++x) {
1096+ int bx = x * 4 ;
1097+ uint32_t *pixel = row + x + sx;
1098+ uint8_t r = *pixel >> 16 ;
1099+ uint8_t g = *pixel >> 8 ;
1100+ uint8_t b = *pixel;
1101+
10681102 dst[bx + 0 ] = r;
10691103 dst[bx + 1 ] = g;
10701104 dst[bx + 2 ] = b;
1071- } else {
1072- // Undo alpha pre-multiplication
1073- float alphaR = (float )255 / a;
1074- dst[bx + 0 ] = (int )((float )r * alphaR);
1075- dst[bx + 1 ] = (int )((float )g * alphaR);
1076- dst[bx + 2 ] = (int )((float )b * alphaR);
1105+ dst[bx + 3 ] = 255 ;
10771106 }
1078-
1107+ dst += dstStride;
10791108 }
1080- dst += dstStride;
1081- }
1082- break ;
1083- }
1084- case CAIRO_FORMAT_RGB24: {
1085- // Rearrange alpha (argb -> rgba) and store in big-endian format
1086- for (int y = 0 ; y < sh; ++y) {
1087- uint32_t *row = (uint32_t *)(src + srcStride * (y + sy));
1088- for (int x = 0 ; x < sw; ++x) {
1089- int bx = x * 4 ;
1090- uint32_t *pixel = row + x + sx;
1091- uint8_t r = *pixel >> 16 ;
1092- uint8_t g = *pixel >> 8 ;
1093- uint8_t b = *pixel;
1094-
1095- dst[bx + 0 ] = r;
1096- dst[bx + 1 ] = g;
1097- dst[bx + 2 ] = b;
1098- dst[bx + 3 ] = 255 ;
1109+ break ;
10991110 }
1100- dst += dstStride;
1111+ case CAIRO_FORMAT_A8: {
1112+ dst += oy * dstStride + ox;
1113+ for (int y = 0 ; y < ch; ++y) {
1114+ uint8_t *row = (uint8_t *)(src + srcStride * (y + sy));
1115+ memcpy (dst, row + sx, cw);
1116+ dst += dstStride;
1117+ }
1118+ break ;
11011119 }
1102- break ;
1103- }
1104- case CAIRO_FORMAT_A8: {
1105- for (int y = 0 ; y < sh; ++y) {
1106- uint8_t *row = (uint8_t *)(src + srcStride * (y + sy));
1107- memcpy (dst, row + sx, dstStride);
1108- dst += dstStride;
1120+ case CAIRO_FORMAT_A1: {
1121+ // TODO Should this be totally packed, or maintain a stride divisible by 4?
1122+ Napi::Error::New (env, " getImageData for CANVAS_FORMAT_A1 is not yet implemented" ).ThrowAsJavaScriptException ();
1123+ break ;
11091124 }
1110- break ;
1111- }
1112- case CAIRO_FORMAT_A1: {
1113- // TODO Should this be totally packed, or maintain a stride divisible by 4?
1114- Napi::Error::New (env, " getImageData for CANVAS_FORMAT_A1 is not yet implemented" ).ThrowAsJavaScriptException ();
1115-
1116- break ;
1117- }
1118- case CAIRO_FORMAT_RGB16_565: {
1119- for (int y = 0 ; y < sh; ++y) {
1120- uint16_t *row = (uint16_t *)(src + srcStride * (y + sy));
1121- memcpy (dst, row + sx, dstStride);
1122- dst += dstStride;
1125+ case CAIRO_FORMAT_RGB16_565: {
1126+ dst += oy * dstStride + ox * 2 ;
1127+ for (int y = 0 ; y < ch; ++y) {
1128+ uint16_t *row = (uint16_t *)(src + srcStride * (y + sy));
1129+ memcpy (dst, row + sx, cw * 2 );
1130+ dst += dstStride;
1131+ }
1132+ break ;
11231133 }
1124- break ;
1125- }
11261134#ifdef CAIRO_FORMAT_RGB30
1127- case CAIRO_FORMAT_RGB30: {
1135+ case CAIRO_FORMAT_RGB30: {
11281136 // TODO
11291137 Napi::Error::New (env, " getImageData for CANVAS_FORMAT_RGB30 is not yet implemented" ).ThrowAsJavaScriptException ();
1130-
1131- break ;
1132- }
1138+ break ;
1139+ }
11331140#endif
1134- default : {
1135- // Unlikely
1136- Napi::Error::New (env, " Invalid pixel format or not an image canvas" ).ThrowAsJavaScriptException ();
1137- return env.Null ();
1138- }
1141+ default : {
1142+ // Unlikely
1143+ Napi::Error::New (env, " Invalid pixel format or not an image canvas" ).ThrowAsJavaScriptException ();
1144+ return ;
1145+ }
1146+ }
11391147 }
11401148
11411149 Napi::Number swHandle = Napi::Number::New (env, sw);
0 commit comments