diff --git a/lib/canvas.js b/lib/canvas.js index 49dd5a930..7962bcd39 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) { diff --git a/src/ImageData.cc b/src/ImageData.cc index 74d275457..b5d047dcb 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 000000000..5e535af20 --- /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); + }); +});