diff --git a/src/CanvasRenderingContext2d.cc b/src/CanvasRenderingContext2d.cc index 83a80a7..964c229 100644 --- a/src/CanvasRenderingContext2d.cc +++ b/src/CanvasRenderingContext2d.cc @@ -10,6 +10,7 @@ #include #include "Canvas.h" #include "Image.h" +#include "ImageData.h" #include "CanvasRenderingContext2d.h" #include "CanvasGradient.h" @@ -65,6 +66,7 @@ Context2d::Initialize(Handle target) { // Prototype Local proto = t->PrototypeTemplate(); NODE_SET_PROTOTYPE_METHOD(t, "drawImage", DrawImage); + NODE_SET_PROTOTYPE_METHOD(t, "putImageData", PutImageData); NODE_SET_PROTOTYPE_METHOD(t, "save", Save); NODE_SET_PROTOTYPE_METHOD(t, "restore", Restore); NODE_SET_PROTOTYPE_METHOD(t, "rotate", Rotate); @@ -381,6 +383,92 @@ Context2d::New(const Arguments &args) { return args.This(); } +/* + * Put image data. + * + * - imageData, dx, dy + * - imageData, dx, dy, sx, sy, dw, dh + * + */ + +Handle +Context2d::PutImageData(const Arguments &args) { + HandleScope scope; + // TODO: validate + Context2d *context = ObjectWrap::Unwrap(args.This()); + ImageData *imageData = ObjectWrap::Unwrap(args[0]->ToObject()); + PixelArray *arr = imageData->pixelArray(); + + uint8_t *src = arr->data(); + uint8_t *dst = context->canvas()->data(); + + int srcStride = arr->stride() + , dstStride = context->canvas()->stride(); + + int sx = 0 + , sy = 0 + , sw = 0 + , sh = 0 + , dx = args[1]->NumberValue() + , dy = args[2]->NumberValue() + , rows + , cols; + + // TODO: spec boundaries + switch (args.Length()) { + // imageData, dx, dy + case 3: + cols = arr->width(); + rows = arr->height(); + break; + // imageData, dx, dy, sx, sy, dw, dh + case 7: { + sx = args[3]->NumberValue(); + sy = args[4]->NumberValue(); + sw = args[5]->NumberValue(); + sh = args[6]->NumberValue(); + cols = sw; + rows = sh; + dx += sx; + dy += sy; + } + break; + default: + return ThrowException(Exception::Error(String::New("invalid arguments"))); + } + + uint8_t *srcRows = src + sy * srcStride + sx * 4; + for (int y = 0; y < rows; ++y) { + uint32_t *row = (uint32_t *)(dst + dstStride * (y + dy)); + 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]; + + // ARGB + *pixel = a << 24 + | r << 16 + | g << 8 + | b; + } + srcRows += srcStride; + } + + cairo_surface_mark_dirty_rectangle( + context->canvas()->surface() + , dx + , dy + , cols + , rows); + + return Undefined(); +} + /* * Draw image src image to the destination (context). * diff --git a/src/CanvasRenderingContext2d.h b/src/CanvasRenderingContext2d.h index f8f413b..b702301 100644 --- a/src/CanvasRenderingContext2d.h +++ b/src/CanvasRenderingContext2d.h @@ -48,6 +48,7 @@ class Context2d: public node::ObjectWrap { static void Initialize(Handle target); static Handle New(const Arguments &args); static Handle DrawImage(const Arguments &args); + static Handle PutImageData(const Arguments &args); static Handle Save(const Arguments &args); static Handle Restore(const Arguments &args); static Handle Rotate(const Arguments &args); diff --git a/src/PixelArray.cc b/src/PixelArray.cc index 1f9ad7a..528ac98 100644 --- a/src/PixelArray.cc +++ b/src/PixelArray.cc @@ -85,23 +85,36 @@ PixelArray::PixelArray(Canvas *canvas, int sx, int sy, int width, int height): // Alloc space for our new data uint8_t *dst = alloc(); uint8_t *src = canvas->data(); - int s = stride(); + int srcStride = canvas->stride() + , dstStride = stride(); // Normalize data (argb -> rgba) for (int y = 0; y < height; ++y) { - uint32_t *row = (uint32_t *)(src + s * y); + uint32_t *row = (uint32_t *)(src + srcStride * (y + sy)); for (int x = 0; x < width; ++x) { int bx = x * 4; - uint32_t *pixel = row + x; + uint32_t *pixel = row + x + sx; - // premultiplied + // ARGB uint8_t a = *pixel >> 24; + uint8_t r = *pixel >> 16; + uint8_t g = *pixel >> 8; + uint8_t b = *pixel; + + // undo premultiplication dst[bx + 3] = a; - dst[bx + 0] = (*pixel >> 16) * 255 / a; - dst[bx + 1] = (*pixel >> 8) * 255 / a; - dst[bx + 2] = *pixel * 255 / a; + // TODO: abstract + if (a) { + dst[bx + 0] = r * 255 / a; + dst[bx + 1] = g * 255 / a; + dst[bx + 2] = b * 255 / a; + } else { + dst[bx + 0] = r; + dst[bx + 1] = g; + dst[bx + 2] = b; + } } - dst += s; + dst += dstStride; } } diff --git a/test/public/tests.js b/test/public/tests.js index 9803589..cc6cd41 100644 --- a/test/public/tests.js +++ b/test/public/tests.js @@ -1440,4 +1440,79 @@ tests['drawImage(img,sx,sy,sw,sh,x,y,w,h)'] = function(ctx, done){ done(); }; img.src = 'state.png'; +}; + +tests['putImageData()'] = function(ctx){ + for (i=0;i<6;i++){ + for (j=0;j<6;j++){ + ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' + + Math.floor(255-42.5*j) + ',0)'; + ctx.fillRect(j*25,i*25,25,25); + } + } + var data = ctx.getImageData(0,0,50,50); + ctx.putImageData(data,10,10); +}; + +tests['putImageData() 2'] = function(ctx){ + for (i=0;i<6;i++){ + for (j=0;j<6;j++){ + ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' + + Math.floor(255-42.5*j) + ',0)'; + ctx.fillRect(j*25,i*25,25,25); + } + } + var data = ctx.getImageData(25,25,50,50); + ctx.putImageData(data,10,10); +}; + +tests['putImageData() 3'] = function(ctx){ + for (i=0;i<6;i++){ + for (j=0;j<6;j++){ + ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' + + Math.floor(255-42.5*j) + ',0)'; + ctx.fillRect(j*25,i*25,25,25); + } + } + var data = ctx.getImageData(10,25,10,50); + ctx.putImageData(data,50,10); +}; + +tests['putImageData() 4'] = function(ctx){ + for (i=0;i<6;i++){ + for (j=0;j<6;j++){ + ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' + + Math.floor(255-42.5*j) + ',0)'; + ctx.fillRect(j*25,i*25,25,25); + } + } + ctx.strokeRect(30,30,30,30); + var data = ctx.getImageData(0,0,50,50); + ctx.putImageData(data,30,30,10,10,30,30); +}; + +tests['putImageData() 5'] = function(ctx){ + for (i=0;i<6;i++){ + for (j=0;j<6;j++){ + ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' + + Math.floor(255-42.5*j) + ',0)'; + ctx.fillRect(j*25,i*25,25,25); + } + } + ctx.strokeRect(60,60,50,30); + var data = ctx.getImageData(0,0,50,50); + ctx.putImageData(data,60,60,0,0,50,30); +}; + +tests['putImageData() 6'] = function(ctx){ + for (i=0;i<6;i++){ + for (j=0;j<6;j++){ + ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' + + Math.floor(255-42.5*j) + ',0)'; + ctx.fillRect(j*25,i*25,25,25); + } + } + ctx.strokeRect(60,60,50,30); + var data = ctx.getImageData(0,0,50,50); + ctx.putImageData(data,60,60,10,0,35,30); }; \ No newline at end of file