From f69171da9a7a477ca210e90bb3dd09b88aa6c590 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= Date: Tue, 23 Jun 2015 14:38:25 +0200 Subject: [PATCH 1/2] expose ImageData constructor --- lib/canvas.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/canvas.js b/lib/canvas.js index 49dd5a9..7962bcd 100644 --- a/lib/canvas.js +++ b/lib/canvas.js @@ -62,6 +62,7 @@ exports.Context2d = Context2d; exports.PNGStream = PNGStream; exports.JPEGStream = JPEGStream; exports.Image = Image; +exports.ImageData = canvas.ImageData; if (FontFace) { function Font(name, path, idx) { From 2884267de4a45cbbb6dfa717903d054cf8a7ab94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= Date: Sat, 24 Oct 2015 19:25:57 +0200 Subject: [PATCH 2/2] make ImageData constructor more spec-compliant --- src/ImageData.cc | 50 +++++++++++++++++++++++++----------- test/imageData.test.js | 58 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 15 deletions(-) create mode 100644 test/imageData.test.js diff --git a/src/ImageData.cc b/src/ImageData.cc index 74d2754..b5d047d 100644 --- a/src/ImageData.cc +++ b/src/ImageData.cc @@ -44,46 +44,66 @@ NAN_METHOD(ImageData::New) { int width; int height; + int length; if (info[0]->IsUint32() && info[1]->IsUint32()) { width = info[0]->Uint32Value(); + if (width == 0) { + Nan::ThrowRangeError("The source width is zero."); + return; + } height = info[1]->Uint32Value(); - int size = width * height; + if (height == 0) { + Nan::ThrowRangeError("The source height is zero."); + return; + } + length = width * height * 4; #if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION <= 10 - Local sizeHandle = Nan::New(size); + Local sizeHandle = Nan::New(length); Local caargv[] = { sizeHandle }; clampedArray = global->Get(Nan::New("Uint8ClampedArray").ToLocalChecked()).As()->NewInstance(1, caargv); #else - clampedArray = Uint8ClampedArray::New(ArrayBuffer::New(Isolate::GetCurrent(), size), 0, size); + clampedArray = Uint8ClampedArray::New(ArrayBuffer::New(Isolate::GetCurrent(), length), 0, length); #endif #if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION <= 10 } else if (info[0]->ToObject()->GetIndexedPropertiesExternalArrayDataType() == kExternalPixelArray && info[1]->IsUint32()) { clampedArray = info[0]->ToObject(); + length = clampedArray->GetIndexedPropertiesExternalArrayDataLength(); #else } else if (info[0]->IsUint8ClampedArray() && info[1]->IsUint32()) { clampedArray = info[0].As(); + length = clampedArray->Length(); #endif + if (length == 0) { + Nan::ThrowRangeError("The input data has a zero byte length."); + return; + } + if (length % 4 != 0) { + Nan::ThrowRangeError("The input data byte length is not a multiple of 4."); + return; + } width = info[1]->Uint32Value(); - if (info[2]->IsUint32()) { - height = info[2]->Uint32Value(); - } else { -#if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION <= 10 - height = clampedArray->GetIndexedPropertiesExternalArrayDataLength() / width; -#else - height = clampedArray->Length() / width; -#endif + int size = length / 4; + if (width == 0) { + Nan::ThrowRangeError("The source width is zero."); + return; + } + if (size % width != 0) { + Nan::ThrowRangeError("The input data byte length is not a multiple of (4 * width)."); + return; + } + height = size / width; + if (info[2]->IsUint32() && info[2]->Uint32Value() != height) { + Nan::ThrowRangeError("The input data byte length is not equal to (4 * width * height)."); + return; } } else { Nan::ThrowTypeError("Expected (Uint8ClampedArray, width[, height]) or (width, height)"); return; } - // No behavior defined in spec. This is what WebKit does: - if (width < 1) width = 1; - if (height < 1) height = 1; - #if NODE_MAJOR_VERSION < 3 void *dataPtr = clampedArray->GetIndexedPropertiesExternalArrayData(); #else diff --git a/test/imageData.test.js b/test/imageData.test.js new file mode 100644 index 0000000..5e535af --- /dev/null +++ b/test/imageData.test.js @@ -0,0 +1,58 @@ +'use strict'; + +var Canvas = require('../') + , ImageData = Canvas.ImageData + , assert = require('assert'); + +describe('ImageData', function () { + it('should throw with invalid numeric arguments', function () { + assert.throws(function () { + new ImageData(0, 0); + }, /width is zero/); + assert.throws(function () { + new ImageData(1, 0); + }, /height is zero/); + assert.throws(function () { + new ImageData(0); + }, TypeError); + }); + + it('should construct with width and height', function () { + var imagedata = new ImageData(2, 3); + assert.strictEqual(imagedata.width, 2); + assert.strictEqual(imagedata.height, 3); + assert(imagedata.data instanceof Uint8ClampedArray); + assert.strictEqual(imagedata.data.length, 24); + }); + + it('should throw with invalid typed array', function () { + assert.throws(function () { + new ImageData(new Uint8ClampedArray(0), 0); + }, /input data has a zero byte length/); + assert.throws(function () { + new ImageData(new Uint8ClampedArray(3), 0); + }, /input data byte length is not a multiple of 4/); + assert.throws(function () { + new ImageData(new Uint8ClampedArray(16), 3); + }, RangeError); + assert.throws(function () { + new ImageData(new Uint8ClampedArray(12), 3, 5); + }, RangeError); + }); + + it('should construct with typed array', function () { + var data = new Uint8ClampedArray(2 * 3 * 4); + var imagedata = new ImageData(data, 2); + assert.strictEqual(imagedata.width, 2); + assert.strictEqual(imagedata.height, 3); + assert(imagedata.data instanceof Uint8ClampedArray); + assert.strictEqual(imagedata.data.length, 24); + + data = new Uint8ClampedArray(3 * 4 * 4); + imagedata = new ImageData(data, 3, 4); + assert.strictEqual(imagedata.width, 3); + assert.strictEqual(imagedata.height, 4); + assert(imagedata.data instanceof Uint8ClampedArray); + assert.strictEqual(imagedata.data.length, 48); + }); +});