diff --git a/History.md b/History.md index e40b0ac..7d61f3f 100644 --- a/History.md +++ b/History.md @@ -1,3 +1,8 @@ +Future / Future +================== + + * Replace CanvasPixelArray with Uint8ClampedArray to be API-compliant (#xxx) + 1.2.7 / 2015-07-29 ================== diff --git a/binding.gyp b/binding.gyp index 597d1fc..b0d7137 100755 --- a/binding.gyp +++ b/binding.gyp @@ -49,8 +49,7 @@ 'src/color.cc', 'src/Image.cc', 'src/ImageData.cc', - 'src/init.cc', - 'src/PixelArray.cc' + 'src/init.cc' ], 'conditions': [ ['OS=="win"', { @@ -72,7 +71,7 @@ 'VCCLCompilerTool': { 'WarningLevel': 4, 'ExceptionHandling': 1, - 'DisableSpecificWarnings': [4100, 4127, 4201, 4244, 4267, 4506, 4611, 4714] + 'DisableSpecificWarnings': [4100, 4127, 4201, 4244, 4267, 4506, 4611, 4714, 4512] } } }, @@ -81,7 +80,7 @@ 'VCCLCompilerTool': { 'WarningLevel': 4, 'ExceptionHandling': 1, - 'DisableSpecificWarnings': [4100, 4127, 4201, 4244, 4267, 4506, 4611, 4714] + 'DisableSpecificWarnings': [4100, 4127, 4201, 4244, 4267, 4506, 4611, 4714, 4512] } } } diff --git a/lib/canvas.js b/lib/canvas.js index 1d85e8a..49dd5a9 100644 --- a/lib/canvas.js +++ b/lib/canvas.js @@ -13,7 +13,6 @@ var canvas = require('./bindings') , Canvas = canvas.Canvas , Image = canvas.Image , cairoVersion = canvas.cairoVersion - , PixelArray = canvas.CanvasPixelArray , Context2d = require('./context2d') , PNGStream = require('./pngstream') , JPEGStream = require('./jpegstream') @@ -62,7 +61,6 @@ if (canvas.gifVersion) { exports.Context2d = Context2d; exports.PNGStream = PNGStream; exports.JPEGStream = JPEGStream; -exports.PixelArray = PixelArray; exports.Image = Image; if (FontFace) { @@ -100,12 +98,6 @@ require('./context2d'); require('./image'); -/** - * PixelArray implementation. - */ - -require('./pixelarray'); - /** * Inspect canvas. * diff --git a/lib/context2d.js b/lib/context2d.js index 7dfbea0..49804bc 100644 --- a/lib/context2d.js +++ b/lib/context2d.js @@ -12,8 +12,7 @@ var canvas = require('./bindings') , Context2d = canvas.CanvasRenderingContext2d , CanvasGradient = canvas.CanvasGradient , CanvasPattern = canvas.CanvasPattern - , ImageData = canvas.ImageData - , PixelArray = canvas.CanvasPixelArray; + , ImageData = canvas.ImageData; /** * Export `Context2d` as the module. @@ -351,22 +350,6 @@ Context2d.prototype.__defineGetter__('textAlign', function(){ return this.lastTextAlignment || 'start'; }); -/** - * Get `ImageData` with the given rect. - * - * @param {Number} x - * @param {Number} y - * @param {Number} width - * @param {Number} height - * @return {ImageData} - * @api public - */ - -Context2d.prototype.getImageData = function(x, y, width, height){ - var arr = new PixelArray(this.canvas, x, y, width, height); - return new ImageData(arr); -}; - /** * Create `ImageData` with the given dimensions or * `ImageData` instance for dimensions. @@ -382,5 +365,5 @@ Context2d.prototype.createImageData = function(width, height){ height = width.height; width = width.width; } - return new ImageData(new PixelArray(width, height)); + return new ImageData(new Uint8ClampedArray(width * height * 4), width, height); }; diff --git a/lib/pixelarray.js b/lib/pixelarray.js deleted file mode 100644 index ff10cee..0000000 --- a/lib/pixelarray.js +++ /dev/null @@ -1,29 +0,0 @@ - -/*! - * Canvas - PixelArray - * Copyright (c) 2010 LearnBoost - * MIT Licensed - */ - -/** - * Module dependencies. - */ - -var Canvas = require('./bindings') - , PixelArray = Canvas.CanvasPixelArray; - -/** - * Custom inspect. - */ - -PixelArray.prototype.inspect = function(){ - var buf = '[PixelArray '; - for (var i = 0, len = this.length; i < len; i += 4) { - buf += '\n ' + i + ': rgba(' - + this[i + 0] + ',' - + this[i + 1] + ',' - + this[i + 2] + ',' - + this[i + 3] + ')'; - } - return buf + '\n]'; -}; diff --git a/package.json b/package.json index 09ed2f7..ab8f8dd 100644 --- a/package.json +++ b/package.json @@ -27,13 +27,14 @@ "nan": "^1.8.4" }, "devDependencies": { - "express": "3.0", - "jade": "0.28.1", + "body-parser": "^1.13.3", + "express": "^4.13.2", + "jade": "^1.11.0", "mocha": "*", "should": "*" }, "engines": { - "node": ">= 0.6.0" + "node": ">=0.8.0 <3" }, "main": "./lib/canvas.js", "license": "MIT" diff --git a/src/CanvasRenderingContext2d.cc b/src/CanvasRenderingContext2d.cc index 1844cd9..e1ad7a2 100755 --- a/src/CanvasRenderingContext2d.cc +++ b/src/CanvasRenderingContext2d.cc @@ -103,6 +103,7 @@ Context2d::Initialize(Handle target) { Local proto = ctor->PrototypeTemplate(); NODE_SET_PROTOTYPE_METHOD(ctor, "drawImage", DrawImage); NODE_SET_PROTOTYPE_METHOD(ctor, "putImageData", PutImageData); + NODE_SET_PROTOTYPE_METHOD(ctor, "getImageData", GetImageData); NODE_SET_PROTOTYPE_METHOD(ctor, "addPage", AddPage); NODE_SET_PROTOTYPE_METHOD(ctor, "save", Save); NODE_SET_PROTOTYPE_METHOD(ctor, "restore", Restore); @@ -576,12 +577,11 @@ NAN_METHOD(Context2d::PutImageData) { Context2d *context = ObjectWrap::Unwrap(args.This()); ImageData *imageData = ObjectWrap::Unwrap(obj); - PixelArray *arr = imageData->pixelArray(); - uint8_t *src = arr->data(); + uint8_t *src = imageData->data(); uint8_t *dst = context->canvas()->data(); - int srcStride = arr->stride() + int srcStride = imageData->stride() , dstStride = context->canvas()->stride(); int sx = 0 @@ -596,8 +596,8 @@ NAN_METHOD(Context2d::PutImageData) { switch (args.Length()) { // imageData, dx, dy case 3: - cols = std::min(arr->width(), context->canvas()->width - dx); - rows = std::min(arr->height(), context->canvas()->height - dy); + cols = std::min(imageData->width(), context->canvas()->width - dx); + rows = std::min(imageData->height(), context->canvas()->height - dy); break; // imageData, dx, dy, sx, sy, sw, sh case 7: @@ -605,12 +605,22 @@ NAN_METHOD(Context2d::PutImageData) { sy = args[4]->Int32Value(); sw = args[5]->Int32Value(); sh = args[6]->Int32Value(); + // fix up negative height, width + if (sw < 0) sx += sw, sw = -sw; + if (sh < 0) sy += sh, sh = -sh; + // clamp the left edge if (sx < 0) sw += sx, sx = 0; if (sy < 0) sh += sy, sy = 0; - if (sx + sw > arr->width()) sw = arr->width() - sx; - if (sy + sh > arr->height()) sh = arr->height() - sy; + // clamp the right edge + if (sx + sw > imageData->width()) sw = imageData->width() - sx; + if (sy + sh > imageData->height()) sh = imageData->height() - sy; + // start destination at source offset dx += sx; dy += sy; + // chop off outlying source data + if (dx < 0) sw += dx, sx -= dx, dx = 0; + if (dy < 0) sh += dy, sy -= dy, dy = 0; + // clamp width at canvas size cols = std::min(sw, context->canvas()->width - dx); rows = std::min(sh, context->canvas()->height - dy); break; @@ -620,27 +630,41 @@ 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) { + *dstRow++ = 0; + *dstRow++ = 0; + *dstRow++ = 0; + *dstRow++ = 0; + } else if (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( @@ -653,6 +677,121 @@ NAN_METHOD(Context2d::PutImageData) { NanReturnUndefined(); } +/* + * Get image data. + * + * - sx, sy, sw, sh + * + */ + +NAN_METHOD(Context2d::GetImageData) { + NanScope(); + + Context2d *context = ObjectWrap::Unwrap(args.This()); + Canvas *canvas = context->canvas(); + + int sx = args[0]->Int32Value(); + int sy = args[1]->Int32Value(); + int sw = args[2]->Int32Value(); + int sh = args[3]->Int32Value(); + + if (!sw) + return NanThrowError("IndexSizeError: The source width is 0."); + if (!sh) + return NanThrowError("IndexSizeError: The source height is 0."); + + // WebKit and Firefox have this behavior: + // Flip the coordinates so the origin is top/left-most: + if (sw < 0) { + sx += sw; + sw = -sw; + } + if (sh < 0) { + sy += sh; + sh = -sh; + } + + if (sx + sw > canvas->width) sw = canvas->width - sx; + if (sy + sh > canvas->height) sh = canvas->height - sy; + + // WebKit/moz functionality. node-canvas used to return in either case. + if (sw <= 0) sw = 1; + if (sh <= 0) sh = 1; + + // 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; + sx = 0; + } + if (sy < 0) { + sh += sy; + sy = 0; + } + + int size = sw * sh * 4; + + int srcStride = canvas->stride(); + int dstStride = sw * 4; + + uint8_t *src = canvas->data(); + uint8_t *dst = (uint8_t *)calloc(1, size); + NanAdjustExternalMemory(size); + +#if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION <= 10 + Local global = Context::GetCurrent()->Global(); + + Handle bufargv[] = { NanNew(size) }; + Local buffer = global->Get(NanNew("ArrayBuffer")).As()->NewInstance(1, bufargv); + + Handle caargv[] = { buffer, NanNew(0), NanNew(size) }; + Local clampedArray = global->Get(NanNew("Uint8ClampedArray")).As()->NewInstance(3, caargv); + + clampedArray->SetIndexedPropertiesToExternalArrayData(dst, kExternalPixelArray, size); +#else + Local buffer = ArrayBuffer::New(Isolate::GetCurrent(), size); + Local clampedArray = Uint8ClampedArray::New(buffer, 0, size); + clampedArray->SetIndexedPropertiesToExternalArrayData(dst, kExternalUint8ClampedArray, size); +#endif + + // Normalize data (argb -> rgba) + 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) { + dst[bx + 0] = r; + dst[bx + 1] = g; + dst[bx + 2] = b; + } else { + float alpha = (float)a / 255; + dst[bx + 0] = (int)((float)r / alpha); + dst[bx + 1] = (int)((float)g / alpha); + dst[bx + 2] = (int)((float)b / alpha); + } + + } + dst += dstStride; + } + + const int argc = 3; + Local argv[argc] = { clampedArray, NanNew(sw), NanNew(sh) }; + + Local cons = NanNew(ImageData::constructor); + Local instance = cons->GetFunction()->NewInstance(argc, argv); + + NanReturnValue(instance); +} + /* * Draw image src image to the destination (context). * diff --git a/src/CanvasRenderingContext2d.h b/src/CanvasRenderingContext2d.h index b5920a5..e377ad6 100644 --- a/src/CanvasRenderingContext2d.h +++ b/src/CanvasRenderingContext2d.h @@ -113,6 +113,7 @@ class Context2d: public node::ObjectWrap { static NAN_METHOD(Rect); static NAN_METHOD(Arc); static NAN_METHOD(ArcTo); + static NAN_METHOD(GetImageData); static NAN_GETTER(GetPatternQuality); static NAN_GETTER(GetGlobalCompositeOperation); static NAN_GETTER(GetGlobalAlpha); diff --git a/src/ImageData.cc b/src/ImageData.cc index bbacb75..13ecfaf 100644 --- a/src/ImageData.cc +++ b/src/ImageData.cc @@ -36,15 +36,61 @@ ImageData::Initialize(Handle target) { NAN_METHOD(ImageData::New) { NanScope(); - Local obj = args[0]->ToObject(); - if (!NanHasInstance(PixelArray::constructor, obj)) - return NanThrowTypeError("CanvasPixelArray expected"); +#if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION <= 10 + Local clampedArray; + Local global = Context::GetCurrent()->Global(); +#else + Local clampedArray; +#endif - PixelArray *arr = ObjectWrap::Unwrap(obj); - ImageData *imageData = new ImageData(arr); - args.This()->Set(NanNew("data"), args[0]); + int width; + int height; + + + if (args[0]->IsUint32() && args[1]->IsUint32()) { + width = args[0]->Uint32Value(); + height = args[1]->Uint32Value(); + int size = width * height; + +#if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION <= 10 + Handle caargv[] = { NanNew(size) }; + Local clampedArray = global->Get(NanNew("Uint8ClampedArray")).As()->NewInstance(1, caargv); +#else + clampedArray = Uint8ClampedArray::New(ArrayBuffer::New(Isolate::GetCurrent(), size), 0, size); +#endif + +#if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION <= 10 + } else if (args[0]->ToObject()->GetIndexedPropertiesExternalArrayDataType() == kExternalPixelArray && args[1]->IsUint32()) { + clampedArray = args[0]->ToObject(); +#else + } else if (args[0]->IsUint8ClampedArray() && args[1]->IsUint32()) { + clampedArray = args[0].As(); +#endif + width = args[1]->Uint32Value(); + if (args[2]->IsUint32()) { + height = args[2]->Uint32Value(); + } else { +#if NODE_MAJOR_VERSION == 0 && NODE_MINOR_VERSION <= 10 + height = clampedArray->GetIndexedPropertiesExternalArrayDataLength() / width; +#else + height = clampedArray->Length() / width; +#endif + } + } else { + NanThrowTypeError("Expected (Uint8ClampedArray, width[, height]) or (width, height)"); + NanReturnUndefined(); + } + + // No behavior defined in spec. This is what WebKit does: + if (width < 1) width = 1; + if (height < 1) height = 1; + + void *dataPtr = clampedArray->GetIndexedPropertiesExternalArrayData(); + + ImageData *imageData = new ImageData(reinterpret_cast(dataPtr), width, height); imageData->Wrap(args.This()); + args.This()->Set(NanNew("data"), clampedArray); NanReturnValue(args.This()); } @@ -55,7 +101,7 @@ NAN_METHOD(ImageData::New) { NAN_GETTER(ImageData::GetWidth) { NanScope(); ImageData *imageData = ObjectWrap::Unwrap(args.This()); - NanReturnValue(NanNew(imageData->pixelArray()->width())); + NanReturnValue(NanNew(imageData->width())); } /* @@ -65,5 +111,5 @@ NAN_GETTER(ImageData::GetWidth) { NAN_GETTER(ImageData::GetHeight) { NanScope(); ImageData *imageData = ObjectWrap::Unwrap(args.This()); - NanReturnValue(NanNew(imageData->pixelArray()->height())); + NanReturnValue(NanNew(imageData->height())); } diff --git a/src/ImageData.h b/src/ImageData.h index 150f662..4ef6e12 100644 --- a/src/ImageData.h +++ b/src/ImageData.h @@ -9,8 +9,8 @@ #define __NODE_IMAGE_DATA_H__ #include "Canvas.h" -#include "PixelArray.h" #include +#include "v8.h" class ImageData: public node::ObjectWrap { public: @@ -19,10 +19,18 @@ class ImageData: public node::ObjectWrap { static NAN_METHOD(New); static NAN_GETTER(GetWidth); static NAN_GETTER(GetHeight); - inline PixelArray *pixelArray(){ return _arr; } - ImageData(PixelArray *arr): _arr(arr) {} + + inline int width() { return _width; } + inline int height() { return _height; } + inline uint8_t *data() { return _data; } + inline int stride() { return _width * 4; } + ImageData(uint8_t *data, int width, int height) : _width(width), _height(height), _data(data) {} + private: - PixelArray *_arr; + int _width; + int _height; + uint8_t *_data; + }; #endif diff --git a/src/PixelArray.cc b/src/PixelArray.cc deleted file mode 100644 index f39b337..0000000 --- a/src/PixelArray.cc +++ /dev/null @@ -1,163 +0,0 @@ - -// -// PixelArray.cc -// -// Copyright (c) 2010 LearnBoost -// - -#include "PixelArray.h" -#include -#include - -Persistent PixelArray::constructor; - -/* - * Initialize PixelArray. - */ - -void -PixelArray::Initialize(Handle target) { - NanScope(); - - // Constructor - Local ctor = NanNew(PixelArray::New); - NanAssignPersistent(constructor, ctor); - ctor->InstanceTemplate()->SetInternalFieldCount(1); - ctor->SetClassName(NanNew("CanvasPixelArray")); - - // Prototype - Local proto = ctor->InstanceTemplate(); - proto->SetAccessor(NanNew("length"), GetLength); - target->Set(NanNew("CanvasPixelArray"), ctor->GetFunction()); -} - -/* - * Initialize a new PixelArray. - */ - -NAN_METHOD(PixelArray::New) { - NanScope(); - PixelArray *arr; - Local obj = args[0]->ToObject(); - - switch (args.Length()) { - // width, height - case 2: - arr = new PixelArray( - args[0]->Int32Value() - , args[1]->Int32Value()); - break; - // canvas, x, y, width, height - case 5: { - if (!NanHasInstance(Canvas::constructor, obj)) - return NanThrowTypeError("Canvas expected"); - - Canvas *canvas = ObjectWrap::Unwrap(obj); - arr = new PixelArray( - canvas - , args[1]->Int32Value() - , args[2]->Int32Value() - , args[3]->Int32Value() - , args[4]->Int32Value()); - } - break; - default: - return NanThrowTypeError("invalid arguments"); - } - - // Let v8 handle accessors (and clamping) - args.This()->SetIndexedPropertiesToPixelData( - arr->data() - , arr->length()); - - arr->Wrap(args.This()); - NanReturnValue(args.This()); -} - -/* - * Get length. - */ - -NAN_GETTER(PixelArray::GetLength) { - NanScope(); - NanReturnValue(NanNew(args.This()->GetIndexedPropertiesPixelDataLength())); -} - -/* - * Initialize a new PixelArray copying data - * from the canvas surface using the given rect. - */ - -PixelArray::PixelArray(Canvas *canvas, int sx, int sy, int width, int height): - _width(width), _height(height) { - - // Alloc space for our new data - uint8_t *dst = alloc(); - uint8_t *src = canvas->data(); - int srcStride = canvas->stride() - , dstStride = stride(); - - if (sx < 0) width += sx, sx = 0; - if (sy < 0) height += sy, sy = 0; - if (sx + width > canvas->width) width = canvas->width - sx; - if (sy + height > canvas->height) height = canvas->height - sy; - if (width <= 0 || height <= 0) return; - - // Normalize data (argb -> rgba) - for (int y = 0; y < height; ++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 + 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) { - float alpha = (float) a / 255; - dst[bx + 0] = (int)((float) r / alpha); - dst[bx + 1] = (int)((float) g / alpha); - dst[bx + 2] = (int)((float) b / alpha); - } else { - dst[bx + 0] = r; - dst[bx + 1] = g; - dst[bx + 2] = b; - } - } - dst += dstStride; - } -} - -/* - * Initialize an empty PixelArray with the given dimensions. - */ - -PixelArray::PixelArray(int width, int height): - _width(width), _height(height) { - alloc(); -} - -/* - * Allocate / zero data buffer. Hint mem adjustment. - */ - -uint8_t * -PixelArray::alloc() { - int len = length(); - _data = (uint8_t *) calloc(1, len); - NanAdjustExternalMemory(len); - return _data; -} - -/* - * Hint mem adjustment. - */ - -PixelArray::~PixelArray() { - NanAdjustExternalMemory(-length()); - free(_data); -} diff --git a/src/PixelArray.h b/src/PixelArray.h deleted file mode 100644 index 21e68f1..0000000 --- a/src/PixelArray.h +++ /dev/null @@ -1,33 +0,0 @@ - -// -// PixelArray.h -// -// Copyright (c) 2010 LearnBoost -// - -#ifndef __NODE_PIXEL_ARRAY_H__ -#define __NODE_PIXEL_ARRAY_H__ - -#include "Canvas.h" - -class PixelArray: public node::ObjectWrap { - public: - static Persistent constructor; - static void Initialize(Handle target); - static NAN_METHOD(New); - static NAN_GETTER(GetLength); - inline int length(){ return _width * _height * 4; } - inline int width(){ return _width; } - inline int height(){ return _height; } - inline int stride(){ return _width * 4; } - inline uint8_t *data(){ return _data; } - PixelArray(Canvas *canvas, int x, int y, int width, int height); - PixelArray(int width, int height); - ~PixelArray(); - private: - uint8_t *alloc(); - uint8_t *_data; - int _width, _height; -}; - -#endif diff --git a/src/init.cc b/src/init.cc index 6628cee..14fd705 100755 --- a/src/init.cc +++ b/src/init.cc @@ -9,7 +9,6 @@ #include "Canvas.h" #include "Image.h" #include "ImageData.h" -#include "PixelArray.h" #include "CanvasGradient.h" #include "CanvasPattern.h" #include "CanvasRenderingContext2d.h" @@ -24,7 +23,6 @@ init (Handle target) { Canvas::Initialize(target); Image::Initialize(target); ImageData::Initialize(target); - PixelArray::Initialize(target); Context2d::Initialize(target); Gradient::Initialize(target); Pattern::Initialize(target); diff --git a/test/canvas.test.js b/test/canvas.test.js index 993f0c0..cb45811 100644 --- a/test/canvas.test.js +++ b/test/canvas.test.js @@ -76,10 +76,6 @@ module.exports = { } }, - 'test .PixelArray': function(){ - assert.equal(typeof Canvas.PixelArray, 'function'); - }, - 'test color serialization': function(){ var canvas = new Canvas(200, 200) , ctx = canvas.getContext('2d'); @@ -169,7 +165,7 @@ module.exports = { ctx.fillStyle = 'hsl(10, 100%, 42%)'; assert.equal('#d62400', ctx.fillStyle); - + ctx.fillStyle = 'hsl(370, 120%, 42%)'; assert.equal('#d62400', ctx.fillStyle); diff --git a/test/public/tests.js b/test/public/tests.js index 728dcf6..cc2beb1 100644 --- a/test/public/tests.js +++ b/test/public/tests.js @@ -14,7 +14,7 @@ tests['strokeRect()'] = function(ctx){ }; tests['fillRect()'] = function(ctx){ - + function render(level){ ctx.fillStyle = getPointColour(122,122); ctx.fillRect(0,0,240,240); @@ -386,7 +386,7 @@ tests['createLinearGradient()'] = function(ctx){ ctx.strokeStyle = lingrad2; ctx.fillRect(10,10,130,130); - ctx.strokeRect(50,50,50,50); + ctx.strokeRect(50,50,50,50); }; tests['createRadialGradient()'] = function(ctx){ @@ -458,7 +458,7 @@ tests['globalAlpha 2'] = function(ctx){ tests['fillStyle'] = function(ctx){ for (i=0;i<6;i++){ for (j=0;j<6;j++){ - ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' + + ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' + Math.floor(255-42.5*j) + ',0)'; ctx.fillRect(j*25,i*25,25,25); } @@ -468,7 +468,7 @@ tests['fillStyle'] = function(ctx){ tests['strokeStyle'] = function(ctx){ for (var i=0;i<6;i++){ for (var j=0;j<6;j++){ - ctx.strokeStyle = 'rgb(0,' + Math.floor(255-42.5*i) + ',' + + ctx.strokeStyle = 'rgb(0,' + Math.floor(255-42.5*i) + ',' + Math.floor(255-42.5*j) + ')'; ctx.beginPath(); ctx.arc(12.5+j*25,12.5+i*25,10,0,Math.PI*2,true); @@ -593,22 +593,22 @@ tests['states'] = function(ctx){ }; tests['states with stroke/fill/globalAlpha'] = function(ctx){ - ctx.fillRect(0,0,150,150); - ctx.save(); - - ctx.fillStyle = '#09F' + ctx.fillRect(0,0,150,150); + ctx.save(); + + ctx.fillStyle = '#09F' ctx.fillRect(15,15,120,120); - - ctx.save(); - ctx.fillStyle = '#FFF' - ctx.globalAlpha = 0.5; - ctx.fillRect(30,30,90,90); - - ctx.restore(); - ctx.fillRect(45,45,60,60); - - ctx.restore(); - ctx.fillRect(60,60,30,30); + + ctx.save(); + ctx.fillStyle = '#FFF' + ctx.globalAlpha = 0.5; + ctx.fillRect(30,30,90,90); + + ctx.restore(); + ctx.fillRect(45,45,60,60); + + ctx.restore(); + ctx.fillRect(60,60,30,30); }; tests['path through fillRect/strokeRect/clearRect'] = function(ctx){ @@ -1208,12 +1208,12 @@ tests['shadowBlur'] = function(ctx){ ctx.stroke(); ctx.shadowBlur = 0; - + ctx.beginPath(); ctx.lineTo(20,180); ctx.lineTo(100,180); ctx.stroke(); - + ctx.fillRect(150,150,20,20); }; @@ -1234,12 +1234,12 @@ tests['shadowColor'] = function(ctx){ ctx.stroke(); ctx.shadowBlur = 0; - + ctx.beginPath(); ctx.lineTo(20,180); ctx.lineTo(100,180); ctx.stroke(); - + ctx.fillRect(150,150,20,20); }; @@ -1262,12 +1262,12 @@ tests['shadowOffset{X,Y}'] = function(ctx){ ctx.stroke(); ctx.shadowBlur = 0; - + ctx.beginPath(); ctx.lineTo(20,180); ctx.lineTo(100,180); ctx.stroke(); - + ctx.fillRect(150,150,20,20); }; @@ -1290,12 +1290,12 @@ tests['shadowOffset{X,Y} large'] = function(ctx){ ctx.stroke(); ctx.shadowBlur = 0; - + ctx.beginPath(); ctx.lineTo(20,180); ctx.lineTo(100,180); ctx.stroke(); - + ctx.fillRect(150,150,20,20); }; @@ -1318,12 +1318,12 @@ tests['shadowOffset{X,Y} negative'] = function(ctx){ ctx.stroke(); ctx.shadowBlur = 0; - + ctx.beginPath(); ctx.lineTo(20,180); ctx.lineTo(100,180); ctx.stroke(); - + ctx.fillRect(150,150,20,20); }; @@ -1350,12 +1350,12 @@ tests['shadowOffset{X,Y} transform'] = function(ctx){ ctx.stroke(); ctx.shadowBlur = 0; - + ctx.beginPath(); ctx.lineTo(20,180); ctx.lineTo(100,180); ctx.stroke(); - + ctx.fillRect(150,150,20,20); }; @@ -1378,12 +1378,12 @@ tests['shadowBlur values'] = function(ctx){ ctx.stroke(); ctx.shadowColor = 'rgba(0,0,0,0)'; - + ctx.beginPath(); ctx.lineTo(20,180); ctx.lineTo(100,180); ctx.stroke(); - + ctx.fillRect(150,150,20,20); }; @@ -1406,12 +1406,12 @@ tests['shadow strokeRect()'] = function(ctx){ ctx.stroke(); ctx.shadowColor = 'rgba(0,0,0,0)'; - + ctx.beginPath(); ctx.lineTo(20,180); ctx.lineTo(100,180); ctx.stroke(); - + ctx.strokeRect(150,150,20,20); }; @@ -1435,12 +1435,12 @@ tests['shadow fill()'] = function(ctx){ ctx.stroke(); ctx.shadowColor = 'rgba(0,0,0,0)'; - + ctx.beginPath(); ctx.lineTo(20,180); ctx.lineTo(100,180); ctx.stroke(); - + ctx.strokeRect(150,150,20,20); }; @@ -1464,12 +1464,12 @@ tests['shadow stroke()'] = function(ctx){ ctx.stroke(); ctx.shadowColor = 'rgba(0,0,0,0)'; - + ctx.beginPath(); ctx.lineTo(20,180); ctx.lineTo(100,180); ctx.stroke(); - + ctx.strokeRect(150,150,20,20); }; @@ -1693,7 +1693,7 @@ tests['drawImage(img,0,0) clip'] = function(ctx, done){ 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) + ',' + + ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' + Math.floor(255-42.5*j) + ',0)'; ctx.fillRect(j*25,i*25,25,25); } @@ -1705,7 +1705,7 @@ tests['putImageData()'] = function(ctx){ 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) + ',' + + ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' + Math.floor(255-42.5*j) + ',0)'; ctx.fillRect(j*25,i*25,25,25); } @@ -1717,7 +1717,7 @@ tests['putImageData() 2'] = function(ctx){ 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) + ',' + + ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' + Math.floor(255-42.5*j) + ',0)'; ctx.fillRect(j*25,i*25,25,25); } @@ -1729,7 +1729,7 @@ tests['putImageData() 3'] = function(ctx){ 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) + ',' + + ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' + Math.floor(255-42.5*j) + ',0)'; ctx.fillRect(j*25,i*25,25,25); } @@ -1742,7 +1742,7 @@ tests['putImageData() 4'] = function(ctx){ 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) + ',' + + ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' + Math.floor(255-42.5*j) + ',0)'; ctx.fillRect(j*25,i*25,25,25); } @@ -1755,7 +1755,7 @@ tests['putImageData() 5'] = function(ctx){ 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) + ',' + + ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' + Math.floor(255-42.5*j) + ',0)'; ctx.fillRect(j*25,i*25,25,25); } @@ -1768,7 +1768,7 @@ tests['putImageData() 6'] = function(ctx){ tests['putImageData() 7'] = function(ctx){ for (i=0;i<6;i++){ for (j=0;j<6;j++){ - ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' + + ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' + Math.floor(255-42.5*j) + ',0)'; ctx.fillRect(j*25,i*25,25,25); } @@ -1782,7 +1782,7 @@ tests['putImageData() 7'] = function(ctx){ tests['putImageData() 8'] = function(ctx){ for (i=0;i<6;i++){ for (j=0;j<6;j++){ - ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' + + ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' + Math.floor(255-42.5*j) + ',0)'; ctx.fillRect(j*25,i*25,25,25); } @@ -1795,7 +1795,7 @@ tests['putImageData() 8'] = function(ctx){ tests['putImageData() 9'] = function(ctx){ for (i=0;i<6;i++){ for (j=0;j<6;j++){ - ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' + + ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' + Math.floor(255-42.5*j) + ',0)'; ctx.fillRect(j*25,i*25,25,25); } @@ -1867,7 +1867,7 @@ tests['putImageData() png data'] = function(ctx, done){ ctx.putImageData(imageData,50,50); done(); }; - + img.onerror = function(){} img.src = 'state.png'; @@ -1980,7 +1980,7 @@ tests['lineDashOffset'] = function(ctx, done){ tests['fillStyle=\'hsl(...)\''] = function(ctx){ for (i=0;i<6;i++){ for (j=0;j<6;j++){ - ctx.fillStyle = 'hsl(' + (360-60*i) + ',' + + ctx.fillStyle = 'hsl(' + (360-60*i) + ',' + (100-16.66*j) + '%,' + (50+(i+j)*(50/12)) + '%)'; ctx.fillRect(j*25,i*25,25,25); } @@ -1990,9 +1990,8 @@ tests['fillStyle=\'hsl(...)\''] = function(ctx){ tests['fillStyle=\'hsla(...)\''] = function(ctx){ for (i=0;i<6;i++){ for (j=0;j<6;j++){ - ctx.fillStyle = 'hsla(' + (360-60*i) + ',' + + ctx.fillStyle = 'hsla(' + (360-60*i) + ',' + (100-16.66*j) + '%,50%,' + (1-0.16*j) + ')'; - console.log((100-16.66*j)); ctx.fillRect(j*25,i*25,25,25); } } diff --git a/test/server.js b/test/server.js index 19e2350..d376e69 100644 --- a/test/server.js +++ b/test/server.js @@ -6,6 +6,7 @@ var express = require('express') , Canvas = require('../lib/canvas') , Image = Canvas.Image + , bodyParser = require('body-parser') , app = express(); // Config @@ -15,12 +16,8 @@ app.set('view engine', 'jade'); // Middleware -app.use(express.favicon()); -app.use(express.logger('dev')); -app.use(express.bodyParser()); -app.use(app.router); +app.use(bodyParser.json()); app.use(express.static(__dirname + '/public')); -app.use(express.errorHandler()); // Routes diff --git a/test/views/layout.jade b/test/views/layout.jade index 132827e..64c5567 100644 --- a/test/views/layout.jade +++ b/test/views/layout.jade @@ -1,4 +1,4 @@ -!!! +doctype html head title node-canvas