From 76580a819934e71e30b1052e6bf26e6658b9ecb7 Mon Sep 17 00:00:00 2001 From: Zach Bjornson Date: Fri, 31 Jul 2015 19:44:01 -0700 Subject: [PATCH] Optimize Context2d::PutImageData. Benchmarked 53% faster. Benchmark: var canvas = new Canvas(300, 600); var ctx = canvas.getContext("2d"); // any manipulation of canvas/ctx here. var data = ctx.getImageData(0,0,300,600); // time 1000x: ctx.putImageData(data, 0, 0); --- src/CanvasRenderingContext2d.cc | 48 ++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/src/CanvasRenderingContext2d.cc b/src/CanvasRenderingContext2d.cc index 40624d4..ff57f77 100755 --- a/src/CanvasRenderingContext2d.cc +++ b/src/CanvasRenderingContext2d.cc @@ -605,12 +605,15 @@ NAN_METHOD(Context2d::PutImageData) { sy = args[4]->Int32Value(); sw = args[5]->Int32Value(); sh = args[6]->Int32Value(); + // clamp the left edge if (sx < 0) sw += sx, sx = 0; if (sy < 0) sh += sy, sy = 0; + // clamp the right edge if (sx + sw > imageData->width()) sw = imageData->width() - sx; if (sy + sh > imageData->height()) sh = imageData->height() - sy; dx += sx; dy += sy; + // clamp width at canvas size cols = std::min(sw, context->canvas()->width - dx); rows = std::min(sh, context->canvas()->height - dy); break; @@ -620,27 +623,36 @@ NAN_METHOD(Context2d::PutImageData) { if (cols <= 0 || rows <= 0) NanReturnUndefined(); - uint8_t *srcRows = src + sy * srcStride + sx * 4; + src += sy * srcStride + sx * 4; + dst += dstStride * dy + 4 * dx; for (int y = 0; y < rows; ++y) { - uint32_t *row = (uint32_t *)(dst + dstStride * (y + dy)); + uint8_t *dstRow = dst; + uint8_t *srcRow = src; for (int x = 0; x < cols; ++x) { - int bx = x * 4; - uint32_t *pixel = row + x + dx; - - // RGBA - uint8_t a = srcRows[bx + 3]; - uint8_t r = srcRows[bx + 0]; - uint8_t g = srcRows[bx + 1]; - uint8_t b = srcRows[bx + 2]; - float alpha = (float) a / 255; - - // ARGB - *pixel = a << 24 - | (int)((float) r * alpha) << 16 - | (int)((float) g * alpha) << 8 - | (int)((float) b * alpha); + // rgba + uint8_t r = *srcRow++; + uint8_t g = *srcRow++; + uint8_t b = *srcRow++; + uint8_t a = *srcRow++; + + // argb + // performance optimization: fully transparent/opaque pixels can be + // processed more efficiently. + if (a == 0 || a == 255) { + *dstRow++ = b; + *dstRow++ = g; + *dstRow++ = r; + *dstRow++ = a; + } else { + float alpha = (float)a / 255; + *dstRow++ = b * alpha; + *dstRow++ = g * alpha; + *dstRow++ = r * alpha; + *dstRow++ = a; + } } - srcRows += srcStride; + dst += dstStride; + src += srcStride; } cairo_surface_mark_dirty_rectangle(