From 2402410933b36540867c9d63679335dfe39ec5fe Mon Sep 17 00:00:00 2001 From: Philippe Plantier Date: Sat, 7 Jan 2023 18:55:08 +0100 Subject: [PATCH] return transparent black pixels when getting image data outside the canvas --- CHANGELOG.md | 1 + src/CanvasRenderingContext2d.cc | 172 ++++++------ test/canvas.test.js | 482 +++++++++++++++++++++++++++++++- test/public/tests.js | 22 ++ 4 files changed, 587 insertions(+), 90 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7a6fd23b..ed1b43c00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ project adheres to [Semantic Versioning](http://semver.org/). ### 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 ================== diff --git a/src/CanvasRenderingContext2d.cc b/src/CanvasRenderingContext2d.cc index 249f528b5..c7fc99abb 100644 --- a/src/CanvasRenderingContext2d.cc +++ b/src/CanvasRenderingContext2d.cc @@ -1011,17 +1011,22 @@ Context2d::GetImageData(const Napi::CallbackInfo& info) { sh = -sh; } - if (sx + sw > width) sw = width - sx; - if (sy + sh > height) sh = height - sy; + int cw = sw; + int ch = sh; + int ox = 0; + int oy = 0; + + if (sx + sw > width) cw = width - sx; + if (sy + sh > height) ch = height - sy; - // Non-compliant. "Pixels outside the canvas must be returned as transparent - // black." This instead clips the returned array to the canvas area. if (sx < 0) { - sw += sx; + ox = -sx; + cw += sx; sx = 0; } if (sy < 0) { - sh += sy; + oy = -sy; + ch += sy; sy = 0; } @@ -1047,95 +1052,98 @@ Context2d::GetImageData(const Napi::CallbackInfo& info) { uint8_t *dst = (uint8_t *)buffer.Data(); - switch (canvas->backend()->getFormat()) { - case CAIRO_FORMAT_ARGB32: { - // Rearrange alpha (argb -> rgba), undo alpha pre-multiplication, - // and store in big-endian format - for (int y = 0; y < sh; ++y) { - uint32_t *row = (uint32_t *)(src + srcStride * (y + sy)); - for (int x = 0; x < sw; ++x) { - int bx = x * 4; - uint32_t *pixel = row + x + sx; - uint8_t a = *pixel >> 24; - uint8_t r = *pixel >> 16; - uint8_t g = *pixel >> 8; - uint8_t b = *pixel; - dst[bx + 3] = a; - - // Performance optimization: fully transparent/opaque pixels can be - // processed more efficiently. - if (a == 0 || a == 255) { + if (cw > 0 && ch > 0) { + 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 < ch; ++y) { + uint32_t *row = (uint32_t *)(src + srcStride * (y + sy)); + for (int x = 0; x < cw; ++x) { + int bx = x * 4; + uint32_t *pixel = row + x + sx; + uint8_t a = *pixel >> 24; + uint8_t r = *pixel >> 16; + uint8_t g = *pixel >> 8; + uint8_t b = *pixel; + dst[bx + 3] = a; + + // Performance optimization: fully transparent/opaque pixels can be + // processed more efficiently. + if (a == 0 || a == 255) { + dst[bx + 0] = r; + dst[bx + 1] = g; + dst[bx + 2] = b; + } else { + // Undo alpha pre-multiplication + float alphaR = (float)255 / a; + dst[bx + 0] = (int)((float)r * alphaR); + dst[bx + 1] = (int)((float)g * alphaR); + dst[bx + 2] = (int)((float)b * alphaR); + } + } + dst += dstStride; + } + 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 < ch; ++y) { + uint32_t *row = (uint32_t *)(src + srcStride * (y + sy)); + for (int x = 0; x < cw; ++x) { + int bx = x * 4; + uint32_t *pixel = row + x + sx; + uint8_t r = *pixel >> 16; + uint8_t g = *pixel >> 8; + uint8_t b = *pixel; + dst[bx + 0] = r; dst[bx + 1] = g; dst[bx + 2] = b; - } else { - // Undo alpha pre-multiplication - float alphaR = (float)255 / a; - dst[bx + 0] = (int)((float)r * alphaR); - dst[bx + 1] = (int)((float)g * alphaR); - dst[bx + 2] = (int)((float)b * alphaR); + dst[bx + 3] = 255; } - + dst += dstStride; } - dst += dstStride; - } - break; - } - case CAIRO_FORMAT_RGB24: { - // Rearrange alpha (argb -> rgba) and store in big-endian format - for (int y = 0; y < sh; ++y) { - uint32_t *row = (uint32_t *)(src + srcStride * (y + sy)); - for (int x = 0; x < sw; ++x) { - int bx = x * 4; - uint32_t *pixel = row + x + sx; - uint8_t r = *pixel >> 16; - uint8_t g = *pixel >> 8; - uint8_t b = *pixel; - - dst[bx + 0] = r; - dst[bx + 1] = g; - dst[bx + 2] = b; - dst[bx + 3] = 255; + break; } - dst += dstStride; + case CAIRO_FORMAT_A8: { + dst += oy * dstStride + ox; + for (int y = 0; y < ch; ++y) { + uint8_t *row = (uint8_t *)(src + srcStride * (y + sy)); + memcpy(dst, row + sx, cw); + dst += dstStride; + } + break; } - break; - } - case CAIRO_FORMAT_A8: { - for (int y = 0; y < sh; ++y) { - uint8_t *row = (uint8_t *)(src + srcStride * (y + sy)); - memcpy(dst, row + sx, dstStride); - dst += dstStride; + case CAIRO_FORMAT_A1: { + // TODO Should this be totally packed, or maintain a stride divisible by 4? + Napi::Error::New(env, "getImageData for CANVAS_FORMAT_A1 is not yet implemented").ThrowAsJavaScriptException(); + break; } - break; - } - case CAIRO_FORMAT_A1: { - // TODO Should this be totally packed, or maintain a stride divisible by 4? - Napi::Error::New(env, "getImageData for CANVAS_FORMAT_A1 is not yet implemented").ThrowAsJavaScriptException(); - - break; - } - case CAIRO_FORMAT_RGB16_565: { - for (int y = 0; y < sh; ++y) { - uint16_t *row = (uint16_t *)(src + srcStride * (y + sy)); - memcpy(dst, row + sx, dstStride); - dst += dstStride; + case CAIRO_FORMAT_RGB16_565: { + 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, cw * 2); + dst += dstStride; + } + break; } - break; - } #ifdef CAIRO_FORMAT_RGB30 - case CAIRO_FORMAT_RGB30: { + case CAIRO_FORMAT_RGB30: { // TODO Napi::Error::New(env, "getImageData for CANVAS_FORMAT_RGB30 is not yet implemented").ThrowAsJavaScriptException(); - - break; - } + break; + } #endif - default: { - // Unlikely - Napi::Error::New(env, "Invalid pixel format or not an image canvas").ThrowAsJavaScriptException(); - return env.Null(); - } + default: { + // Unlikely + Napi::Error::New(env, "Invalid pixel format or not an image canvas").ThrowAsJavaScriptException(); + return; + } + } } Napi::Number swHandle = Napi::Number::New(env, sw); diff --git a/test/canvas.test.js b/test/canvas.test.js index a3d1a09ae..2987e0a26 100644 --- a/test/canvas.test.js +++ b/test/canvas.test.js @@ -1251,6 +1251,430 @@ describe('Canvas', function () { it('works, slice, RGB30') + describe('slice partially outside the canvas', function () { + describe('left', function () { + if('works, RGBA32', function () { + const ctx = createTestCanvas() + const imageData = ctx.getImageData(-1, 0, 2, 1) + assert.equal(2, imageData.width) + assert.equal(1, imageData.height) + assert.equal(8, imageData.data.length) + + assert.equal(0, imageData.data[0]) + assert.equal(0, imageData.data[1]) + assert.equal(0, imageData.data[2]) + assert.equal(0, imageData.data[3]) + + assert.equal(255, imageData.data[4]) + assert.equal(0, imageData.data[5]) + assert.equal(0, imageData.data[6]) + assert.equal(255, imageData.data[7]) + }) + + it('works, RGB24', function () { + const ctx = createTestCanvas(false, { pixelFormat: 'RGB24' }) + const imageData = ctx.getImageData(-1, 0, 2, 1) + assert.equal(2, imageData.width) + assert.equal(1, imageData.height) + assert.equal(8, imageData.data.length) + + assert.equal(0, imageData.data[0]) + assert.equal(0, imageData.data[1]) + assert.equal(0, imageData.data[2]) + assert.equal(0, imageData.data[3]) + + assert.equal(255, imageData.data[4]) + assert.equal(0, imageData.data[5]) + assert.equal(0, imageData.data[6]) + assert.equal(255, imageData.data[7]) + }) + + it('works, RGB16_565', function () { + const ctx = createTestCanvas(false, { pixelFormat: 'RGB16_565' }) + const imageData = ctx.getImageData(-1, 0, 2, 1) + assert.equal(2, imageData.width) + assert.equal(1, imageData.height) + assert.equal(2, imageData.data.length) + + assert.equal(0, imageData.data[0]) + assert.equal((255 & 0b11111) << 11, imageData.data[1]) + }) + + it('works, A8', function () { + const ctx = createTestCanvas(true, { pixelFormat: 'A8' }) + const imageData = ctx.getImageData(-1, 0, 2, 1) + assert.equal(2, imageData.width) + assert.equal(1, imageData.height) + assert.equal(2, imageData.data.length) + + assert.equal(0, imageData.data[0]) + assert.equal(63, imageData.data[1]) + }) + }) + + describe('right', function () { + it('works, RGBA32', function () { + const ctx = createTestCanvas() + const imageData = ctx.getImageData(2, 0, 2, 1) + assert.equal(2, imageData.width) + assert.equal(1, imageData.height) + assert.equal(8, imageData.data.length) + + assert.equal(0, imageData.data[0]) + assert.equal(0, imageData.data[1]) + assert.equal(255, imageData.data[2]) + assert.equal(255, imageData.data[3]) + + assert.equal(0, imageData.data[4]) + assert.equal(0, imageData.data[5]) + assert.equal(0, imageData.data[6]) + assert.equal(0, imageData.data[7]) + }) + + it('works, RGB24', function () { + const ctx = createTestCanvas(false, { pixelFormat: 'RGB24' }) + const imageData = ctx.getImageData(2, 0, 2, 1) + assert.equal(2, imageData.width) + assert.equal(1, imageData.height) + assert.equal(8, imageData.data.length) + + assert.equal(0, imageData.data[0]) + assert.equal(0, imageData.data[1]) + assert.equal(255, imageData.data[2]) + assert.equal(255, imageData.data[3]) + + assert.equal(0, imageData.data[4]) + assert.equal(0, imageData.data[5]) + assert.equal(0, imageData.data[6]) + assert.equal(0, imageData.data[7]) + }) + + it('works, RGB16_565', function () { + const ctx = createTestCanvas(false, { pixelFormat: 'RGB16_565' }) + const imageData = ctx.getImageData(2, 0, 2, 1) + assert.equal(2, imageData.width) + assert.equal(1, imageData.height) + assert.equal(2, imageData.data.length) + + assert.equal((255 & 0b11111), imageData.data[0]) + assert.equal(0, imageData.data[1]) + }) + + it('works, A8', function () { + const ctx = createTestCanvas(true, { pixelFormat: 'A8' }) + const imageData = ctx.getImageData(2, 0, 2, 1) + assert.equal(2, imageData.width) + assert.equal(1, imageData.height) + assert.equal(2, imageData.data.length) + + assert.equal(191, imageData.data[0]) + assert.equal(0, imageData.data[1]) + }) + }) + + describe('left and right', function () { + it('works, RGBA32', function () { + const ctx = createTestCanvas() + const imageData = ctx.getImageData(-1, 0, 5, 1) + assert.equal(5, imageData.width) + assert.equal(1, imageData.height) + assert.equal(20, imageData.data.length) + + assert.equal(0, imageData.data[0]) + assert.equal(0, imageData.data[1]) + assert.equal(0, imageData.data[2]) + assert.equal(0, imageData.data[3]) + + assert.equal(255, imageData.data[4]) + assert.equal(0, imageData.data[5]) + assert.equal(0, imageData.data[6]) + assert.equal(255, imageData.data[7]) + + assert.equal(0, imageData.data[8]) + assert.equal(255, imageData.data[9]) + assert.equal(0, imageData.data[10]) + assert.equal(255, imageData.data[11]) + + assert.equal(0, imageData.data[12]) + assert.equal(0, imageData.data[13]) + assert.equal(255, imageData.data[14]) + assert.equal(255, imageData.data[15]) + + assert.equal(0, imageData.data[16]) + assert.equal(0, imageData.data[17]) + assert.equal(0, imageData.data[18]) + assert.equal(0, imageData.data[19]) + }) + + it('works, RGB24', function () { + const ctx = createTestCanvas(false, { pixelFormat: 'RGB24' }) + const imageData = ctx.getImageData(-1, 0, 5, 1) + assert.equal(5, imageData.width) + assert.equal(1, imageData.height) + assert.equal(20, imageData.data.length) + + assert.equal(0, imageData.data[0]) + assert.equal(0, imageData.data[1]) + assert.equal(0, imageData.data[2]) + assert.equal(0, imageData.data[3]) + + assert.equal(255, imageData.data[4]) + assert.equal(0, imageData.data[5]) + assert.equal(0, imageData.data[6]) + assert.equal(255, imageData.data[7]) + + assert.equal(0, imageData.data[8]) + assert.equal(255, imageData.data[9]) + assert.equal(0, imageData.data[10]) + assert.equal(255, imageData.data[11]) + + assert.equal(0, imageData.data[12]) + assert.equal(0, imageData.data[13]) + assert.equal(255, imageData.data[14]) + assert.equal(255, imageData.data[15]) + + assert.equal(0, imageData.data[16]) + assert.equal(0, imageData.data[17]) + assert.equal(0, imageData.data[18]) + assert.equal(0, imageData.data[19]) + }) + + it('works, RGB16_565', function () { + const ctx = createTestCanvas(false, { pixelFormat: 'RGB16_565' }) + const imageData = ctx.getImageData(-1, 0, 5, 1) + assert.equal(5, imageData.width) + assert.equal(1, imageData.height) + assert.equal(5, imageData.data.length) + + assert.equal(0, imageData.data[0]) + + assert.equal((255 & 0b11111) << 11, imageData.data[1]) + assert.equal((255 & 0b111111) << 5, imageData.data[2]) + assert.equal((255 & 0b11111), imageData.data[3]) + + assert.equal(0, imageData.data[4]) + }) + + it('works, A8', function () { + const ctx = createTestCanvas(true, { pixelFormat: 'A8' }) + const imageData = ctx.getImageData(-1, 0, 5, 1) + assert.equal(5, imageData.width) + assert.equal(1, imageData.height) + assert.equal(5, imageData.data.length) + + assert.equal(0, imageData.data[0]) + assert.equal(63, imageData.data[1]) + assert.equal(127, imageData.data[2]) + assert.equal(191, imageData.data[3]) + assert.equal(0, imageData.data[4]) + }) + }) + + describe('top', function () { + it('works, RGBA32', function () { + const ctx = createTestCanvas() + const imageData = ctx.getImageData(0, -1, 1, 2) + assert.equal(1, imageData.width) + assert.equal(2, imageData.height) + assert.equal(8, imageData.data.length) + + assert.equal(0, imageData.data[0]) + assert.equal(0, imageData.data[1]) + assert.equal(0, imageData.data[2]) + assert.equal(0, imageData.data[3]) + + assert.equal(255, imageData.data[4]) + assert.equal(0, imageData.data[5]) + assert.equal(0, imageData.data[6]) + assert.equal(255, imageData.data[7]) + }) + + it('works, RGB24', function () { + const ctx = createTestCanvas(false, { pixelFormat: 'RGB24' }) + const imageData = ctx.getImageData(0, -1, 1, 2) + assert.equal(1, imageData.width) + assert.equal(2, imageData.height) + assert.equal(8, imageData.data.length) + + assert.equal(0, imageData.data[0]) + assert.equal(0, imageData.data[1]) + assert.equal(0, imageData.data[2]) + assert.equal(0, imageData.data[3]) + + assert.equal(255, imageData.data[4]) + assert.equal(0, imageData.data[5]) + assert.equal(0, imageData.data[6]) + assert.equal(255, imageData.data[7]) + }) + + it('works, RGB16_565', function () { + const ctx = createTestCanvas(false, { pixelFormat: 'RGB16_565' }) + const imageData = ctx.getImageData(0, -1, 1, 2) + assert.equal(1, imageData.width) + assert.equal(2, imageData.height) + assert.equal(2, imageData.data.length) + + assert.equal(0, imageData.data[0]) + assert.equal((255 & 0b11111) << 11, imageData.data[1]) + }) + + it('works, A8', function () { + const ctx = createTestCanvas(true, { pixelFormat: 'A8' }) + const imageData = ctx.getImageData(0, -1, 1, 2) + assert.equal(1, imageData.width) + assert.equal(2, imageData.height) + assert.equal(2, imageData.data.length) + + assert.equal(0, imageData.data[0]) + assert.equal(63, imageData.data[1]) + }) + }) + + describe('bottom', function () { + it('works, RGBA32', function () { + const ctx = createTestCanvas() + const imageData = ctx.getImageData(0, 5, 1, 2) + assert.equal(1, imageData.width) + assert.equal(2, imageData.height) + assert.equal(8, imageData.data.length) + + assert.equal(255, imageData.data[0]) + assert.equal(0, imageData.data[1]) + assert.equal(0, imageData.data[2]) + assert.equal(255, imageData.data[3]) + + assert.equal(0, imageData.data[4]) + assert.equal(0, imageData.data[5]) + assert.equal(0, imageData.data[6]) + assert.equal(0, imageData.data[7]) + }) + + it('works, RGB24', function () { + const ctx = createTestCanvas(false, { pixelFormat: 'RGB24' }) + const imageData = ctx.getImageData(0, 5, 1, 2) + assert.equal(1, imageData.width) + assert.equal(2, imageData.height) + assert.equal(8, imageData.data.length) + + assert.equal(255, imageData.data[0]) + assert.equal(0, imageData.data[1]) + assert.equal(0, imageData.data[2]) + assert.equal(255, imageData.data[3]) + + assert.equal(0, imageData.data[4]) + assert.equal(0, imageData.data[5]) + assert.equal(0, imageData.data[6]) + assert.equal(0, imageData.data[7]) + }) + + it('works, RGB16_565', function () { + const ctx = createTestCanvas(false, { pixelFormat: 'RGB16_565' }) + const imageData = ctx.getImageData(0, 5, 1, 2) + assert.equal(1, imageData.width) + assert.equal(2, imageData.height) + assert.equal(2, imageData.data.length) + + assert.equal((255 & 0b11111) << 11, imageData.data[0]) + assert.equal(0, imageData.data[1]) + }) + + it('works, A8', function () { + const ctx = createTestCanvas(true, { pixelFormat: 'A8' }) + const imageData = ctx.getImageData(0, 5, 1, 2) + assert.equal(1, imageData.width) + assert.equal(2, imageData.height) + assert.equal(2, imageData.data.length) + + assert.equal(63, imageData.data[0]) + assert.equal(0, imageData.data[1]) + }) + }) + + describe('top to bottom', function () { + it('works, RGBA32', function () { + const ctx = createTestCanvas() + const imageData = ctx.getImageData(0, -1, 1, 8) + assert.equal(1, imageData.width) + assert.equal(8, imageData.height) + assert.equal(32, imageData.data.length) + + assert.equal(0, imageData.data[0]) + assert.equal(0, imageData.data[1]) + assert.equal(0, imageData.data[2]) + assert.equal(0, imageData.data[3]) + + assert.equal(255, imageData.data[4]) + assert.equal(0, imageData.data[5]) + assert.equal(0, imageData.data[6]) + assert.equal(255, imageData.data[7]) + + assert.equal(255, imageData.data[24]) + assert.equal(0, imageData.data[25]) + assert.equal(0, imageData.data[26]) + assert.equal(255, imageData.data[27]) + + assert.equal(0, imageData.data[28]) + assert.equal(0, imageData.data[29]) + assert.equal(0, imageData.data[30]) + assert.equal(0, imageData.data[31]) + }) + + it('works, RGB24', function () { + const ctx = createTestCanvas(false, { pixelFormat: 'RGB24' }) + const imageData = ctx.getImageData(0, -1, 1, 8) + assert.equal(1, imageData.width) + assert.equal(8, imageData.height) + assert.equal(32, imageData.data.length) + + assert.equal(0, imageData.data[0]) + assert.equal(0, imageData.data[1]) + assert.equal(0, imageData.data[2]) + assert.equal(0, imageData.data[3]) + + assert.equal(255, imageData.data[4]) + assert.equal(0, imageData.data[5]) + assert.equal(0, imageData.data[6]) + assert.equal(255, imageData.data[7]) + + assert.equal(255, imageData.data[24]) + assert.equal(0, imageData.data[25]) + assert.equal(0, imageData.data[26]) + assert.equal(255, imageData.data[27]) + + assert.equal(0, imageData.data[28]) + assert.equal(0, imageData.data[29]) + assert.equal(0, imageData.data[30]) + assert.equal(0, imageData.data[31]) + }) + + it('works, RGB16_565', function () { + const ctx = createTestCanvas(false, { pixelFormat: 'RGB16_565' }) + const imageData = ctx.getImageData(0, -1, 1, 8) + assert.equal(1, imageData.width) + assert.equal(8, imageData.height) + assert.equal(8, imageData.data.length) + + assert.equal(0, imageData.data[0]) + assert.equal((255 & 0b11111) << 11, imageData.data[1]) + assert.equal((255 & 0b11111) << 11, imageData.data[6]) + assert.equal(0, imageData.data[7]) + }) + + it('works, A8', function () { + const ctx = createTestCanvas(true, { pixelFormat: 'A8' }) + const imageData = ctx.getImageData(0, -1, 1, 8) + assert.equal(1, imageData.width) + assert.equal(8, imageData.height) + assert.equal(8, imageData.data.length) + + assert.equal(0, imageData.data[0]) + assert.equal(63, imageData.data[1]) + assert.equal(63, imageData.data[6]) + assert.equal(0, imageData.data[7]) + }) + }) + }) + it('works, assignment', function () { const ctx = createTestCanvas() const data = ctx.getImageData(0, 0, 5, 5).data @@ -1275,16 +1699,58 @@ describe('Canvas', function () { }) }) - it('does not throw if rectangle is outside the canvas (#2024)', () => { - const canvas = createCanvas(200, 200) - const ctx = canvas.getContext('2d') + describe('does not throw if rectangle is outside the canvas (#2024)', function () { + it('on the left', function () { + const ctx = createTestCanvas(true, { pixelFormat: 'A8' }) + + const imageData = ctx.getImageData(-11, 0, 2, 2); + assert.equal(2, imageData.width) + assert.equal(2, imageData.height) + assert.equal(4, imageData.data.length) + assert.equal(0, imageData.data[0]) + assert.equal(0, imageData.data[1]) + assert.equal(0, imageData.data[2]) + assert.equal(0, imageData.data[3]) + }) - ctx.rect(0, 0, 100, 100); - ctx.fill(); + it('on the right', function () { + const ctx = createTestCanvas(true, { pixelFormat: 'A8' }) + + const imageData = ctx.getImageData(98, 0, 2, 2); + assert.equal(2, imageData.width) + assert.equal(2, imageData.height) + assert.equal(4, imageData.data.length) + assert.equal(0, imageData.data[0]) + assert.equal(0, imageData.data[1]) + assert.equal(0, imageData.data[2]) + assert.equal(0, imageData.data[3]) + }) - const imageData = ctx.getImageData(0, -11, 10, 10); - assert.equal(10, imageData.width) - assert.equal(1, imageData.height) + it('on the top', function () { + const ctx = createTestCanvas(true, { pixelFormat: 'A8' }) + + const imageData = ctx.getImageData(0, -12, 2, 2); + assert.equal(2, imageData.width) + assert.equal(2, imageData.height) + assert.equal(4, imageData.data.length) + assert.equal(0, imageData.data[0]) + assert.equal(0, imageData.data[1]) + assert.equal(0, imageData.data[2]) + assert.equal(0, imageData.data[3]) + }) + + it('on the bottom', function () { + const ctx = createTestCanvas(true, { pixelFormat: 'A8' }) + + const imageData = ctx.getImageData(0, 98, 2, 2); + assert.equal(2, imageData.width) + assert.equal(2, imageData.height) + assert.equal(4, imageData.data.length) + assert.equal(0, imageData.data[0]) + assert.equal(0, imageData.data[1]) + assert.equal(0, imageData.data[2]) + assert.equal(0, imageData.data[3]) + }) }) }) diff --git a/test/public/tests.js b/test/public/tests.js index c904cde7e..89d5ba67e 100644 --- a/test/public/tests.js +++ b/test/public/tests.js @@ -2378,6 +2378,28 @@ tests['putImageData() 10'] = function (ctx) { ctx.putImageData(data, 20, 120) } +tests['putImageData() 11'] = function (ctx) { + for (let i = 0; i < 8; i++) { + for (let j = 0; j < 8; 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) + } + } + const data = ctx.getImageData(-25, -25, 50, 50) + ctx.putImageData(data, 10, 10) +} + +tests['putImageData() 12'] = function (ctx) { + for (let i = 0; i < 8; i++) { + for (let j = 0; j < 8; 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) + } + } + const data = ctx.getImageData(175, 175, 50, 50) + ctx.putImageData(data, 10, 10) +} + tests['putImageData() alpha'] = function (ctx) { ctx.fillStyle = 'rgba(255,0,0,0.5)' ctx.fillRect(0, 0, 50, 100)