From ad0a41f41e394f85ee94c8b17b470f0350fa1ab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s-Combarro=20=27piranna?= Date: Mon, 19 Nov 2018 16:24:55 +0100 Subject: [PATCH 01/26] Extracted code for FbDev support from #571 to make it easier to review and approve. It's a squashed version with the final code, for references on the development history and commit logs go to the original issue. --- binding.gyp | 10 ++- examples/simple_fbdev.js | 39 ++++++++++ src/Backends.cc | 8 +++ src/Canvas.cc | 4 +- src/backend/Backend.cc | 7 ++ src/backend/Backend.h | 1 + src/backend/FBDevBackend.cc | 140 ++++++++++++++++++++++++++++++++++++ src/backend/FBDevBackend.h | 35 +++++++++ 8 files changed, 242 insertions(+), 2 deletions(-) create mode 100644 examples/simple_fbdev.js create mode 100644 src/backend/FBDevBackend.cc create mode 100644 src/backend/FBDevBackend.h diff --git a/binding.gyp b/binding.gyp index e770b86c7..5d11e00d1 100644 --- a/binding.gyp +++ b/binding.gyp @@ -145,6 +145,11 @@ 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES' } }], + ['has_FBDev=="true"', + { + 'defines': ['HAS_FBDEV'], + 'sources': ['src/backend/FBDevBackend.cc'] + }], ['with_jpeg=="true"', { 'defines': [ 'HAVE_JPEG' @@ -216,7 +221,10 @@ }] ] }] - ] + ], + 'variables': { + 'has_FBDev%': 'true', + } } ] } diff --git a/examples/simple_fbdev.js b/examples/simple_fbdev.js new file mode 100644 index 000000000..a1f28df5c --- /dev/null +++ b/examples/simple_fbdev.js @@ -0,0 +1,39 @@ +#!/usr/bin/env node + +/** +* Module dependencies. +*/ + +const fs = require('fs') +const { join } = require('path') + +const { backends: { FBDevBackend }, Canvas } = require('..') + +const squareSize = 100 + +var device = process.argv[2] || '/dev/fb0' + +var backend = new FBDevBackend(device) +var canvas = new Canvas(backend) +var ctx = canvas.getContext('2d') + +var offsetX = canvas.width - squareSize +var offsetY = canvas.height - squareSize + +ctx.fillStyle = '#FF0000' +ctx.fillRect(0, 0, squareSize, squareSize) + +ctx.fillStyle = '#00FF00' +ctx.fillRect(offsetX, 0, squareSize, squareSize) + +ctx.fillStyle = '#0000FF' +ctx.fillRect(0, offsetY, squareSize, squareSize) + +ctx.fillStyle = '#FFFFFF' +ctx.fillRect(offsetX, offsetY, squareSize, squareSize) + +console.log('Width: ' + canvas.width + ', Height: ' + canvas.height) + +var outPath = join(__dirname, 'rectangle.png') + +canvas.createPNGStream().pipe(fs.createWriteStream(outPath)) diff --git a/src/Backends.cc b/src/Backends.cc index 6490ab6e0..880ae076d 100644 --- a/src/Backends.cc +++ b/src/Backends.cc @@ -4,6 +4,10 @@ #include "backend/PdfBackend.h" #include "backend/SvgBackend.h" +#ifdef HAS_FBDEV +#include "backend/FBDevBackend.h" +#endif + using namespace v8; void Backends::Initialize(Handle target) { @@ -14,5 +18,9 @@ void Backends::Initialize(Handle target) { PdfBackend::Initialize(obj); SvgBackend::Initialize(obj); + #ifdef HAS_FBDEV + FBDevBackend::Initialize(obj); + #endif + target->Set(Nan::New("Backends").ToLocalChecked(), obj); } diff --git a/src/Canvas.cc b/src/Canvas.cc index 1ffed6b3d..e7d97259b 100644 --- a/src/Canvas.cc +++ b/src/Canvas.cc @@ -25,6 +25,7 @@ #include "JPEGStream.h" #endif +#include "backend/FBDevBackend.h" #include "backend/ImageBackend.h" #include "backend/PdfBackend.h" #include "backend/SvgBackend.h" @@ -104,7 +105,8 @@ NAN_METHOD(Canvas::New) { backend = new ImageBackend(width, height); } else if (info[0]->IsObject()) { - if (Nan::New(ImageBackend::constructor)->HasInstance(info[0]) || + if (Nan::New(FBDevBackend::constructor)->HasInstance(info[0]) || + Nan::New(ImageBackend::constructor)->HasInstance(info[0]) || Nan::New(PdfBackend::constructor)->HasInstance(info[0]) || Nan::New(SvgBackend::constructor)->HasInstance(info[0])) { backend = Nan::ObjectWrap::Unwrap(Nan::To(info[0]).ToLocalChecked()); diff --git a/src/backend/Backend.cc b/src/backend/Backend.cc index 34ef541f5..0ea3b7af2 100644 --- a/src/backend/Backend.cc +++ b/src/backend/Backend.cc @@ -1,6 +1,13 @@ #include "Backend.h" +Backend::Backend(string name) + : name(name) + , width(0) + , height(0) + , surface(NULL) + , canvas(NULL) +{} Backend::Backend(string name, int width, int height) : name(name) , width(width) diff --git a/src/backend/Backend.h b/src/backend/Backend.h index 75ae37aad..5bbc80f4e 100644 --- a/src/backend/Backend.h +++ b/src/backend/Backend.h @@ -28,6 +28,7 @@ class Backend : public Nan::ObjectWrap cairo_surface_t* surface; Canvas* canvas; + Backend(string name); Backend(string name, int width, int height); static void init(const Nan::FunctionCallbackInfo &info); static Backend *construct(int width, int height){ return nullptr; } diff --git a/src/backend/FBDevBackend.cc b/src/backend/FBDevBackend.cc new file mode 100644 index 000000000..581be177c --- /dev/null +++ b/src/backend/FBDevBackend.cc @@ -0,0 +1,140 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "FBDevBackend.h" + + +using namespace v8; + + +FBDevBackend::FBDevBackend(string deviceName) + : Backend("fbdev") +{ + // Open the file for reading and writing + this->fb_fd = open(deviceName.c_str(), O_RDWR); + + if(this->fb_fd == -1) + { + std::ostringstream o; + o << "cannot open framebuffer device \"" << deviceName << "\""; + throw FBDevBackendException(o.str()); + } + + this->FbDevIoctlHelper(FBIOGET_FSCREENINFO, &this->fb_finfo, + "Error reading fixed framebuffer information"); + + // Map the device to memory + this->fb_data = (unsigned char*) mmap(0, this->fb_finfo.smem_len, + PROT_READ | PROT_WRITE, MAP_SHARED, this->fb_fd, 0); + + if(this->fb_data == MAP_FAILED) + throw FBDevBackendException("Failed to map framebuffer device to memory"); +} + +FBDevBackend::~FBDevBackend() +{ + this->destroySurface(); + + munmap(this->fb_data, this->fb_finfo.smem_len); + close(this->fb_fd); +} + + +void FBDevBackend::FbDevIoctlHelper(unsigned long request, void* data, + string errmsg) +{ + if(ioctl(this->fb_fd, request, data) == -1) + throw FBDevBackendException(errmsg.c_str()); +} + + +cairo_surface_t* FBDevBackend::createSurface() +{ + struct fb_var_screeninfo fb_vinfo; + + this->FbDevIoctlHelper(FBIOGET_VSCREENINFO, &fb_vinfo, + "Error reading variable framebuffer information"); + + this->width = fb_vinfo.xres; + this->height = fb_vinfo.yres; + + // switch through bpp and decide on which format for the cairo surface to use + cairo_format_t format; + switch(fb_vinfo.bits_per_pixel) + { + case 16: format = CAIRO_FORMAT_RGB16_565; break; + case 32: format = CAIRO_FORMAT_ARGB32; break; + + default: + throw FBDevBackendException("Only valid formats are RGB16_565 & ARGB32"); + } + + // create cairo surface from data + this->surface = cairo_image_surface_create_for_data(this->fb_data, format, + this->width, this->height, fb_finfo.line_length); + + return this->surface; +} + + +void FBDevBackend::setWidth(int width) +{ + struct fb_var_screeninfo fb_vinfo; + + this->FbDevIoctlHelper(FBIOGET_VSCREENINFO, &fb_vinfo, + "Error reading variable framebuffer information"); + + fb_vinfo.xres = width; + + this->FbDevIoctlHelper(FBIOPUT_VSCREENINFO, &fb_vinfo, + "Error setting variable framebuffer information"); + + Backend::setWidth(width); +} +void FBDevBackend::setHeight(int height) +{ + struct fb_var_screeninfo fb_vinfo; + + this->FbDevIoctlHelper(FBIOGET_VSCREENINFO, &fb_vinfo, + "Error reading variable framebuffer information"); + + fb_vinfo.yres = height; + + this->FbDevIoctlHelper(FBIOPUT_VSCREENINFO, &fb_vinfo, + "Error setting variable framebuffer information"); + + Backend::setHeight(width); +} + + +Nan::Persistent FBDevBackend::constructor; + +void FBDevBackend::Initialize(Handle target) +{ + Nan::HandleScope scope; + + Local ctor = Nan::New(FBDevBackend::New); + FBDevBackend::constructor.Reset(ctor); + ctor->InstanceTemplate()->SetInternalFieldCount(1); + ctor->SetClassName(Nan::New("FBDevBackend").ToLocalChecked()); + target->Set(Nan::New("FBDevBackend").ToLocalChecked(), ctor->GetFunction()); +} + +NAN_METHOD(FBDevBackend::New) +{ + string fbDevice = "/dev/fb0"; + if(info[0]->IsString()) fbDevice = *String::Utf8Value(info[0].As()); + + FBDevBackend* backend = new FBDevBackend(fbDevice); + + backend->Wrap(info.This()); + info.GetReturnValue().Set(info.This()); +} diff --git a/src/backend/FBDevBackend.h b/src/backend/FBDevBackend.h new file mode 100644 index 000000000..55e90e51c --- /dev/null +++ b/src/backend/FBDevBackend.h @@ -0,0 +1,35 @@ +#ifndef __FBDEV_BACKEND_H__ +#define __FBDEV_BACKEND_H__ + #include +#include + #include + #include + #include "Backend.h" + using namespace std; + class FBDevBackend : public Backend +{ + private: + int fb_fd; + struct fb_fix_screeninfo fb_finfo; + unsigned char* fb_data; + ~FBDevBackend(); + void FbDevIoctlHelper(unsigned long request, void* data, string errmsg); + cairo_surface_t* createSurface(); + void setWidth(int width); + void setHeight(int height); + public: + FBDevBackend(string deviceName); + static Nan::Persistent constructor; + static void Initialize(v8::Handle target); + static NAN_METHOD(New); +}; + class FBDevBackendException : public std::exception +{ + private: + string err_msg; + public: + FBDevBackendException(const string msg) : err_msg(msg) {}; + ~FBDevBackendException() throw() {}; + const char *what() const throw() { return this->err_msg.c_str(); }; +}; + #endif From 3a1d55d6e8313731a296f447a712ad6877c67bdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s-Combarro=20=27piranna?= Date: Tue, 20 Nov 2018 02:01:53 +0100 Subject: [PATCH 02/26] Fix linting --- benchmarks/run.js | 10 +- lib/DOMMatrix.js | 96 +- lib/bindings.js | 4 +- lib/canvas.js | 86 +- lib/context2d.js | 152 +-- lib/image.js | 56 +- lib/jpegstream.js | 42 +- lib/parse-font.js | 19 +- lib/pdfstream.js | 38 +- lib/pngstream.js | 42 +- test/canvas.test.js | 2397 ++++++++++++++++++++------------------- test/dommatrix.test.js | 20 +- test/image.test.js | 180 +-- test/imageData.test.js | 14 +- util/win_jpeg_lookup.js | 6 +- 15 files changed, 1594 insertions(+), 1568 deletions(-) diff --git a/benchmarks/run.js b/benchmarks/run.js index e088e0692..4914ea97b 100644 --- a/benchmarks/run.js +++ b/benchmarks/run.js @@ -65,8 +65,8 @@ function done (benchmark, times, start, isAsync) { // node-canvas bm('fillStyle= name', function () { - ctx.fillStyle = "transparent"; -}); + ctx.fillStyle = 'transparent' +}) bm('lineTo()', function () { ctx.lineTo(0, 50) @@ -138,11 +138,11 @@ bm('moveTo() / arc() / stroke()', function () { ctx.beginPath() ctx.arc(75, 75, 50, 0, Math.PI * 2, true) // Outer circle ctx.moveTo(110, 75) - ctx.arc(75, 75, 35, 0, Math.PI, false) // Mouth + ctx.arc(75, 75, 35, 0, Math.PI, false) // Mouth ctx.moveTo(65, 65) - ctx.arc(60, 65, 5, 0, Math.PI * 2, true) // Left eye + ctx.arc(60, 65, 5, 0, Math.PI * 2, true) // Left eye ctx.moveTo(95, 65) - ctx.arc(90, 65, 5, 0, Math.PI * 2, true) // Right eye + ctx.arc(90, 65, 5, 0, Math.PI * 2, true) // Right eye ctx.stroke() }) diff --git a/lib/DOMMatrix.js b/lib/DOMMatrix.js index 3c5262449..cf4485395 100644 --- a/lib/DOMMatrix.js +++ b/lib/DOMMatrix.js @@ -2,7 +2,7 @@ // DOMMatrix per https://drafts.fxtf.org/geometry/#DOMMatrix -function DOMPoint(x, y, z, w) { +function DOMPoint (x, y, z, w) { if (!(this instanceof DOMPoint)) { throw new TypeError("Class constructors cannot be invoked without 'new'") } @@ -20,15 +20,15 @@ function DOMPoint(x, y, z, w) { } // Constants to index into _values (col-major) -const M11 = 0, M12 = 1, M13 = 2, M14 = 3 -const M21 = 4, M22 = 5, M23 = 6, M24 = 7 -const M31 = 8, M32 = 9, M33 = 10, M34 = 11 -const M41 = 12, M42 = 13, M43 = 14, M44 = 15 +const M11 = 0; const M12 = 1; const M13 = 2; const M14 = 3 +const M21 = 4; const M22 = 5; const M23 = 6; const M24 = 7 +const M31 = 8; const M32 = 9; const M33 = 10; const M34 = 11 +const M41 = 12; const M42 = 13; const M43 = 14; const M44 = 15 const DEGREE_PER_RAD = 180 / Math.PI const RAD_PER_DEGREE = Math.PI / 180 -function parseMatrix(init) { +function parseMatrix (init) { var parsed = init.replace(/matrix\(/, '') parsed = parsed.split(/,/, 7) // 6 + 1 to handle too many params if (parsed.length !== 6) throw new Error(`Failed to parse ${init}`) @@ -41,14 +41,14 @@ function parseMatrix(init) { ] } -function parseMatrix3d(init) { +function parseMatrix3d (init) { var parsed = init.replace(/matrix3d\(/, '') parsed = parsed.split(/,/, 17) // 16 + 1 to handle too many params if (parsed.length !== 16) throw new Error(`Failed to parse ${init}`) return parsed.map(parseFloat) } -function parseTransform(tform) { +function parseTransform (tform) { var type = tform.split(/\(/, 1)[0] switch (type) { case 'matrix': @@ -158,15 +158,15 @@ DOMMatrix.prototype.inspect = function (depth, options) { } DOMMatrix.prototype.toString = function () { - return this.is2D ? - `matrix(${this.a}, ${this.b}, ${this.c}, ${this.d}, ${this.e}, ${this.f})` : - `matrix3d(${this._values.join(', ')})` + return this.is2D + ? `matrix(${this.a}, ${this.b}, ${this.c}, ${this.d}, ${this.e}, ${this.f})` + : `matrix3d(${this._values.join(', ')})` } /** * Checks that `value` is a number and sets the value. */ -function setNumber2D(receiver, index, value) { +function setNumber2D (receiver, index, value) { if (typeof value !== 'number') throw new TypeError('Expected number') return receiver._values[index] = value } @@ -175,7 +175,7 @@ function setNumber2D(receiver, index, value) { * Checks that `value` is a number, sets `_is2D = false` if necessary and sets * the value. */ -function setNumber3D(receiver, index, value) { +function setNumber3D (receiver, index, value) { if (typeof value !== 'number') throw new TypeError('Expected number') if (index === M33 || index === M44) { if (value !== 1) receiver._is2D = false @@ -184,31 +184,31 @@ function setNumber3D(receiver, index, value) { } Object.defineProperties(DOMMatrix.prototype, { - m11: {get: function () { return this._values[M11] }, set: function (v) { return setNumber2D(this, M11, v) }}, - m12: {get: function () { return this._values[M12] }, set: function (v) { return setNumber2D(this, M12, v) }}, - m13: {get: function () { return this._values[M13] }, set: function (v) { return setNumber3D(this, M13, v) }}, - m14: {get: function () { return this._values[M14] }, set: function (v) { return setNumber3D(this, M14, v) }}, - m21: {get: function () { return this._values[M21] }, set: function (v) { return setNumber2D(this, M21, v) }}, - m22: {get: function () { return this._values[M22] }, set: function (v) { return setNumber2D(this, M22, v) }}, - m23: {get: function () { return this._values[M23] }, set: function (v) { return setNumber3D(this, M23, v) }}, - m24: {get: function () { return this._values[M24] }, set: function (v) { return setNumber3D(this, M24, v) }}, - m31: {get: function () { return this._values[M31] }, set: function (v) { return setNumber3D(this, M31, v) }}, - m32: {get: function () { return this._values[M32] }, set: function (v) { return setNumber3D(this, M32, v) }}, - m33: {get: function () { return this._values[M33] }, set: function (v) { return setNumber3D(this, M33, v) }}, - m34: {get: function () { return this._values[M34] }, set: function (v) { return setNumber3D(this, M34, v) }}, - m41: {get: function () { return this._values[M41] }, set: function (v) { return setNumber2D(this, M41, v) }}, - m42: {get: function () { return this._values[M42] }, set: function (v) { return setNumber2D(this, M42, v) }}, - m43: {get: function () { return this._values[M43] }, set: function (v) { return setNumber3D(this, M43, v) }}, - m44: {get: function () { return this._values[M44] }, set: function (v) { return setNumber3D(this, M44, v) }}, - - a: {get: function () { return this.m11 }, set: function (v) { return this.m11 = v }}, - b: {get: function () { return this.m12 }, set: function (v) { return this.m12 = v }}, - c: {get: function () { return this.m21 }, set: function (v) { return this.m21 = v }}, - d: {get: function () { return this.m22 }, set: function (v) { return this.m22 = v }}, - e: {get: function () { return this.m41 }, set: function (v) { return this.m41 = v }}, - f: {get: function () { return this.m42 }, set: function (v) { return this.m42 = v }}, - - is2D: {get: function () { return this._is2D }}, // read-only + m11: { get: function () { return this._values[M11] }, set: function (v) { return setNumber2D(this, M11, v) } }, + m12: { get: function () { return this._values[M12] }, set: function (v) { return setNumber2D(this, M12, v) } }, + m13: { get: function () { return this._values[M13] }, set: function (v) { return setNumber3D(this, M13, v) } }, + m14: { get: function () { return this._values[M14] }, set: function (v) { return setNumber3D(this, M14, v) } }, + m21: { get: function () { return this._values[M21] }, set: function (v) { return setNumber2D(this, M21, v) } }, + m22: { get: function () { return this._values[M22] }, set: function (v) { return setNumber2D(this, M22, v) } }, + m23: { get: function () { return this._values[M23] }, set: function (v) { return setNumber3D(this, M23, v) } }, + m24: { get: function () { return this._values[M24] }, set: function (v) { return setNumber3D(this, M24, v) } }, + m31: { get: function () { return this._values[M31] }, set: function (v) { return setNumber3D(this, M31, v) } }, + m32: { get: function () { return this._values[M32] }, set: function (v) { return setNumber3D(this, M32, v) } }, + m33: { get: function () { return this._values[M33] }, set: function (v) { return setNumber3D(this, M33, v) } }, + m34: { get: function () { return this._values[M34] }, set: function (v) { return setNumber3D(this, M34, v) } }, + m41: { get: function () { return this._values[M41] }, set: function (v) { return setNumber2D(this, M41, v) } }, + m42: { get: function () { return this._values[M42] }, set: function (v) { return setNumber2D(this, M42, v) } }, + m43: { get: function () { return this._values[M43] }, set: function (v) { return setNumber3D(this, M43, v) } }, + m44: { get: function () { return this._values[M44] }, set: function (v) { return setNumber3D(this, M44, v) } }, + + a: { get: function () { return this.m11 }, set: function (v) { return this.m11 = v } }, + b: { get: function () { return this.m12 }, set: function (v) { return this.m12 = v } }, + c: { get: function () { return this.m21 }, set: function (v) { return this.m21 = v } }, + d: { get: function () { return this.m22 }, set: function (v) { return this.m22 = v } }, + e: { get: function () { return this.m41 }, set: function (v) { return this.m41 = v } }, + f: { get: function () { return this.m42 }, set: function (v) { return this.m42 = v } }, + + is2D: { get: function () { return this._is2D } }, // read-only isIdentity: { get: function () { @@ -226,7 +226,7 @@ Object.defineProperties(DOMMatrix.prototype, { * @param {Float64Array} values Value to assign to `_values`. This is assigned * without copying (okay because all usages are followed by a multiply). */ -function newInstance(values) { +function newInstance (values) { var instance = Object.create(DOMMatrix.prototype) instance.constructor = DOMMatrix instance._is2D = true @@ -234,7 +234,7 @@ function newInstance(values) { return instance } -function multiply(A, B) { +function multiply (A, B) { var dest = new Float64Array(16) for (var i = 0; i < 4; i++) { for (var j = 0; j < 4; j++) { @@ -383,10 +383,10 @@ DOMMatrix.prototype.rotateAxisAngleSelf = function (x, y, z, angle) { // NB: This is the generic transform. If the axis is a major axis, there are // faster transforms. this._values = multiply([ - tx * x + c, tx * y + s * z, tx * z - s * y, 0, - tx * y - s * z, ty * y + c, ty * z + s * x, 0, - tx * z + s * y, ty * z - s * x, t * z * z + c, 0, - 0, 0, 0, 1 + tx * x + c, tx * y + s * z, tx * z - s * y, 0, + tx * y - s * z, ty * y + c, ty * z + s * x, 0, + tx * z + s * y, ty * z - s * x, t * z * z + c, 0, + 0, 0, 0, 1 ], this._values) if (x !== 0 || y !== 0) this._is2D = false return this @@ -422,7 +422,7 @@ DOMMatrix.prototype.skewYSelf = function (sy) { return this } -DOMMatrix.prototype.flipX = function () { +DOMMatrix.prototype.flipX = function () { return newInstance(multiply([ -1, 0, 0, 0, 0, 1, 0, 0, @@ -468,12 +468,12 @@ DOMMatrix.prototype.transformPoint = function (point) { return new DOMPoint(nx, ny, nz, nw) } -DOMMatrix.prototype.toFloat32Array = function () { +DOMMatrix.prototype.toFloat32Array = function () { return Float32Array.from(this._values) } -DOMMatrix.prototype.toFloat64Array = function () { +DOMMatrix.prototype.toFloat64Array = function () { return this._values.slice(0) } -module.exports = {DOMMatrix, DOMPoint} +module.exports = { DOMMatrix, DOMPoint } diff --git a/lib/bindings.js b/lib/bindings.js index c0afc9841..95ee914f3 100644 --- a/lib/bindings.js +++ b/lib/bindings.js @@ -1,3 +1,3 @@ -'use strict'; +'use strict' -module.exports = require('../build/Release/canvas.node'); +module.exports = require('../build/Release/canvas.node') diff --git a/lib/canvas.js b/lib/canvas.js index ec16b92ea..72a729366 100644 --- a/lib/canvas.js +++ b/lib/canvas.js @@ -1,4 +1,4 @@ -'use strict'; +'use strict' /*! * Canvas @@ -25,9 +25,9 @@ const FORMATS = ['image/png', 'image/jpeg'] * @api public */ -Canvas.prototype.inspect = function(){ - return '[Canvas ' + this.width + 'x' + this.height + ']'; -}; +Canvas.prototype.inspect = function () { + return '[Canvas ' + this.width + 'x' + this.height + ']' +} /** * Get a context object. @@ -39,13 +39,13 @@ Canvas.prototype.inspect = function(){ */ Canvas.prototype.getContext = function (contextType, contextAttributes) { - if ('2d' == contextType) { - var ctx = this._context2d || (this._context2d = new Context2d(this, contextAttributes)); - this.context = ctx; - ctx.canvas = this; - return ctx; + if (contextType == '2d') { + var ctx = this._context2d || (this._context2d = new Context2d(this, contextAttributes)) + this.context = ctx + ctx.canvas = this + return ctx } -}; +} /** * Create a `PNGStream` for `this` canvas. @@ -66,9 +66,9 @@ Canvas.prototype.getContext = function (contextType, contextAttributes) { * @public */ Canvas.prototype.pngStream = -Canvas.prototype.createPNGStream = function(options){ - return new PNGStream(this, options); -}; +Canvas.prototype.createPNGStream = function (options) { + return new PNGStream(this, options) +} /** * Create a `PDFStream` for `this` canvas. @@ -77,9 +77,9 @@ Canvas.prototype.createPNGStream = function(options){ * @public */ Canvas.prototype.pdfStream = -Canvas.prototype.createPDFStream = function(){ - return new PDFStream(this); -}; +Canvas.prototype.createPDFStream = function () { + return new PDFStream(this) +} /** * Create a `JPEGStream` for `this` canvas. @@ -94,9 +94,9 @@ Canvas.prototype.createPDFStream = function(){ * @public */ Canvas.prototype.jpegStream = -Canvas.prototype.createJPEGStream = function(options){ - return new JPEGStream(this, options); -}; +Canvas.prototype.createJPEGStream = function (options) { + return new JPEGStream(this, options) +} /** * Returns a data URI. Pass a function for async operation (non-standard). @@ -110,7 +110,7 @@ Canvas.prototype.createJPEGStream = function(options){ * @return {String} data URL if synchronous (callback omitted) * @api public */ -Canvas.prototype.toDataURL = function(a1, a2, a3){ +Canvas.prototype.toDataURL = function (a1, a2, a3) { // valid arg patterns (args -> [type, opts, fn]): // [] -> ['image/png', null, null] // [qual] -> ['image/png', null, null] @@ -129,51 +129,51 @@ Canvas.prototype.toDataURL = function(a1, a2, a3){ // ['image/jpeg', opts] -> ['image/jpeg', opts, fn] // ['image/jpeg', qual] -> ['image/jpeg', {quality: qual}, fn] - var type = 'image/png'; - var opts = {}; - var fn; + var type = 'image/png' + var opts = {} + var fn - if ('function' === typeof a1) { - fn = a1; + if (typeof a1 === 'function') { + fn = a1 } else { - if ('string' === typeof a1 && FORMATS.includes(a1.toLowerCase())) { - type = a1.toLowerCase(); + if (typeof a1 === 'string' && FORMATS.includes(a1.toLowerCase())) { + type = a1.toLowerCase() } - if ('function' === typeof a2) { - fn = a2; + if (typeof a2 === 'function') { + fn = a2 } else { - if ('object' === typeof a2) { - opts = a2; - } else if ('number' === typeof a2) { - opts = {quality: Math.max(0, Math.min(1, a2))}; + if (typeof a2 === 'object') { + opts = a2 + } else if (typeof a2 === 'number') { + opts = { quality: Math.max(0, Math.min(1, a2)) } } - if ('function' === typeof a3) { - fn = a3; + if (typeof a3 === 'function') { + fn = a3 } else if (undefined !== a3) { - throw new TypeError(typeof a3 + ' is not a function'); + throw new TypeError(typeof a3 + ' is not a function') } } } if (this.width === 0 || this.height === 0) { // Per spec, if the bitmap has no pixels, return this string: - var str = "data:,"; + var str = 'data:,' if (fn) { - setTimeout(() => fn(null, str)); - return; + setTimeout(() => fn(null, str)) + return } else { - return str; + return str } } if (fn) { this.toBuffer((err, buf) => { - if (err) return fn(err); - fn(null, `data:${type};base64,${buf.toString('base64')}`); + if (err) return fn(err) + fn(null, `data:${type};base64,${buf.toString('base64')}`) }, type, opts) } else { return `data:${type};base64,${this.toBuffer(type).toString('base64')}` } -}; +} diff --git a/lib/context2d.js b/lib/context2d.js index b9c28e6ea..b5237593c 100644 --- a/lib/context2d.js +++ b/lib/context2d.js @@ -1,4 +1,4 @@ -'use strict'; +'use strict' /*! * Canvas - Context2d @@ -22,7 +22,7 @@ const DOMMatrix = require('./DOMMatrix').DOMMatrix * Text baselines. */ -var baselines = ['alphabetic', 'top', 'bottom', 'middle', 'ideographic', 'hanging']; +var baselines = ['alphabetic', 'top', 'bottom', 'middle', 'ideographic', 'hanging'] /** * Create a pattern from `Image` or `Canvas`. @@ -33,9 +33,9 @@ var baselines = ['alphabetic', 'top', 'bottom', 'middle', 'ideographic', 'hangin * @api public */ -Context2d.prototype.createPattern = function(image, repetition){ - return new CanvasPattern(image, repetition || 'repeat'); -}; +Context2d.prototype.createPattern = function (image, repetition) { + return new CanvasPattern(image, repetition || 'repeat') +} /** * Create a linear gradient at the given point `(x0, y0)` and `(x1, y1)`. @@ -48,9 +48,9 @@ Context2d.prototype.createPattern = function(image, repetition){ * @api public */ -Context2d.prototype.createLinearGradient = function(x0, y0, x1, y1){ - return new CanvasGradient(x0, y0, x1, y1); -}; +Context2d.prototype.createLinearGradient = function (x0, y0, x1, y1) { + return new CanvasGradient(x0, y0, x1, y1) +} /** * Create a radial gradient at the given point `(x0, y0)` and `(x1, y1)` @@ -66,9 +66,9 @@ Context2d.prototype.createLinearGradient = function(x0, y0, x1, y1){ * @api public */ -Context2d.prototype.createRadialGradient = function(x0, y0, r0, x1, y1, r1){ - return new CanvasGradient(x0, y0, r0, x1, y1, r1); -}; +Context2d.prototype.createRadialGradient = function (x0, y0, r0, x1, y1, r1) { + return new CanvasGradient(x0, y0, r0, x1, y1, r1) +} /** * Reset transform matrix to identity, then apply the given args. @@ -77,10 +77,10 @@ Context2d.prototype.createRadialGradient = function(x0, y0, r0, x1, y1, r1){ * @api public */ -Context2d.prototype.setTransform = function(){ - this.resetTransform(); - this.transform.apply(this, arguments); -}; +Context2d.prototype.setTransform = function () { + this.resetTransform() + this.transform.apply(this, arguments) +} Object.defineProperty(Context2d.prototype, 'currentTransform', { get: function () { @@ -103,16 +103,16 @@ Object.defineProperty(Context2d.prototype, 'currentTransform', { * @api public */ -Context2d.prototype.__defineSetter__('fillStyle', function(val){ - if (val instanceof CanvasGradient - || val instanceof CanvasPattern) { - this.lastFillStyle = val; - this._setFillPattern(val); +Context2d.prototype.__defineSetter__('fillStyle', function (val) { + if (val instanceof CanvasGradient || + val instanceof CanvasPattern) { + this.lastFillStyle = val + this._setFillPattern(val) } else { - this.lastFillStyle = undefined; - this._setFillColor(String(val)); + this.lastFillStyle = undefined + this._setFillColor(String(val)) } -}); +}) /** * Get previous fill style. @@ -121,9 +121,9 @@ Context2d.prototype.__defineSetter__('fillStyle', function(val){ * @api public */ -Context2d.prototype.__defineGetter__('fillStyle', function(){ - return this.lastFillStyle || this.fillColor; -}); +Context2d.prototype.__defineGetter__('fillStyle', function () { + return this.lastFillStyle || this.fillColor +}) /** * Set the stroke style with the given css color string. @@ -131,15 +131,15 @@ Context2d.prototype.__defineGetter__('fillStyle', function(){ * @api public */ -Context2d.prototype.__defineSetter__('strokeStyle', function(val){ - if (val instanceof CanvasGradient - || val instanceof CanvasPattern) { - this.lastStrokeStyle = val; - this._setStrokePattern(val); +Context2d.prototype.__defineSetter__('strokeStyle', function (val) { + if (val instanceof CanvasGradient || + val instanceof CanvasPattern) { + this.lastStrokeStyle = val + this._setStrokePattern(val) } else { - this._setStrokeColor(String(val)); + this._setStrokeColor(String(val)) } -}); +}) /** * Get previous stroke style. @@ -148,9 +148,9 @@ Context2d.prototype.__defineSetter__('strokeStyle', function(val){ * @api public */ -Context2d.prototype.__defineGetter__('strokeStyle', function(){ - return this.lastStrokeStyle || this.strokeColor; -}); +Context2d.prototype.__defineGetter__('strokeStyle', function () { + return this.lastStrokeStyle || this.strokeColor +}) /** * Set font. @@ -159,21 +159,21 @@ Context2d.prototype.__defineGetter__('strokeStyle', function(){ * @api public */ -Context2d.prototype.__defineSetter__('font', function(val){ - if (!val) return; - if ('string' == typeof val) { - var font; +Context2d.prototype.__defineSetter__('font', function (val) { + if (!val) return + if (typeof val === 'string') { + var font if (font = parseFont(val)) { - this.lastFontString = val; + this.lastFontString = val this._setFont( - font.weight + font.weight , font.style , font.size , font.unit - , font.family); + , font.family) } } -}); +}) /** * Get the current font. @@ -181,9 +181,9 @@ Context2d.prototype.__defineSetter__('font', function(val){ * @api public */ -Context2d.prototype.__defineGetter__('font', function(){ - return this.lastFontString || '10px sans-serif'; -}); +Context2d.prototype.__defineGetter__('font', function () { + return this.lastFontString || '10px sans-serif' +}) /** * Set text baseline. @@ -191,14 +191,14 @@ Context2d.prototype.__defineGetter__('font', function(){ * @api public */ -Context2d.prototype.__defineSetter__('textBaseline', function(val){ - if (!val) return; - var n = baselines.indexOf(val); +Context2d.prototype.__defineSetter__('textBaseline', function (val) { + if (!val) return + var n = baselines.indexOf(val) if (~n) { - this.lastBaseline = val; - this._setTextBaseline(n); + this.lastBaseline = val + this._setTextBaseline(n) } -}); +}) /** * Get the current baseline setting. @@ -206,9 +206,9 @@ Context2d.prototype.__defineSetter__('textBaseline', function(val){ * @api public */ -Context2d.prototype.__defineGetter__('textBaseline', function(){ - return this.lastBaseline || 'alphabetic'; -}); +Context2d.prototype.__defineGetter__('textBaseline', function () { + return this.lastBaseline || 'alphabetic' +}) /** * Set text alignment. @@ -216,24 +216,24 @@ Context2d.prototype.__defineGetter__('textBaseline', function(){ * @api public */ -Context2d.prototype.__defineSetter__('textAlign', function(val){ +Context2d.prototype.__defineSetter__('textAlign', function (val) { switch (val) { case 'center': - this._setTextAlignment(0); - this.lastTextAlignment = val; - break; + this._setTextAlignment(0) + this.lastTextAlignment = val + break case 'left': case 'start': - this._setTextAlignment(-1); - this.lastTextAlignment = val; - break; + this._setTextAlignment(-1) + this.lastTextAlignment = val + break case 'right': case 'end': - this._setTextAlignment(1); - this.lastTextAlignment = val; - break; + this._setTextAlignment(1) + this.lastTextAlignment = val + break } -}); +}) /** * Get the current font. @@ -242,9 +242,9 @@ Context2d.prototype.__defineSetter__('textAlign', function(val){ * @api public */ -Context2d.prototype.__defineGetter__('textAlign', function(){ - return this.lastTextAlignment || 'start'; -}); +Context2d.prototype.__defineGetter__('textAlign', function () { + return this.lastTextAlignment || 'start' +}) /** * Create `ImageData` with the given dimensions or @@ -261,13 +261,13 @@ Context2d.prototype.createImageData = function (width, height) { height = width.height width = width.width } - var Bpp = this.canvas.stride / this.canvas.width; + var Bpp = this.canvas.stride / this.canvas.width var nBytes = Bpp * width * height - var arr; - if (this.pixelFormat === "RGB16_565") { - arr = new Uint16Array(nBytes / 2); + var arr + if (this.pixelFormat === 'RGB16_565') { + arr = new Uint16Array(nBytes / 2) } else { - arr = new Uint8ClampedArray(nBytes); + arr = new Uint8ClampedArray(nBytes) } - return new ImageData(arr, width, height); + return new ImageData(arr, width, height) } diff --git a/lib/image.js b/lib/image.js index ff84f2b24..a64b7796d 100644 --- a/lib/image.js +++ b/lib/image.js @@ -1,4 +1,4 @@ -'use strict'; +'use strict' /*! * Canvas - Image @@ -12,15 +12,15 @@ const bindings = require('./bindings') const Image = module.exports = bindings.Image -const http = require("http") -const https = require("https") +const http = require('http') +const https = require('https') -const proto = Image.prototype; -const _getSource = proto.getSource; -const _setSource = proto.setSource; +const proto = Image.prototype +const _getSource = proto.getSource +const _setSource = proto.setSource -delete proto.getSource; -delete proto.setSource; +delete proto.getSource +delete proto.setSource Object.defineProperty(Image.prototype, 'src', { /** @@ -33,14 +33,14 @@ Object.defineProperty(Image.prototype, 'src', { * @param {String|Buffer} val filename, buffer, data URI, URL * @api public */ - set(val) { + set (val) { if (typeof val === 'string') { if (/^\s*data:/.test(val)) { // data: URI const commaI = val.indexOf(',') // 'base64' must come before the comma const isBase64 = val.lastIndexOf('base64', commaI) !== -1 const content = val.slice(commaI + 1) - setSource(this, Buffer.from(content, isBase64 ? 'base64' : 'utf8'), val); + setSource(this, Buffer.from(content, isBase64 ? 'base64' : 'utf8'), val) } else if (/^\s*https?:\/\//.test(val)) { // remote URL const onerror = err => { if (typeof this.onerror === 'function') { @@ -58,24 +58,24 @@ Object.defineProperty(Image.prototype, 'src', { const buffers = [] res.on('data', buffer => buffers.push(buffer)) res.on('end', () => { - setSource(this, Buffer.concat(buffers)); + setSource(this, Buffer.concat(buffers)) }) }).on('error', onerror) } else { // local file path assumed - setSource(this, val); + setSource(this, val) } } else if (Buffer.isBuffer(val)) { - setSource(this, val); + setSource(this, val) } }, - get() { + get () { // TODO https://github.com/Automattic/node-canvas/issues/118 - return getSource(this); + return getSource(this) }, configurable: true -}); +}) /** * Inspect image. @@ -86,19 +86,19 @@ Object.defineProperty(Image.prototype, 'src', { * @api public */ -Image.prototype.inspect = function(){ - return '[Image' - + (this.complete ? ':' + this.width + 'x' + this.height : '') - + (this.src ? ' ' + this.src : '') - + (this.complete ? ' complete' : '') - + ']'; -}; +Image.prototype.inspect = function () { + return '[Image' + + (this.complete ? ':' + this.width + 'x' + this.height : '') + + (this.src ? ' ' + this.src : '') + + (this.complete ? ' complete' : '') + + ']' +} -function getSource(img){ - return img._originalSource || _getSource.call(img); +function getSource (img) { + return img._originalSource || _getSource.call(img) } -function setSource(img, src, origSrc){ - _setSource.call(img, src); - img._originalSource = origSrc; +function setSource (img, src, origSrc) { + _setSource.call(img, src) + img._originalSource = origSrc } diff --git a/lib/jpegstream.js b/lib/jpegstream.js index e44ed4c65..92c5df80b 100644 --- a/lib/jpegstream.js +++ b/lib/jpegstream.js @@ -1,4 +1,4 @@ -'use strict'; +'use strict' /*! * Canvas - JPEGStream @@ -10,8 +10,8 @@ * Module dependencies. */ -var Readable = require('stream').Readable; -var util = require('util'); +var Readable = require('stream').Readable +var util = require('util') /** * Initialize a `JPEGStream` with the given `canvas`. @@ -29,37 +29,37 @@ var util = require('util'); * @private */ -var JPEGStream = module.exports = function JPEGStream(canvas, options) { +var JPEGStream = module.exports = function JPEGStream (canvas, options) { if (!(this instanceof JPEGStream)) { - throw new TypeError("Class constructors cannot be invoked without 'new'"); + throw new TypeError("Class constructors cannot be invoked without 'new'") } if (canvas.streamJPEGSync === undefined) { - throw new Error("node-canvas was built without JPEG support."); + throw new Error('node-canvas was built without JPEG support.') } - Readable.call(this); + Readable.call(this) - this.options = options; - this.canvas = canvas; -}; + this.options = options + this.canvas = canvas +} -util.inherits(JPEGStream, Readable); +util.inherits(JPEGStream, Readable) -function noop() {} +function noop () {} -JPEGStream.prototype._read = function _read() { +JPEGStream.prototype._read = function _read () { // For now we're not controlling the c++ code's data emission, so we only // call canvas.streamJPEGSync once and let it emit data at will. - this._read = noop; - var self = this; - self.canvas.streamJPEGSync(this.options, function(err, chunk){ + this._read = noop + var self = this + self.canvas.streamJPEGSync(this.options, function (err, chunk) { if (err) { - self.emit('error', err); + self.emit('error', err) } else if (chunk) { - self.push(chunk); + self.push(chunk) } else { - self.push(null); + self.push(null) } - }); -}; + }) +} diff --git a/lib/parse-font.js b/lib/parse-font.js index a149c709c..8fb9551a7 100644 --- a/lib/parse-font.js +++ b/lib/parse-font.js @@ -5,11 +5,16 @@ */ const weights = 'bold|bolder|lighter|[1-9]00' - , styles = 'italic|oblique' - , variants = 'small-caps' - , stretches = 'ultra-condensed|extra-condensed|condensed|semi-condensed|semi-expanded|expanded|extra-expanded|ultra-expanded' - , units = 'px|pt|pc|in|cm|mm|%|em|ex|ch|rem|q' - , string = '\'([^\']+)\'|"([^"]+)"|[\\w\\s-]+' + +const styles = 'italic|oblique' + +const variants = 'small-caps' + +const stretches = 'ultra-condensed|extra-condensed|condensed|semi-condensed|semi-expanded|expanded|extra-expanded|ultra-expanded' + +const units = 'px|pt|pc|in|cm|mm|%|em|ex|ch|rem|q' + +const string = '\'([^\']+)\'|"([^"]+)"|[\\w\\s-]+' // [ [ <‘font-style’> || || <‘font-weight’> || <‘font-stretch’> ]? // <‘font-size’> [ / <‘line-height’> ]? <‘font-family’> ] @@ -19,8 +24,8 @@ const styleRe = new RegExp(`(${styles}) +`, 'i') const variantRe = new RegExp(`(${variants}) +`, 'i') const stretchRe = new RegExp(`(${stretches}) +`, 'i') const sizeFamilyRe = new RegExp( - '([\\d\\.]+)(' + units + ') *' - + '((?:' + string + ')( *, *(?:' + string + '))*)') + '([\\d\\.]+)(' + units + ') *' + + '((?:' + string + ')( *, *(?:' + string + '))*)') /** * Cache font parsing. diff --git a/lib/pdfstream.js b/lib/pdfstream.js index 837a034c3..b056c6e47 100644 --- a/lib/pdfstream.js +++ b/lib/pdfstream.js @@ -1,4 +1,4 @@ -'use strict'; +'use strict' /*! * Canvas - PDFStream @@ -8,8 +8,8 @@ * Module dependencies. */ -var Readable = require('stream').Readable; -var util = require('util'); +var Readable = require('stream').Readable +var util = require('util') /** * Initialize a `PDFStream` with the given `canvas`. @@ -27,32 +27,32 @@ var util = require('util'); * @api public */ -var PDFStream = module.exports = function PDFStream(canvas) { +var PDFStream = module.exports = function PDFStream (canvas) { if (!(this instanceof PDFStream)) { - throw new TypeError("Class constructors cannot be invoked without 'new'"); + throw new TypeError("Class constructors cannot be invoked without 'new'") } - Readable.call(this); + Readable.call(this) - this.canvas = canvas; -}; + this.canvas = canvas +} -util.inherits(PDFStream, Readable); +util.inherits(PDFStream, Readable) -function noop() {} +function noop () {} -PDFStream.prototype._read = function _read() { +PDFStream.prototype._read = function _read () { // For now we're not controlling the c++ code's data emission, so we only // call canvas.streamPDFSync once and let it emit data at will. - this._read = noop; - var self = this; - self.canvas.streamPDFSync(function(err, chunk, len){ + this._read = noop + var self = this + self.canvas.streamPDFSync(function (err, chunk, len) { if (err) { - self.emit('error', err); + self.emit('error', err) } else if (len) { - self.push(chunk); + self.push(chunk) } else { - self.push(null); + self.push(null) } - }); -}; + }) +} diff --git a/lib/pngstream.js b/lib/pngstream.js index 3a03c78c7..7fac31129 100644 --- a/lib/pngstream.js +++ b/lib/pngstream.js @@ -1,4 +1,4 @@ -'use strict'; +'use strict' /*! * Canvas - PNGStream @@ -10,46 +10,46 @@ * Module dependencies. */ -var Readable = require('stream').Readable; -var util = require('util'); +var Readable = require('stream').Readable +var util = require('util') /** * @param {Canvas} canvas * @param {Object} options * @private */ -var PNGStream = module.exports = function PNGStream(canvas, options) { +var PNGStream = module.exports = function PNGStream (canvas, options) { if (!(this instanceof PNGStream)) { - throw new TypeError("Class constructors cannot be invoked without 'new'"); + throw new TypeError("Class constructors cannot be invoked without 'new'") } - Readable.call(this); + Readable.call(this) if (options && options.palette instanceof Uint8ClampedArray && options.palette.length % 4 !== 0) { - throw new Error("Palette length must be a multiple of 4."); + throw new Error('Palette length must be a multiple of 4.') } - this.canvas = canvas; - this.options = options || {}; -}; + this.canvas = canvas + this.options = options || {} +} -util.inherits(PNGStream, Readable); +util.inherits(PNGStream, Readable) -function noop() {} +function noop () {} -PNGStream.prototype._read = function _read() { +PNGStream.prototype._read = function _read () { // For now we're not controlling the c++ code's data emission, so we only // call canvas.streamPNGSync once and let it emit data at will. - this._read = noop; - var self = this; - self.canvas.streamPNGSync(function(err, chunk, len){ + this._read = noop + var self = this + self.canvas.streamPNGSync(function (err, chunk, len) { if (err) { - self.emit('error', err); + self.emit('error', err) } else if (len) { - self.push(chunk); + self.push(chunk) } else { - self.push(null); + self.push(null) } - }, self.options); -}; + }, self.options) +} diff --git a/test/canvas.test.js b/test/canvas.test.js index 77e012648..3239d1b85 100644 --- a/test/canvas.test.js +++ b/test/canvas.test.js @@ -18,136 +18,140 @@ const Readable = require('stream').Readable describe('Canvas', function () { it('Prototype and ctor are well-shaped, don\'t hit asserts on accessors (GH-803)', function () { - const Canvas = require('../').Canvas; - var c = new Canvas(10, 10); - assert.throws(function () { Canvas.prototype.width; }, /incompatible receiver/); - assert(!c.hasOwnProperty('width')); - assert('width' in c); - assert(Canvas.prototype.hasOwnProperty('width')); - }); + const Canvas = require('../').Canvas + var c = new Canvas(10, 10) + assert.throws(function () { Canvas.prototype.width }, /incompatible receiver/) + assert(!c.hasOwnProperty('width')) + assert('width' in c) + assert(Canvas.prototype.hasOwnProperty('width')) + }) it('.parseFont()', function () { var tests = [ - '20px Arial' - , { size: 20, unit: 'px', family: 'Arial' } - , '20pt Arial' - , { size: 26.666666666666668, unit: 'pt', family: 'Arial' } - , '20.5pt Arial' - , { size: 27.333333333333332, unit: 'pt', family: 'Arial' } - , '20% Arial' - , { size: 20, unit: '%', family: 'Arial' } // TODO I think this is a bad assertion - ZB 23-Jul-2017 - , '20mm Arial' - , { size: 75.59055118110237, unit: 'mm', family: 'Arial' } - , '20px serif' - , { size: 20, unit: 'px', family: 'serif' } - , '20px sans-serif' - , { size: 20, unit: 'px', family: 'sans-serif' } - , '20px monospace' - , { size: 20, unit: 'px', family: 'monospace' } - , '50px Arial, sans-serif' - , { size: 50, unit: 'px', family: 'Arial,sans-serif' } - , 'bold italic 50px Arial, sans-serif' - , { style: 'italic', weight: 'bold', size: 50, unit: 'px', family: 'Arial,sans-serif' } - , '50px Helvetica , Arial, sans-serif' - , { size: 50, unit: 'px', family: 'Helvetica,Arial,sans-serif' } - , '50px "Helvetica Neue", sans-serif' - , { size: 50, unit: 'px', family: 'Helvetica Neue,sans-serif' } - , '50px "Helvetica Neue", "foo bar baz" , sans-serif' - , { size: 50, unit: 'px', family: 'Helvetica Neue,foo bar baz,sans-serif' } - , "50px 'Helvetica Neue'" - , { size: 50, unit: 'px', family: 'Helvetica Neue' } - , 'italic 20px Arial' - , { size: 20, unit: 'px', style: 'italic', family: 'Arial' } - , 'oblique 20px Arial' - , { size: 20, unit: 'px', style: 'oblique', family: 'Arial' } - , 'normal 20px Arial' - , { size: 20, unit: 'px', style: 'normal', family: 'Arial' } - , '300 20px Arial' - , { size: 20, unit: 'px', weight: '300', family: 'Arial' } - , '800 20px Arial' - , { size: 20, unit: 'px', weight: '800', family: 'Arial' } - , 'bolder 20px Arial' - , { size: 20, unit: 'px', weight: 'bolder', family: 'Arial' } - , 'lighter 20px Arial' - , { size: 20, unit: 'px', weight: 'lighter', family: 'Arial' } - , 'normal normal normal 16px Impact' - , { size: 16, unit: 'px', weight: 'normal', family: 'Impact', style: 'normal', variant: 'normal' } - , 'italic small-caps bolder 16px cursive' - , { size: 16, unit: 'px', style: 'italic', variant: 'small-caps', weight: 'bolder', family: 'cursive' } - , '20px "new century schoolbook", serif' - , { size: 20, unit: 'px', family: 'new century schoolbook,serif' } - , '20px "Arial bold 300"' // synthetic case with weight keyword inside family - , { size: 20, unit: 'px', family: 'Arial bold 300', variant: 'normal' } - ]; + '20px Arial', + { size: 20, unit: 'px', family: 'Arial' }, + '20pt Arial', + { size: 26.666666666666668, unit: 'pt', family: 'Arial' }, + '20.5pt Arial', + { size: 27.333333333333332, unit: 'pt', family: 'Arial' }, + '20% Arial', + { size: 20, unit: '%', family: 'Arial' }, // TODO I think this is a bad assertion - ZB 23-Jul-2017 + '20mm Arial', + { size: 75.59055118110237, unit: 'mm', family: 'Arial' }, + '20px serif', + { size: 20, unit: 'px', family: 'serif' }, + '20px sans-serif', + { size: 20, unit: 'px', family: 'sans-serif' }, + '20px monospace', + { size: 20, unit: 'px', family: 'monospace' }, + '50px Arial, sans-serif', + { size: 50, unit: 'px', family: 'Arial,sans-serif' }, + 'bold italic 50px Arial, sans-serif', + { style: 'italic', weight: 'bold', size: 50, unit: 'px', family: 'Arial,sans-serif' }, + '50px Helvetica , Arial, sans-serif', + { size: 50, unit: 'px', family: 'Helvetica,Arial,sans-serif' }, + '50px "Helvetica Neue", sans-serif', + { size: 50, unit: 'px', family: 'Helvetica Neue,sans-serif' }, + '50px "Helvetica Neue", "foo bar baz" , sans-serif', + { size: 50, unit: 'px', family: 'Helvetica Neue,foo bar baz,sans-serif' }, + "50px 'Helvetica Neue'", + { size: 50, unit: 'px', family: 'Helvetica Neue' }, + 'italic 20px Arial', + { size: 20, unit: 'px', style: 'italic', family: 'Arial' }, + 'oblique 20px Arial', + { size: 20, unit: 'px', style: 'oblique', family: 'Arial' }, + 'normal 20px Arial', + { size: 20, unit: 'px', style: 'normal', family: 'Arial' }, + '300 20px Arial', + { size: 20, unit: 'px', weight: '300', family: 'Arial' }, + '800 20px Arial', + { size: 20, unit: 'px', weight: '800', family: 'Arial' }, + 'bolder 20px Arial', + { size: 20, unit: 'px', weight: 'bolder', family: 'Arial' }, + 'lighter 20px Arial', + { size: 20, unit: 'px', weight: 'lighter', family: 'Arial' }, + 'normal normal normal 16px Impact', + { size: 16, unit: 'px', weight: 'normal', family: 'Impact', style: 'normal', variant: 'normal' }, + 'italic small-caps bolder 16px cursive', + { size: 16, unit: 'px', style: 'italic', variant: 'small-caps', weight: 'bolder', family: 'cursive' }, + '20px "new century schoolbook", serif', + { size: 20, unit: 'px', family: 'new century schoolbook,serif' }, + '20px "Arial bold 300"', // synthetic case with weight keyword inside family + { size: 20, unit: 'px', family: 'Arial bold 300', variant: 'normal' } + ] for (var i = 0, len = tests.length; i < len; ++i) { var str = tests[i++] - , expected = tests[i] - , actual = parseFont(str); - if (!expected.style) expected.style = 'normal'; - if (!expected.weight) expected.weight = 'normal'; - if (!expected.stretch) expected.stretch = 'normal'; - if (!expected.variant) expected.variant = 'normal'; + var expected = tests[i] - assert.deepEqual(actual, expected, 'Failed to parse: ' + str); + var actual = parseFont(str) + + if (!expected.style) expected.style = 'normal' + if (!expected.weight) expected.weight = 'normal' + if (!expected.stretch) expected.stretch = 'normal' + if (!expected.variant) expected.variant = 'normal' + + assert.deepEqual(actual, expected, 'Failed to parse: ' + str) } - }); + }) it('registerFont', function () { // Minimal test to make sure nothing is thrown - registerFont('./examples/pfennigFont/Pfennig.ttf', {family: 'Pfennig'}) - registerFont('./examples/pfennigFont/PfennigBold.ttf', {family: 'Pfennig', weight: 'bold'}) - }); + registerFont('./examples/pfennigFont/Pfennig.ttf', { family: 'Pfennig' }) + registerFont('./examples/pfennigFont/PfennigBold.ttf', { family: 'Pfennig', weight: 'bold' }) + }) it('color serialization', function () { var canvas = createCanvas(200, 200) - , ctx = canvas.getContext('2d'); - ['fillStyle', 'strokeStyle', 'shadowColor'].forEach(function(prop){ - ctx[prop] = '#FFFFFF'; - assert.equal('#ffffff', ctx[prop], prop + ' #FFFFFF -> #ffffff, got ' + ctx[prop]); + var ctx = canvas.getContext('2d'); + + ['fillStyle', 'strokeStyle', 'shadowColor'].forEach(function (prop) { + ctx[prop] = '#FFFFFF' + assert.equal('#ffffff', ctx[prop], prop + ' #FFFFFF -> #ffffff, got ' + ctx[prop]) - ctx[prop] = '#FFF'; - assert.equal('#ffffff', ctx[prop], prop + ' #FFF -> #ffffff, got ' + ctx[prop]); + ctx[prop] = '#FFF' + assert.equal('#ffffff', ctx[prop], prop + ' #FFF -> #ffffff, got ' + ctx[prop]) - ctx[prop] = 'rgba(128, 200, 128, 1)'; - assert.equal('#80c880', ctx[prop], prop + ' rgba(128, 200, 128, 1) -> #80c880, got ' + ctx[prop]); + ctx[prop] = 'rgba(128, 200, 128, 1)' + assert.equal('#80c880', ctx[prop], prop + ' rgba(128, 200, 128, 1) -> #80c880, got ' + ctx[prop]) - ctx[prop] = 'rgba(128,80,0,0.5)'; - assert.equal('rgba(128, 80, 0, 0.50)', ctx[prop], prop + ' rgba(128,80,0,0.5) -> rgba(128, 80, 0, 0.5), got ' + ctx[prop]); + ctx[prop] = 'rgba(128,80,0,0.5)' + assert.equal('rgba(128, 80, 0, 0.50)', ctx[prop], prop + ' rgba(128,80,0,0.5) -> rgba(128, 80, 0, 0.5), got ' + ctx[prop]) - ctx[prop] = 'rgba(128,80,0,0.75)'; - assert.equal('rgba(128, 80, 0, 0.75)', ctx[prop], prop + ' rgba(128,80,0,0.75) -> rgba(128, 80, 0, 0.75), got ' + ctx[prop]); + ctx[prop] = 'rgba(128,80,0,0.75)' + assert.equal('rgba(128, 80, 0, 0.75)', ctx[prop], prop + ' rgba(128,80,0,0.75) -> rgba(128, 80, 0, 0.75), got ' + ctx[prop]) - if ('shadowColor' == prop) return; + if (prop == 'shadowColor') return - var grad = ctx.createLinearGradient(0,0,0,150); - ctx[prop] = grad; - assert.strictEqual(grad, ctx[prop], prop + ' pattern getter failed'); - }); - }); + var grad = ctx.createLinearGradient(0, 0, 0, 150) + ctx[prop] = grad + assert.strictEqual(grad, ctx[prop], prop + ' pattern getter failed') + }) + }) it('color parser', function () { var canvas = createCanvas(200, 200) - , ctx = canvas.getContext('2d'); - ctx.fillStyle = '#ffccaa'; - assert.equal('#ffccaa', ctx.fillStyle); + var ctx = canvas.getContext('2d') + + ctx.fillStyle = '#ffccaa' + assert.equal('#ffccaa', ctx.fillStyle) - ctx.fillStyle = '#FFCCAA'; - assert.equal('#ffccaa', ctx.fillStyle); + ctx.fillStyle = '#FFCCAA' + assert.equal('#ffccaa', ctx.fillStyle) - ctx.fillStyle = '#FCA'; - assert.equal('#ffccaa', ctx.fillStyle); + ctx.fillStyle = '#FCA' + assert.equal('#ffccaa', ctx.fillStyle) - ctx.fillStyle = '#fff'; - ctx.fillStyle = '#FGG'; - assert.equal('#ff0000', ctx.fillStyle); + ctx.fillStyle = '#fff' + ctx.fillStyle = '#FGG' + assert.equal('#ff0000', ctx.fillStyle) - ctx.fillStyle = '#fff'; - ctx.fillStyle = 'afasdfasdf'; - assert.equal('#ffffff', ctx.fillStyle); + ctx.fillStyle = '#fff' + ctx.fillStyle = 'afasdfasdf' + assert.equal('#ffffff', ctx.fillStyle) // #rgba and #rrggbbaa ctx.fillStyle = '#ffccaa80' @@ -159,108 +163,109 @@ describe('Canvas', function () { ctx.fillStyle = '#BEAD' assert.equal('rgba(187, 238, 170, 0.87)', ctx.fillStyle) - ctx.fillStyle = 'rgb(255,255,255)'; - assert.equal('#ffffff', ctx.fillStyle); + ctx.fillStyle = 'rgb(255,255,255)' + assert.equal('#ffffff', ctx.fillStyle) - ctx.fillStyle = 'rgb(0,0,0)'; - assert.equal('#000000', ctx.fillStyle); + ctx.fillStyle = 'rgb(0,0,0)' + assert.equal('#000000', ctx.fillStyle) - ctx.fillStyle = 'rgb( 0 , 0 , 0)'; - assert.equal('#000000', ctx.fillStyle); + ctx.fillStyle = 'rgb( 0 , 0 , 0)' + assert.equal('#000000', ctx.fillStyle) - ctx.fillStyle = 'rgba( 0 , 0 , 0, 1)'; - assert.equal('#000000', ctx.fillStyle); + ctx.fillStyle = 'rgba( 0 , 0 , 0, 1)' + assert.equal('#000000', ctx.fillStyle) - ctx.fillStyle = 'rgba( 255, 200, 90, 0.5)'; - assert.equal('rgba(255, 200, 90, 0.50)', ctx.fillStyle); + ctx.fillStyle = 'rgba( 255, 200, 90, 0.5)' + assert.equal('rgba(255, 200, 90, 0.50)', ctx.fillStyle) - ctx.fillStyle = 'rgba( 255, 200, 90, 0.75)'; - assert.equal('rgba(255, 200, 90, 0.75)', ctx.fillStyle); + ctx.fillStyle = 'rgba( 255, 200, 90, 0.75)' + assert.equal('rgba(255, 200, 90, 0.75)', ctx.fillStyle) - ctx.fillStyle = 'rgba( 255, 200, 90, 0.7555)'; - assert.equal('rgba(255, 200, 90, 0.75)', ctx.fillStyle); + ctx.fillStyle = 'rgba( 255, 200, 90, 0.7555)' + assert.equal('rgba(255, 200, 90, 0.75)', ctx.fillStyle) - ctx.fillStyle = 'rgba( 255, 200, 90, .7555)'; - assert.equal('rgba(255, 200, 90, 0.75)', ctx.fillStyle); + ctx.fillStyle = 'rgba( 255, 200, 90, .7555)' + assert.equal('rgba(255, 200, 90, 0.75)', ctx.fillStyle) - ctx.fillStyle = 'rgb(0, 0, 9000)'; - assert.equal('#0000ff', ctx.fillStyle); + ctx.fillStyle = 'rgb(0, 0, 9000)' + assert.equal('#0000ff', ctx.fillStyle) - ctx.fillStyle = 'rgba(0, 0, 0, 42.42)'; - assert.equal('#000000', ctx.fillStyle); + ctx.fillStyle = 'rgba(0, 0, 0, 42.42)' + assert.equal('#000000', ctx.fillStyle) // hsl / hsla tests - ctx.fillStyle = 'hsl(0, 0%, 0%)'; - assert.equal('#000000', ctx.fillStyle); + ctx.fillStyle = 'hsl(0, 0%, 0%)' + assert.equal('#000000', ctx.fillStyle) - ctx.fillStyle = 'hsl(3600, -10%, -10%)'; - assert.equal('#000000', ctx.fillStyle); + ctx.fillStyle = 'hsl(3600, -10%, -10%)' + assert.equal('#000000', ctx.fillStyle) - ctx.fillStyle = 'hsl(10, 100%, 42%)'; - assert.equal('#d62400', ctx.fillStyle); + ctx.fillStyle = 'hsl(10, 100%, 42%)' + assert.equal('#d62400', ctx.fillStyle) - ctx.fillStyle = 'hsl(370, 120%, 42%)'; - assert.equal('#d62400', ctx.fillStyle); + ctx.fillStyle = 'hsl(370, 120%, 42%)' + assert.equal('#d62400', ctx.fillStyle) - ctx.fillStyle = 'hsl(0, 100%, 100%)'; - assert.equal('#ffffff', ctx.fillStyle); + ctx.fillStyle = 'hsl(0, 100%, 100%)' + assert.equal('#ffffff', ctx.fillStyle) - ctx.fillStyle = 'hsl(0, 150%, 150%)'; - assert.equal('#ffffff', ctx.fillStyle); + ctx.fillStyle = 'hsl(0, 150%, 150%)' + assert.equal('#ffffff', ctx.fillStyle) - ctx.fillStyle = 'hsl(237, 76%, 25%)'; - assert.equal('#0f1470', ctx.fillStyle); + ctx.fillStyle = 'hsl(237, 76%, 25%)' + assert.equal('#0f1470', ctx.fillStyle) - ctx.fillStyle = 'hsl(240, 73%, 25%)'; - assert.equal('#11116e', ctx.fillStyle); + ctx.fillStyle = 'hsl(240, 73%, 25%)' + assert.equal('#11116e', ctx.fillStyle) - ctx.fillStyle = 'hsl(262, 32%, 42%)'; - assert.equal('#62498d', ctx.fillStyle); + ctx.fillStyle = 'hsl(262, 32%, 42%)' + assert.equal('#62498d', ctx.fillStyle) - ctx.fillStyle = 'hsla(0, 0%, 0%, 1)'; - assert.equal('#000000', ctx.fillStyle); + ctx.fillStyle = 'hsla(0, 0%, 0%, 1)' + assert.equal('#000000', ctx.fillStyle) - ctx.fillStyle = 'hsla(0, 100%, 100%, 1)'; - assert.equal('#ffffff', ctx.fillStyle); + ctx.fillStyle = 'hsla(0, 100%, 100%, 1)' + assert.equal('#ffffff', ctx.fillStyle) - ctx.fillStyle = 'hsla(120, 25%, 75%, 0.5)'; - assert.equal('rgba(175, 207, 175, 0.50)', ctx.fillStyle); + ctx.fillStyle = 'hsla(120, 25%, 75%, 0.5)' + assert.equal('rgba(175, 207, 175, 0.50)', ctx.fillStyle) - ctx.fillStyle = 'hsla(240, 75%, 25%, 0.75)'; - assert.equal('rgba(16, 16, 112, 0.75)', ctx.fillStyle); + ctx.fillStyle = 'hsla(240, 75%, 25%, 0.75)' + assert.equal('rgba(16, 16, 112, 0.75)', ctx.fillStyle) - ctx.fillStyle = 'hsla(172.0, 33.00000e0%, 42%, 1)'; - assert.equal('#488e85', ctx.fillStyle); + ctx.fillStyle = 'hsla(172.0, 33.00000e0%, 42%, 1)' + assert.equal('#488e85', ctx.fillStyle) - ctx.fillStyle = 'hsl(124.5, 76.1%, 47.6%)'; - assert.equal('#1dd62b', ctx.fillStyle); + ctx.fillStyle = 'hsl(124.5, 76.1%, 47.6%)' + assert.equal('#1dd62b', ctx.fillStyle) - ctx.fillStyle = 'hsl(1.24e2, 760e-1%, 4.7e1%)'; - assert.equal('#1dd329', ctx.fillStyle); + ctx.fillStyle = 'hsl(1.24e2, 760e-1%, 4.7e1%)' + assert.equal('#1dd329', ctx.fillStyle) // case-insensitive (#235) - ctx.fillStyle = "sILveR"; - assert.equal(ctx.fillStyle, "#c0c0c0"); - }); + ctx.fillStyle = 'sILveR' + assert.equal(ctx.fillStyle, '#c0c0c0') + }) it('Canvas#type', function () { - var canvas = createCanvas(10, 10); - assert.equal(canvas.type, 'image'); - var canvas = createCanvas(10, 10, 'pdf'); - assert.equal(canvas.type, 'pdf'); - var canvas = createCanvas(10, 10, 'svg'); - assert.equal(canvas.type, 'svg'); - var canvas = createCanvas(10, 10, 'hey'); - assert.equal(canvas.type, 'image'); - }); + var canvas = createCanvas(10, 10) + assert.equal(canvas.type, 'image') + var canvas = createCanvas(10, 10, 'pdf') + assert.equal(canvas.type, 'pdf') + var canvas = createCanvas(10, 10, 'svg') + assert.equal(canvas.type, 'svg') + var canvas = createCanvas(10, 10, 'hey') + assert.equal(canvas.type, 'image') + }) it('Canvas#getContext("2d")', function () { var canvas = createCanvas(200, 300) - , ctx = canvas.getContext('2d'); - assert.ok('object' == typeof ctx); - assert.equal(canvas, ctx.canvas, 'context.canvas is not canvas'); - assert.equal(ctx, canvas.context, 'canvas.context is not context'); + + var ctx = canvas.getContext('2d') + assert.ok(typeof ctx === 'object') + assert.equal(canvas, ctx.canvas, 'context.canvas is not canvas') + assert.equal(ctx, canvas.context, 'canvas.context is not context') const MAX_IMAGE_SIZE = 32767; @@ -275,332 +280,342 @@ describe('Canvas', function () { [Math.pow(2, 30), 0, 3], [Math.pow(2, 30), 1, 3], [Math.pow(2, 32), 0, 1], - [Math.pow(2, 32), 1, 1], + [Math.pow(2, 32), 1, 1] ].forEach(params => { - var width = params[0]; - var height = params[1]; - var errorLevel = params[2]; + var width = params[0] + var height = params[1] + var errorLevel = params[2] - var level = 3; + var level = 3 try { - var canvas = createCanvas(width, height); - level--; + var canvas = createCanvas(width, height) + level-- - var ctx = canvas.getContext('2d'); - level--; + var ctx = canvas.getContext('2d') + level-- - ctx.getImageData(0, 0, 1, 1); - level--; + ctx.getImageData(0, 0, 1, 1) + level-- } catch (err) {} - if (errorLevel !== null) - assert.strictEqual(level, errorLevel); - }); - }); + if (errorLevel !== null) { assert.strictEqual(level, errorLevel) } + }) + }) it('Canvas#getContext("2d", {pixelFormat: string})', function () { - var canvas, context; + var canvas, context // default: - canvas = createCanvas(10, 10); - context = canvas.getContext("2d", {pixelFormat: "RGBA32"}); - assert.equal(context.pixelFormat, "RGBA32"); + canvas = createCanvas(10, 10) + context = canvas.getContext('2d', { pixelFormat: 'RGBA32' }) + assert.equal(context.pixelFormat, 'RGBA32') - canvas = createCanvas(10, 10); - context = canvas.getContext("2d", {pixelFormat: "RGBA32"}); - assert.equal(context.pixelFormat, "RGBA32"); + canvas = createCanvas(10, 10) + context = canvas.getContext('2d', { pixelFormat: 'RGBA32' }) + assert.equal(context.pixelFormat, 'RGBA32') - canvas = createCanvas(10, 10); - context = canvas.getContext("2d", {pixelFormat: "RGB24"}); - assert.equal(context.pixelFormat, "RGB24"); + canvas = createCanvas(10, 10) + context = canvas.getContext('2d', { pixelFormat: 'RGB24' }) + assert.equal(context.pixelFormat, 'RGB24') - canvas = createCanvas(10, 10); - context = canvas.getContext("2d", {pixelFormat: "A8"}); - assert.equal(context.pixelFormat, "A8"); + canvas = createCanvas(10, 10) + context = canvas.getContext('2d', { pixelFormat: 'A8' }) + assert.equal(context.pixelFormat, 'A8') - canvas = createCanvas(10, 10); - context = canvas.getContext("2d", {pixelFormat: "A1"}); - assert.equal(context.pixelFormat, "A1"); + canvas = createCanvas(10, 10) + context = canvas.getContext('2d', { pixelFormat: 'A1' }) + assert.equal(context.pixelFormat, 'A1') - canvas = createCanvas(10, 10); - context = canvas.getContext("2d", {pixelFormat: "RGB16_565"}); - assert.equal(context.pixelFormat, "RGB16_565"); + canvas = createCanvas(10, 10) + context = canvas.getContext('2d', { pixelFormat: 'RGB16_565' }) + assert.equal(context.pixelFormat, 'RGB16_565') // Not tested: RGB30 - }); + }) it('Canvas#getContext("2d", {alpha: boolean})', function () { - var canvas, context; + var canvas, context - canvas = createCanvas(10, 10); - context = canvas.getContext("2d", {alpha: true}); - assert.equal(context.pixelFormat, "RGBA32"); + canvas = createCanvas(10, 10) + context = canvas.getContext('2d', { alpha: true }) + assert.equal(context.pixelFormat, 'RGBA32') - canvas = createCanvas(10, 10); - context = canvas.getContext("2d", {alpha: false}); - assert.equal(context.pixelFormat, "RGB24"); + canvas = createCanvas(10, 10) + context = canvas.getContext('2d', { alpha: false }) + assert.equal(context.pixelFormat, 'RGB24') // alpha takes priority: - canvas = createCanvas(10, 10); - context = canvas.getContext("2d", {pixelFormat: "RGBA32", alpha: false}); - assert.equal(context.pixelFormat, "RGB24"); - }); + canvas = createCanvas(10, 10) + context = canvas.getContext('2d', { pixelFormat: 'RGBA32', alpha: false }) + assert.equal(context.pixelFormat, 'RGB24') + }) it('Canvas#{width,height}=', function () { - var context, canvas = createCanvas(100, 200); + var context; var canvas = createCanvas(100, 200) - assert.equal(100, canvas.width); - assert.equal(200, canvas.height); + assert.equal(100, canvas.width) + assert.equal(200, canvas.height) - canvas = createCanvas(); - context = canvas.getContext("2d"); - assert.equal(0, canvas.width); - assert.equal(0, canvas.height); + canvas = createCanvas() + context = canvas.getContext('2d') + assert.equal(0, canvas.width) + assert.equal(0, canvas.height) - canvas.width = 50; - canvas.height = 50; - assert.equal(50, canvas.width); - assert.equal(50, canvas.height); - assert.equal(1, context.lineWidth); // #1095 - }); + canvas.width = 50 + canvas.height = 50 + assert.equal(50, canvas.width) + assert.equal(50, canvas.height) + assert.equal(1, context.lineWidth) // #1095 + }) - it('Canvas#stride', function() { - var canvas = createCanvas(24, 10); - assert.ok(canvas.stride >= 24, 'canvas.stride is too short'); - assert.ok(canvas.stride < 1024, 'canvas.stride seems too long'); + it('Canvas#stride', function () { + var canvas = createCanvas(24, 10) + assert.ok(canvas.stride >= 24, 'canvas.stride is too short') + assert.ok(canvas.stride < 1024, 'canvas.stride seems too long') // TODO test stride on other formats - }); + }) it('Canvas#getContext("invalid")', function () { - assert.equal(null, createCanvas(200, 300).getContext('invalid')); - }); + assert.equal(null, createCanvas(200, 300).getContext('invalid')) + }) it('Context2d#patternQuality', function () { var canvas = createCanvas(200, 200) - , ctx = canvas.getContext('2d'); - assert.equal('good', ctx.patternQuality); - ctx.patternQuality = 'best'; - assert.equal('best', ctx.patternQuality); - ctx.patternQuality = 'invalid'; - assert.equal('best', ctx.patternQuality); - }); + var ctx = canvas.getContext('2d') + + assert.equal('good', ctx.patternQuality) + ctx.patternQuality = 'best' + assert.equal('best', ctx.patternQuality) + ctx.patternQuality = 'invalid' + assert.equal('best', ctx.patternQuality) + }) it('Context2d#imageSmoothingEnabled', function () { var canvas = createCanvas(200, 200) - , ctx = canvas.getContext('2d'); - assert.equal(true, ctx.imageSmoothingEnabled); - ctx.imageSmoothingEnabled = false; - assert.equal('good', ctx.patternQuality); - assert.equal(false, ctx.imageSmoothingEnabled); - assert.equal('good', ctx.patternQuality); - }); + var ctx = canvas.getContext('2d') + + assert.equal(true, ctx.imageSmoothingEnabled) + ctx.imageSmoothingEnabled = false + assert.equal('good', ctx.patternQuality) + assert.equal(false, ctx.imageSmoothingEnabled) + assert.equal('good', ctx.patternQuality) + }) it('Context2d#font=', function () { var canvas = createCanvas(200, 200) - , ctx = canvas.getContext('2d'); - assert.equal('10px sans-serif', ctx.font); - ctx.font = '15px Arial, sans-serif'; - assert.equal('15px Arial, sans-serif', ctx.font); - }); + var ctx = canvas.getContext('2d') + + assert.equal('10px sans-serif', ctx.font) + ctx.font = '15px Arial, sans-serif' + assert.equal('15px Arial, sans-serif', ctx.font) + }) it('Context2d#lineWidth=', function () { var canvas = createCanvas(200, 200) - , ctx = canvas.getContext('2d'); - - ctx.lineWidth = 10.0; - assert.equal(10, ctx.lineWidth); - ctx.lineWidth = Infinity; - assert.equal(10, ctx.lineWidth); - ctx.lineWidth = -Infinity; - assert.equal(10, ctx.lineWidth); - ctx.lineWidth = -5; - assert.equal(10, ctx.lineWidth); - ctx.lineWidth = 0; - assert.equal(10, ctx.lineWidth); - }); + + var ctx = canvas.getContext('2d') + + ctx.lineWidth = 10.0 + assert.equal(10, ctx.lineWidth) + ctx.lineWidth = Infinity + assert.equal(10, ctx.lineWidth) + ctx.lineWidth = -Infinity + assert.equal(10, ctx.lineWidth) + ctx.lineWidth = -5 + assert.equal(10, ctx.lineWidth) + ctx.lineWidth = 0 + assert.equal(10, ctx.lineWidth) + }) it('Context2d#antiAlias=', function () { var canvas = createCanvas(200, 200) - , ctx = canvas.getContext('2d'); - - assert.equal('default', ctx.antialias); - ctx.antialias = 'none'; - assert.equal('none', ctx.antialias); - ctx.antialias = 'gray'; - assert.equal('gray', ctx.antialias); - ctx.antialias = 'subpixel'; - assert.equal('subpixel', ctx.antialias); - ctx.antialias = 'invalid'; - assert.equal('subpixel', ctx.antialias); - ctx.antialias = 1; - assert.equal('subpixel', ctx.antialias); - }); + + var ctx = canvas.getContext('2d') + + assert.equal('default', ctx.antialias) + ctx.antialias = 'none' + assert.equal('none', ctx.antialias) + ctx.antialias = 'gray' + assert.equal('gray', ctx.antialias) + ctx.antialias = 'subpixel' + assert.equal('subpixel', ctx.antialias) + ctx.antialias = 'invalid' + assert.equal('subpixel', ctx.antialias) + ctx.antialias = 1 + assert.equal('subpixel', ctx.antialias) + }) it('Context2d#lineCap=', function () { var canvas = createCanvas(200, 200) - , ctx = canvas.getContext('2d'); - assert.equal('butt', ctx.lineCap); - ctx.lineCap = 'round'; - assert.equal('round', ctx.lineCap); - }); + var ctx = canvas.getContext('2d') + + assert.equal('butt', ctx.lineCap) + ctx.lineCap = 'round' + assert.equal('round', ctx.lineCap) + }) it('Context2d#lineJoin=', function () { var canvas = createCanvas(200, 200) - , ctx = canvas.getContext('2d'); - assert.equal('miter', ctx.lineJoin); - ctx.lineJoin = 'round'; - assert.equal('round', ctx.lineJoin); - }); + var ctx = canvas.getContext('2d') + + assert.equal('miter', ctx.lineJoin) + ctx.lineJoin = 'round' + assert.equal('round', ctx.lineJoin) + }) it('Context2d#globalAlpha=', function () { var canvas = createCanvas(200, 200) - , ctx = canvas.getContext('2d'); - assert.equal(1, ctx.globalAlpha); + var ctx = canvas.getContext('2d') + + assert.equal(1, ctx.globalAlpha) ctx.globalAlpha = 0.5 - assert.equal(0.5, ctx.globalAlpha); - }); + assert.equal(0.5, ctx.globalAlpha) + }) it('Context2d#isPointInPath()', function () { var canvas = createCanvas(200, 200) - , ctx = canvas.getContext('2d'); - - ctx.rect(5,5,100,100); - ctx.rect(50,100,10,10); - assert.ok(ctx.isPointInPath(10,10)); - assert.ok(ctx.isPointInPath(10,50)); - assert.ok(ctx.isPointInPath(100,100)); - assert.ok(ctx.isPointInPath(105,105)); - assert.ok(!ctx.isPointInPath(106,105)); - assert.ok(!ctx.isPointInPath(150,150)); - - assert.ok(ctx.isPointInPath(50,110)); - assert.ok(ctx.isPointInPath(60,110)); - assert.ok(!ctx.isPointInPath(70,110)); - assert.ok(!ctx.isPointInPath(50,120)); - }); + + var ctx = canvas.getContext('2d') + + ctx.rect(5, 5, 100, 100) + ctx.rect(50, 100, 10, 10) + assert.ok(ctx.isPointInPath(10, 10)) + assert.ok(ctx.isPointInPath(10, 50)) + assert.ok(ctx.isPointInPath(100, 100)) + assert.ok(ctx.isPointInPath(105, 105)) + assert.ok(!ctx.isPointInPath(106, 105)) + assert.ok(!ctx.isPointInPath(150, 150)) + + assert.ok(ctx.isPointInPath(50, 110)) + assert.ok(ctx.isPointInPath(60, 110)) + assert.ok(!ctx.isPointInPath(70, 110)) + assert.ok(!ctx.isPointInPath(50, 120)) + }) it('Context2d#textAlign', function () { - var canvas = createCanvas(200,200) - , ctx = canvas.getContext('2d'); - - assert.equal('start', ctx.textAlign); - ctx.textAlign = 'center'; - assert.equal('center', ctx.textAlign); - ctx.textAlign = 'right'; - assert.equal('right', ctx.textAlign); - ctx.textAlign = 'end'; - assert.equal('end', ctx.textAlign); - ctx.textAlign = 'fail'; - assert.equal('end', ctx.textAlign); - }); + var canvas = createCanvas(200, 200) + + var ctx = canvas.getContext('2d') + + assert.equal('start', ctx.textAlign) + ctx.textAlign = 'center' + assert.equal('center', ctx.textAlign) + ctx.textAlign = 'right' + assert.equal('right', ctx.textAlign) + ctx.textAlign = 'end' + assert.equal('end', ctx.textAlign) + ctx.textAlign = 'fail' + assert.equal('end', ctx.textAlign) + }) describe('#toBuffer', function () { it('Canvas#toBuffer()', function () { - var buf = createCanvas(200,200).toBuffer(); - assert.equal('PNG', buf.slice(1,4).toString()); - }); + var buf = createCanvas(200, 200).toBuffer() + assert.equal('PNG', buf.slice(1, 4).toString()) + }) it('Canvas#toBuffer("image/png")', function () { - var buf = createCanvas(200,200).toBuffer('image/png'); - assert.equal('PNG', buf.slice(1,4).toString()); - }); + var buf = createCanvas(200, 200).toBuffer('image/png') + assert.equal('PNG', buf.slice(1, 4).toString()) + }) it('Canvas#toBuffer("image/png", {resolution: 96})', function () { - const buf = createCanvas(200, 200).toBuffer('image/png', {resolution: 96}); + const buf = createCanvas(200, 200).toBuffer('image/png', { resolution: 96 }) // 3780 ppm ~= 96 ppi for (let i = 0; i < buf.length - 12; i++) { if (buf[i] === 0x70 && buf[i + 1] === 0x48 && buf[i + 2] === 0x59 && buf[i + 3] === 0x73) { // pHYs - assert.equal(buf[i + 4], 0); - assert.equal(buf[i + 5], 0); - assert.equal(buf[i + 6], 0x0e); - assert.equal(buf[i + 7], 0xc4); // x - assert.equal(buf[i + 8], 0); - assert.equal(buf[i + 9], 0); - assert.equal(buf[i + 10], 0x0e); - assert.equal(buf[i + 11], 0xc4); // y + assert.equal(buf[i + 4], 0) + assert.equal(buf[i + 5], 0) + assert.equal(buf[i + 6], 0x0e) + assert.equal(buf[i + 7], 0xc4) // x + assert.equal(buf[i + 8], 0) + assert.equal(buf[i + 9], 0) + assert.equal(buf[i + 10], 0x0e) + assert.equal(buf[i + 11], 0xc4) // y } } }) it('Canvas#toBuffer("image/png", {compressionLevel: 5})', function () { - var buf = createCanvas(200,200).toBuffer('image/png', {compressionLevel: 5}); - assert.equal('PNG', buf.slice(1,4).toString()); - }); + var buf = createCanvas(200, 200).toBuffer('image/png', { compressionLevel: 5 }) + assert.equal('PNG', buf.slice(1, 4).toString()) + }) it('Canvas#toBuffer("image/jpeg")', function () { - var buf = createCanvas(200,200).toBuffer('image/jpeg'); - assert.equal(buf[0], 0xff); - assert.equal(buf[1], 0xd8); - assert.equal(buf[buf.byteLength - 2], 0xff); - assert.equal(buf[buf.byteLength - 1], 0xd9); - }); + var buf = createCanvas(200, 200).toBuffer('image/jpeg') + assert.equal(buf[0], 0xff) + assert.equal(buf[1], 0xd8) + assert.equal(buf[buf.byteLength - 2], 0xff) + assert.equal(buf[buf.byteLength - 1], 0xd9) + }) it('Canvas#toBuffer("image/jpeg", {quality: 0.95})', function () { - var buf = createCanvas(200,200).toBuffer('image/jpeg', {quality: 0.95}); - assert.equal(buf[0], 0xff); - assert.equal(buf[1], 0xd8); - assert.equal(buf[buf.byteLength - 2], 0xff); - assert.equal(buf[buf.byteLength - 1], 0xd9); - }); + var buf = createCanvas(200, 200).toBuffer('image/jpeg', { quality: 0.95 }) + assert.equal(buf[0], 0xff) + assert.equal(buf[1], 0xd8) + assert.equal(buf[buf.byteLength - 2], 0xff) + assert.equal(buf[buf.byteLength - 1], 0xd9) + }) it('Canvas#toBuffer(callback)', function (done) { - createCanvas(200, 200).toBuffer(function(err, buf){ - assert.ok(!err); - assert.equal('PNG', buf.slice(1,4).toString()); - done(); - }); - }); + createCanvas(200, 200).toBuffer(function (err, buf) { + assert.ok(!err) + assert.equal('PNG', buf.slice(1, 4).toString()) + done() + }) + }) it('Canvas#toBuffer(callback, "image/jpeg")', function (done) { - createCanvas(200,200).toBuffer(function (err, buf) { - assert.ok(!err); - assert.equal(buf[0], 0xff); - assert.equal(buf[1], 0xd8); - assert.equal(buf[buf.byteLength - 2], 0xff); - assert.equal(buf[buf.byteLength - 1], 0xd9); - done(); - }, 'image/jpeg'); - }); + createCanvas(200, 200).toBuffer(function (err, buf) { + assert.ok(!err) + assert.equal(buf[0], 0xff) + assert.equal(buf[1], 0xd8) + assert.equal(buf[buf.byteLength - 2], 0xff) + assert.equal(buf[buf.byteLength - 1], 0xd9) + done() + }, 'image/jpeg') + }) it('Canvas#toBuffer(callback, "image/jpeg", {quality: 0.95})', function (done) { - createCanvas(200,200).toBuffer(function (err, buf) { - assert.ok(!err); - assert.equal(buf[0], 0xff); - assert.equal(buf[1], 0xd8); - assert.equal(buf[buf.byteLength - 2], 0xff); - assert.equal(buf[buf.byteLength - 1], 0xd9); - done(); - }, 'image/jpeg', {quality: 0.95}); - }); - - describe('#toBuffer("raw")', function() { + createCanvas(200, 200).toBuffer(function (err, buf) { + assert.ok(!err) + assert.equal(buf[0], 0xff) + assert.equal(buf[1], 0xd8) + assert.equal(buf[buf.byteLength - 2], 0xff) + assert.equal(buf[buf.byteLength - 1], 0xd9) + done() + }, 'image/jpeg', { quality: 0.95 }) + }) + + describe('#toBuffer("raw")', function () { var canvas = createCanvas(11, 10) - , ctx = canvas.getContext('2d'); - ctx.clearRect(0, 0, 11, 10); + var ctx = canvas.getContext('2d') + + ctx.clearRect(0, 0, 11, 10) - ctx.fillStyle = 'rgba(200, 200, 200, 0.505)'; - ctx.fillRect(0, 0, 5, 5); + ctx.fillStyle = 'rgba(200, 200, 200, 0.505)' + ctx.fillRect(0, 0, 5, 5) - ctx.fillStyle = 'red'; - ctx.fillRect(5, 0, 5, 5); + ctx.fillStyle = 'red' + ctx.fillRect(5, 0, 5, 5) - ctx.fillStyle = '#00ff00'; - ctx.fillRect(0, 5, 5, 5); + ctx.fillStyle = '#00ff00' + ctx.fillRect(0, 5, 5, 5) - ctx.fillStyle = 'black'; - ctx.fillRect(5, 5, 4, 5); + ctx.fillStyle = 'black' + ctx.fillRect(5, 5, 4, 5) /** Output: * *****RRRRR- @@ -615,284 +630,291 @@ describe('Canvas', function () { * GGGGGBBBB-- */ - var buf = canvas.toBuffer('raw'); - var stride = canvas.stride; + var buf = canvas.toBuffer('raw') + var stride = canvas.stride - var endianness = os.endianness(); + var endianness = os.endianness() - function assertPixel(u32, x, y, message) { - var expected = '0x' + u32.toString(16); + function assertPixel (u32, x, y, message) { + var expected = '0x' + u32.toString(16) // Buffer doesn't have readUInt32(): it only has readUInt32LE() and // readUInt32BE(). - var px = buf['readUInt32' + endianness](y * stride + x * 4); - var actual = '0x' + px.toString(16); + var px = buf['readUInt32' + endianness](y * stride + x * 4) + var actual = '0x' + px.toString(16) - assert.equal(actual, expected, message); + assert.equal(actual, expected, message) } - it('should have the correct size', function() { - assert.equal(buf.length, stride * 10); - }); - - it('does not premultiply alpha', function() { - assertPixel(0x80646464, 0, 0, 'first semitransparent pixel'); - assertPixel(0x80646464, 4, 4, 'last semitransparent pixel'); - }); - - it('draws red', function() { - assertPixel(0xffff0000, 5, 0, 'first red pixel'); - assertPixel(0xffff0000, 9, 4, 'last red pixel'); - }); - - it('draws green', function() { - assertPixel(0xff00ff00, 0, 5, 'first green pixel'); - assertPixel(0xff00ff00, 4, 9, 'last green pixel'); - }); - - it('draws black', function() { - assertPixel(0xff000000, 5, 5, 'first black pixel'); - assertPixel(0xff000000, 8, 9, 'last black pixel'); - }); - - it('leaves undrawn pixels black, transparent', function() { - assertPixel(0x0, 9, 5, 'first undrawn pixel'); - assertPixel(0x0, 9, 9, 'last undrawn pixel'); - }); - - it('is immutable', function() { - ctx.fillStyle = 'white'; - ctx.fillRect(0, 0, 10, 10); - canvas.toBuffer('raw'); // (side-effect: flushes canvas) - assertPixel(0xffff0000, 5, 0, 'first red pixel'); - }); - }); - }); + it('should have the correct size', function () { + assert.equal(buf.length, stride * 10) + }) + + it('does not premultiply alpha', function () { + assertPixel(0x80646464, 0, 0, 'first semitransparent pixel') + assertPixel(0x80646464, 4, 4, 'last semitransparent pixel') + }) + + it('draws red', function () { + assertPixel(0xffff0000, 5, 0, 'first red pixel') + assertPixel(0xffff0000, 9, 4, 'last red pixel') + }) + + it('draws green', function () { + assertPixel(0xff00ff00, 0, 5, 'first green pixel') + assertPixel(0xff00ff00, 4, 9, 'last green pixel') + }) + + it('draws black', function () { + assertPixel(0xff000000, 5, 5, 'first black pixel') + assertPixel(0xff000000, 8, 9, 'last black pixel') + }) + + it('leaves undrawn pixels black, transparent', function () { + assertPixel(0x0, 9, 5, 'first undrawn pixel') + assertPixel(0x0, 9, 9, 'last undrawn pixel') + }) + + it('is immutable', function () { + ctx.fillStyle = 'white' + ctx.fillRect(0, 0, 10, 10) + canvas.toBuffer('raw') // (side-effect: flushes canvas) + assertPixel(0xffff0000, 5, 0, 'first red pixel') + }) + }) + }) describe('#toDataURL()', function () { var canvas = createCanvas(200, 200) - , ctx = canvas.getContext('2d'); - ctx.fillRect(0,0,100,100); - ctx.fillStyle = 'red'; - ctx.fillRect(100,0,100,100); + var ctx = canvas.getContext('2d') + + ctx.fillRect(0, 0, 100, 100) + ctx.fillStyle = 'red' + ctx.fillRect(100, 0, 100, 100) it('toDataURL() works and defaults to PNG', function () { - assert.ok(canvas.toDataURL().startsWith('data:image/png;base64,')); - }); + assert.ok(canvas.toDataURL().startsWith('data:image/png;base64,')) + }) it('toDataURL(0.5) works and defaults to PNG', function () { - assert.ok(canvas.toDataURL(0.5).startsWith('data:image/png;base64,')); - }); + assert.ok(canvas.toDataURL(0.5).startsWith('data:image/png;base64,')) + }) it('toDataURL(undefined) works and defaults to PNG', function () { - assert.ok(canvas.toDataURL(undefined).startsWith('data:image/png;base64,')); - }); + assert.ok(canvas.toDataURL(undefined).startsWith('data:image/png;base64,')) + }) it('toDataURL("image/png") works', function () { - assert.ok(canvas.toDataURL('image/png').startsWith('data:image/png;base64,')); - }); + assert.ok(canvas.toDataURL('image/png').startsWith('data:image/png;base64,')) + }) it('toDataURL("image/png", 0.5) works', function () { - assert.ok(canvas.toDataURL('image/png').startsWith('data:image/png;base64,')); - }); + assert.ok(canvas.toDataURL('image/png').startsWith('data:image/png;base64,')) + }) it('toDataURL("iMaGe/PNg") works', function () { - assert.ok(canvas.toDataURL('iMaGe/PNg').startsWith('data:image/png;base64,')); - }); + assert.ok(canvas.toDataURL('iMaGe/PNg').startsWith('data:image/png;base64,')) + }) it('toDataURL("image/jpeg") works', function () { - assert.ok(canvas.toDataURL('image/jpeg').startsWith('data:image/jpeg;base64,')); - }); + assert.ok(canvas.toDataURL('image/jpeg').startsWith('data:image/jpeg;base64,')) + }) it('toDataURL(function (err, str) {...}) works and defaults to PNG', function (done) { - createCanvas(200,200).toDataURL(function(err, str){ - assert.ifError(err); - assert.ok(0 === str.indexOf('data:image/png;base64,')); - done(); - }); - }); + createCanvas(200, 200).toDataURL(function (err, str) { + assert.ifError(err) + assert.ok(str.indexOf('data:image/png;base64,') === 0) + done() + }) + }) it('toDataURL(function (err, str) {...}) is async even with no canvas data', function (done) { - createCanvas().toDataURL(function(err, str){ - assert.ifError(err); - assert.ok('data:,' === str); - done(); - }); - }); + createCanvas().toDataURL(function (err, str) { + assert.ifError(err) + assert.ok(str === 'data:,') + done() + }) + }) it('toDataURL(0.5, function (err, str) {...}) works and defaults to PNG', function (done) { - createCanvas(200,200).toDataURL(0.5, function(err, str){ - assert.ifError(err); - assert.ok(0 === str.indexOf('data:image/png;base64,')); - done(); - }); - }); + createCanvas(200, 200).toDataURL(0.5, function (err, str) { + assert.ifError(err) + assert.ok(str.indexOf('data:image/png;base64,') === 0) + done() + }) + }) it('toDataURL(undefined, function (err, str) {...}) works and defaults to PNG', function (done) { - createCanvas(200,200).toDataURL(undefined, function(err, str){ - assert.ifError(err); - assert.ok(0 === str.indexOf('data:image/png;base64,')); - done(); - }); - }); + createCanvas(200, 200).toDataURL(undefined, function (err, str) { + assert.ifError(err) + assert.ok(str.indexOf('data:image/png;base64,') === 0) + done() + }) + }) it('toDataURL("image/png", function (err, str) {...}) works', function (done) { - createCanvas(200,200).toDataURL('image/png', function(err, str){ - assert.ifError(err); - assert.ok(0 === str.indexOf('data:image/png;base64,')); - done(); - }); - }); + createCanvas(200, 200).toDataURL('image/png', function (err, str) { + assert.ifError(err) + assert.ok(str.indexOf('data:image/png;base64,') === 0) + done() + }) + }) it('toDataURL("image/png", 0.5, function (err, str) {...}) works', function (done) { - createCanvas(200,200).toDataURL('image/png', 0.5, function(err, str){ - assert.ifError(err); - assert.ok(0 === str.indexOf('data:image/png;base64,')); - done(); - }); - }); + createCanvas(200, 200).toDataURL('image/png', 0.5, function (err, str) { + assert.ifError(err) + assert.ok(str.indexOf('data:image/png;base64,') === 0) + done() + }) + }) it('toDataURL("image/png", {}) works', function () { - assert.ok(canvas.toDataURL('image/png', {}).startsWith('data:image/png;base64,')); - }); + assert.ok(canvas.toDataURL('image/png', {}).startsWith('data:image/png;base64,')) + }) it('toDataURL("image/jpeg", {}) works', function () { - assert.ok(canvas.toDataURL('image/jpeg', {}).startsWith('data:image/jpeg;base64,')); - }); + assert.ok(canvas.toDataURL('image/jpeg', {}).startsWith('data:image/jpeg;base64,')) + }) it('toDataURL("image/jpeg", function (err, str) {...}) works', function (done) { - createCanvas(200,200).toDataURL('image/jpeg', function(err, str){ - assert.ifError(err); - assert.ok(0 === str.indexOf('data:image/jpeg;base64,')); - done(); - }); - }); + createCanvas(200, 200).toDataURL('image/jpeg', function (err, str) { + assert.ifError(err) + assert.ok(str.indexOf('data:image/jpeg;base64,') === 0) + done() + }) + }) it('toDataURL("iMAge/JPEG", function (err, str) {...}) works', function (done) { - createCanvas(200,200).toDataURL('iMAge/JPEG', function(err, str){ - assert.ifError(err); - assert.ok(0 === str.indexOf('data:image/jpeg;base64,')); - done(); - }); - }); + createCanvas(200, 200).toDataURL('iMAge/JPEG', function (err, str) { + assert.ifError(err) + assert.ok(str.indexOf('data:image/jpeg;base64,') === 0) + done() + }) + }) it('toDataURL("image/jpeg", undefined, function (err, str) {...}) works', function (done) { - createCanvas(200,200).toDataURL('image/jpeg', undefined, function(err, str){ - assert.ifError(err); - assert.ok(0 === str.indexOf('data:image/jpeg;base64,')); - done(); - }); - }); + createCanvas(200, 200).toDataURL('image/jpeg', undefined, function (err, str) { + assert.ifError(err) + assert.ok(str.indexOf('data:image/jpeg;base64,') === 0) + done() + }) + }) it('toDataURL("image/jpeg", 0.5, function (err, str) {...}) works', function (done) { - createCanvas(200,200).toDataURL('image/jpeg', 0.5, function(err, str){ - assert.ifError(err); - assert.ok(0 === str.indexOf('data:image/jpeg;base64,')); - done(); - }); - }); + createCanvas(200, 200).toDataURL('image/jpeg', 0.5, function (err, str) { + assert.ifError(err) + assert.ok(str.indexOf('data:image/jpeg;base64,') === 0) + done() + }) + }) it('toDataURL("image/jpeg", opts, function (err, str) {...}) works', function (done) { - createCanvas(200,200).toDataURL('image/jpeg', {quality: 100}, function(err, str){ - assert.ifError(err); - assert.ok(0 === str.indexOf('data:image/jpeg;base64,')); - done(); - }); - }); - }); + createCanvas(200, 200).toDataURL('image/jpeg', { quality: 100 }, function (err, str) { + assert.ifError(err) + assert.ok(str.indexOf('data:image/jpeg;base64,') === 0) + done() + }) + }) + }) describe('Context2d#createImageData(width, height)', function () { - it("works", function () { + it('works', function () { var canvas = createCanvas(20, 20) - , ctx = canvas.getContext('2d'); - var imageData = ctx.createImageData(2,6); - assert.equal(2, imageData.width); - assert.equal(6, imageData.height); - assert.equal(2 * 6 * 4, imageData.data.length); + var ctx = canvas.getContext('2d') + + var imageData = ctx.createImageData(2, 6) + assert.equal(2, imageData.width) + assert.equal(6, imageData.height) + assert.equal(2 * 6 * 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]); - }); + 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("works, A8 format", function () { + it('works, A8 format', function () { var canvas = createCanvas(20, 20) - , ctx = canvas.getContext('2d', {pixelFormat: "A8"}); - var imageData = ctx.createImageData(2,6); - assert.equal(2, imageData.width); - assert.equal(6, imageData.height); - assert.equal(2 * 6 * 1, imageData.data.length); + var ctx = canvas.getContext('2d', { pixelFormat: 'A8' }) - assert.equal(0, imageData.data[0]); - assert.equal(0, imageData.data[1]); - assert.equal(0, imageData.data[2]); - assert.equal(0, imageData.data[3]); - }); + var imageData = ctx.createImageData(2, 6) + assert.equal(2, imageData.width) + assert.equal(6, imageData.height) + assert.equal(2 * 6 * 1, 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("works, A1 format", function () { + it('works, A1 format', function () { var canvas = createCanvas(20, 20) - , ctx = canvas.getContext('2d', {pixelFormat: "A1"}); - var imageData = ctx.createImageData(2,6); - assert.equal(2, imageData.width); - assert.equal(6, imageData.height); - assert.equal(Math.ceil(2 * 6 / 8), imageData.data.length); + var ctx = canvas.getContext('2d', { pixelFormat: 'A1' }) - assert.equal(0, imageData.data[0]); - assert.equal(0, imageData.data[1]); - }); + var imageData = ctx.createImageData(2, 6) + assert.equal(2, imageData.width) + assert.equal(6, imageData.height) + assert.equal(Math.ceil(2 * 6 / 8), imageData.data.length) - it("works, RGB24 format", function () { + assert.equal(0, imageData.data[0]) + assert.equal(0, imageData.data[1]) + }) + + it('works, RGB24 format', function () { var canvas = createCanvas(20, 20) - , ctx = canvas.getContext('2d', {pixelFormat: "RGB24"}); - var imageData = ctx.createImageData(2,6); - assert.equal(2, imageData.width); - assert.equal(6, imageData.height); - assert.equal(2 * 6 * 4, imageData.data.length); + var ctx = canvas.getContext('2d', { pixelFormat: 'RGB24' }) - assert.equal(0, imageData.data[0]); - assert.equal(0, imageData.data[1]); - assert.equal(0, imageData.data[2]); - assert.equal(0, imageData.data[3]); - }); + var imageData = ctx.createImageData(2, 6) + assert.equal(2, imageData.width) + assert.equal(6, imageData.height) + assert.equal(2 * 6 * 4, imageData.data.length) - it("works, RGB16_565 format", function () { + 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('works, RGB16_565 format', function () { var canvas = createCanvas(20, 20) - , ctx = canvas.getContext('2d', {pixelFormat: "RGB16_565"}); - var imageData = ctx.createImageData(2,6); - assert(imageData.data instanceof Uint16Array); - assert.equal(2, imageData.width); - assert.equal(6, imageData.height); - assert.equal(2 * 6, imageData.data.length); + var ctx = canvas.getContext('2d', { pixelFormat: 'RGB16_565' }) + + var imageData = ctx.createImageData(2, 6) + assert(imageData.data instanceof Uint16Array) + assert.equal(2, imageData.width) + assert.equal(6, imageData.height) + assert.equal(2 * 6, imageData.data.length) - assert.equal(0, imageData.data[0]); - assert.equal(0, imageData.data[1]); - }); - }); + assert.equal(0, imageData.data[0]) + assert.equal(0, imageData.data[1]) + }) + }) describe('Context2d#measureText()', function () { it('Context2d#measureText().width', function () { var canvas = createCanvas(20, 20) - , ctx = canvas.getContext('2d'); - assert.ok(ctx.measureText('foo').width); - assert.ok(ctx.measureText('foo').width != ctx.measureText('foobar').width); - assert.ok(ctx.measureText('foo').width != ctx.measureText(' foo').width); - }); + var ctx = canvas.getContext('2d') + + assert.ok(ctx.measureText('foo').width) + assert.ok(ctx.measureText('foo').width != ctx.measureText('foobar').width) + assert.ok(ctx.measureText('foo').width != ctx.measureText(' foo').width) + }) it('works', function () { var canvas = createCanvas(20, 20) var ctx = canvas.getContext('2d') - ctx.font = "20px Arial" + ctx.font = '20px Arial' - ctx.textBaseline = "alphabetic" - var metrics = ctx.measureText("Alphabet") + ctx.textBaseline = 'alphabetic' + var metrics = ctx.measureText('Alphabet') // Zero if the given baseline is the alphabetic baseline assert.equal(metrics.alphabeticBaseline, 0) // Positive = going up from the baseline @@ -900,807 +922,806 @@ describe('Canvas', function () { // Positive = going down from the baseline assert.ok(metrics.actualBoundingBoxDescent > 0) // ~4-5 - ctx.textBaseline = "bottom" - metrics = ctx.measureText("Alphabet") + ctx.textBaseline = 'bottom' + metrics = ctx.measureText('Alphabet') assert.ok(metrics.alphabeticBaseline > 0) // ~4-5 assert.ok(metrics.actualBoundingBoxAscent > 0) // On the baseline or slightly above assert.ok(metrics.actualBoundingBoxDescent <= 0) - }); - }); + }) + }) it('Context2d#currentTransform', function () { - var canvas = createCanvas(20, 20); - var ctx = canvas.getContext('2d'); - - ctx.scale(0.1, 0.3); - var actual = ctx.currentTransform; - assert.equal(actual.a, 0.1); - assert.equal(actual.b, 0); - assert.equal(actual.c, 0); - assert.equal(actual.d, 0.3); - assert.equal(actual.e, 0); - assert.equal(actual.f, 0); - }); + var canvas = createCanvas(20, 20) + var ctx = canvas.getContext('2d') + + ctx.scale(0.1, 0.3) + var actual = ctx.currentTransform + assert.equal(actual.a, 0.1) + assert.equal(actual.b, 0) + assert.equal(actual.c, 0) + assert.equal(actual.d, 0.3) + assert.equal(actual.e, 0) + assert.equal(actual.f, 0) + }) it('Context2d#createImageData(ImageData)', function () { var canvas = createCanvas(20, 20) - , ctx = canvas.getContext('2d'); - var imageData = ctx.createImageData(ctx.createImageData(2, 6)); - assert.equal(2, imageData.width); - assert.equal(6, imageData.height); - assert.equal(2 * 6 * 4, imageData.data.length); - }); + var ctx = canvas.getContext('2d') + + var imageData = ctx.createImageData(ctx.createImageData(2, 6)) + assert.equal(2, imageData.width) + assert.equal(6, imageData.height) + assert.equal(2 * 6 * 4, imageData.data.length) + }) describe('Context2d#getImageData()', function () { - function createTestCanvas(useAlpha, attributes) { - var canvas = createCanvas(3, 6); - var ctx = canvas.getContext('2d', attributes); + function createTestCanvas (useAlpha, attributes) { + var canvas = createCanvas(3, 6) + var ctx = canvas.getContext('2d', attributes) - ctx.fillStyle = useAlpha ? 'rgba(255,0,0,0.25)' : '#f00'; - ctx.fillRect(0,0,1,6); + ctx.fillStyle = useAlpha ? 'rgba(255,0,0,0.25)' : '#f00' + ctx.fillRect(0, 0, 1, 6) - ctx.fillStyle = useAlpha ? 'rgba(0,255,0,0.5)' : '#0f0'; - ctx.fillRect(1,0,1,6); + ctx.fillStyle = useAlpha ? 'rgba(0,255,0,0.5)' : '#0f0' + ctx.fillRect(1, 0, 1, 6) - ctx.fillStyle = useAlpha ? 'rgba(0,0,255,0.75)' : '#00f'; - ctx.fillRect(2,0,1,6); + ctx.fillStyle = useAlpha ? 'rgba(0,0,255,0.75)' : '#00f' + ctx.fillRect(2, 0, 1, 6) - return ctx; + return ctx } - it("works, full width, RGBA32", function () { - var ctx = createTestCanvas(); - var imageData = ctx.getImageData(0,0,3,6); - - assert.equal(3, imageData.width); - assert.equal(6, imageData.height); - assert.equal(3 * 6 * 4, 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(255, imageData.data[5]); - assert.equal(0, imageData.data[6]); - assert.equal(255, imageData.data[7]); - - assert.equal(0, imageData.data[8]); - assert.equal(0, imageData.data[9]); - assert.equal(255, imageData.data[10]); - assert.equal(255, imageData.data[11]); - }); - - it("works, full width, RGB24", function () { - var ctx = createTestCanvas(false, {pixelFormat: "RGB24"}); - var imageData = ctx.getImageData(0,0,3,6); - assert.equal(3, imageData.width); - assert.equal(6, imageData.height); - assert.equal(3 * 6 * 4, 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(255, imageData.data[5]); - assert.equal(0, imageData.data[6]); - assert.equal(255, imageData.data[7]); - - assert.equal(0, imageData.data[8]); - assert.equal(0, imageData.data[9]); - assert.equal(255, imageData.data[10]); - assert.equal(255, imageData.data[11]); - }); - - it("works, full width, RGB16_565", function () { - var ctx = createTestCanvas(false, {pixelFormat: "RGB16_565"}); - var imageData = ctx.getImageData(0,0,3,6); - assert.equal(3, imageData.width); - assert.equal(6, imageData.height); - assert.equal(3 * 6 * 2, imageData.data.length); - - assert.equal((255 & 0b11111) << 11, imageData.data[0]); - assert.equal((255 & 0b111111) << 5, imageData.data[1]); - assert.equal((255 & 0b11111), imageData.data[2]); - - assert.equal((255 & 0b11111) << 11, imageData.data[3]); - assert.equal((255 & 0b111111) << 5, imageData.data[4]); - assert.equal((255 & 0b11111), imageData.data[5]); - }); - - it("works, full width, A8", function () { - var ctx = createTestCanvas(true, {pixelFormat: "A8"}); - var imageData = ctx.getImageData(0,0,3,6); - assert.equal(3, imageData.width); - assert.equal(6, imageData.height); - assert.equal(3 * 6, imageData.data.length); - - assert.equal(63, imageData.data[0]); - assert.equal(127, imageData.data[1]); - assert.equal(191, imageData.data[2]); - - assert.equal(63, imageData.data[3]); - assert.equal(127, imageData.data[4]); - assert.equal(191, imageData.data[5]); - }); - - it("works, full width, A1"); - - it("works, full width, RGB30"); - - it("works, slice, RGBA32", function () { - var ctx = createTestCanvas(); - var imageData = ctx.getImageData(0,0,2,1); - assert.equal(2, imageData.width); - assert.equal(1, 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(255, imageData.data[5]); - assert.equal(0, imageData.data[6]); - assert.equal(255, imageData.data[7]); - }); - - it("works, slice, RGB24", function () { - var ctx = createTestCanvas(false, {pixelFormat: "RGB24"}); - var imageData = ctx.getImageData(0,0,2,1); - assert.equal(2, imageData.width); - assert.equal(1, 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(255, imageData.data[5]); - assert.equal(0, imageData.data[6]); - assert.equal(255, imageData.data[7]); - }); - - it("works, slice, RGB16_565", function () { - var ctx = createTestCanvas(false, {pixelFormat: "RGB16_565"}); - var imageData = ctx.getImageData(0,0,2,1); - assert.equal(2, imageData.width); - assert.equal(1, imageData.height); - assert.equal(2 * 1 * 2, imageData.data.length); - - assert.equal((255 & 0b11111) << 11, imageData.data[0]); - assert.equal((255 & 0b111111) << 5, imageData.data[1]); - }); - - it("works, slice, A8", function () { - var ctx = createTestCanvas(true, {pixelFormat: "A8"}); - var imageData = ctx.getImageData(0,0,2,1); - assert.equal(2, imageData.width); - assert.equal(1, imageData.height); - assert.equal(2 * 1, imageData.data.length); - - assert.equal(63, imageData.data[0]); - assert.equal(127, imageData.data[1]); - }); - - it("works, slice, A1"); - - it("works, slice, RGB30"); - - it("works, assignment", function () { - var ctx = createTestCanvas(); - var data = ctx.getImageData(0,0,5,5).data; - data[0] = 50; - assert.equal(50, data[0]); - data[0] = 280; - assert.equal(255, data[0]); - data[0] = -4444; - assert.equal(0, data[0]); - }); - - it("throws if indexes are invalid", function () { - var ctx = createTestCanvas(); - assert.throws(function () { ctx.getImageData(0, 0, 0, 0); }, /IndexSizeError/); - }); - }); + it('works, full width, RGBA32', function () { + var ctx = createTestCanvas() + var imageData = ctx.getImageData(0, 0, 3, 6) + + assert.equal(3, imageData.width) + assert.equal(6, imageData.height) + assert.equal(3 * 6 * 4, 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(255, imageData.data[5]) + assert.equal(0, imageData.data[6]) + assert.equal(255, imageData.data[7]) + + assert.equal(0, imageData.data[8]) + assert.equal(0, imageData.data[9]) + assert.equal(255, imageData.data[10]) + assert.equal(255, imageData.data[11]) + }) + + it('works, full width, RGB24', function () { + var ctx = createTestCanvas(false, { pixelFormat: 'RGB24' }) + var imageData = ctx.getImageData(0, 0, 3, 6) + assert.equal(3, imageData.width) + assert.equal(6, imageData.height) + assert.equal(3 * 6 * 4, 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(255, imageData.data[5]) + assert.equal(0, imageData.data[6]) + assert.equal(255, imageData.data[7]) + + assert.equal(0, imageData.data[8]) + assert.equal(0, imageData.data[9]) + assert.equal(255, imageData.data[10]) + assert.equal(255, imageData.data[11]) + }) + + it('works, full width, RGB16_565', function () { + var ctx = createTestCanvas(false, { pixelFormat: 'RGB16_565' }) + var imageData = ctx.getImageData(0, 0, 3, 6) + assert.equal(3, imageData.width) + assert.equal(6, imageData.height) + assert.equal(3 * 6 * 2, imageData.data.length) + + assert.equal((255 & 0b11111) << 11, imageData.data[0]) + assert.equal((255 & 0b111111) << 5, imageData.data[1]) + assert.equal((255 & 0b11111), imageData.data[2]) + + assert.equal((255 & 0b11111) << 11, imageData.data[3]) + assert.equal((255 & 0b111111) << 5, imageData.data[4]) + assert.equal((255 & 0b11111), imageData.data[5]) + }) + + it('works, full width, A8', function () { + var ctx = createTestCanvas(true, { pixelFormat: 'A8' }) + var imageData = ctx.getImageData(0, 0, 3, 6) + assert.equal(3, imageData.width) + assert.equal(6, imageData.height) + assert.equal(3 * 6, imageData.data.length) + + assert.equal(63, imageData.data[0]) + assert.equal(127, imageData.data[1]) + assert.equal(191, imageData.data[2]) + + assert.equal(63, imageData.data[3]) + assert.equal(127, imageData.data[4]) + assert.equal(191, imageData.data[5]) + }) + + it('works, full width, A1') + + it('works, full width, RGB30') + + it('works, slice, RGBA32', function () { + var ctx = createTestCanvas() + var imageData = ctx.getImageData(0, 0, 2, 1) + assert.equal(2, imageData.width) + assert.equal(1, 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(255, imageData.data[5]) + assert.equal(0, imageData.data[6]) + assert.equal(255, imageData.data[7]) + }) + + it('works, slice, RGB24', function () { + var ctx = createTestCanvas(false, { pixelFormat: 'RGB24' }) + var imageData = ctx.getImageData(0, 0, 2, 1) + assert.equal(2, imageData.width) + assert.equal(1, 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(255, imageData.data[5]) + assert.equal(0, imageData.data[6]) + assert.equal(255, imageData.data[7]) + }) + + it('works, slice, RGB16_565', function () { + var ctx = createTestCanvas(false, { pixelFormat: 'RGB16_565' }) + var imageData = ctx.getImageData(0, 0, 2, 1) + assert.equal(2, imageData.width) + assert.equal(1, imageData.height) + assert.equal(2 * 1 * 2, imageData.data.length) + + assert.equal((255 & 0b11111) << 11, imageData.data[0]) + assert.equal((255 & 0b111111) << 5, imageData.data[1]) + }) + + it('works, slice, A8', function () { + var ctx = createTestCanvas(true, { pixelFormat: 'A8' }) + var imageData = ctx.getImageData(0, 0, 2, 1) + assert.equal(2, imageData.width) + assert.equal(1, imageData.height) + assert.equal(2 * 1, imageData.data.length) + + assert.equal(63, imageData.data[0]) + assert.equal(127, imageData.data[1]) + }) + + it('works, slice, A1') + + it('works, slice, RGB30') + + it('works, assignment', function () { + var ctx = createTestCanvas() + var data = ctx.getImageData(0, 0, 5, 5).data + data[0] = 50 + assert.equal(50, data[0]) + data[0] = 280 + assert.equal(255, data[0]) + data[0] = -4444 + assert.equal(0, data[0]) + }) + + it('throws if indexes are invalid', function () { + var ctx = createTestCanvas() + assert.throws(function () { ctx.getImageData(0, 0, 0, 0) }, /IndexSizeError/) + }) + }) it('Context2d#createPattern(Canvas)', function () { - var pattern = createCanvas(2,2) - , checkers = pattern.getContext('2d'); + var pattern = createCanvas(2, 2) + + var checkers = pattern.getContext('2d') // white - checkers.fillStyle = '#fff'; - checkers.fillRect(0,0,2,2); + checkers.fillStyle = '#fff' + checkers.fillRect(0, 0, 2, 2) // black - checkers.fillStyle = '#000'; - checkers.fillRect(0,0,1,1); - checkers.fillRect(1,1,1,1); + checkers.fillStyle = '#000' + checkers.fillRect(0, 0, 1, 1) + checkers.fillRect(1, 1, 1, 1) - var imageData = checkers.getImageData(0,0,2,2); - assert.equal(2, imageData.width); - assert.equal(2, imageData.height); - assert.equal(16, imageData.data.length); + var imageData = checkers.getImageData(0, 0, 2, 2) + assert.equal(2, imageData.width) + assert.equal(2, imageData.height) + assert.equal(16, imageData.data.length) // (0,0) black - assert.equal(0, 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[0]) + assert.equal(0, imageData.data[1]) + assert.equal(0, imageData.data[2]) + assert.equal(255, imageData.data[3]) // (1,0) white - assert.equal(255, imageData.data[4]); - assert.equal(255, imageData.data[5]); - assert.equal(255, imageData.data[6]); - assert.equal(255, imageData.data[7]); + assert.equal(255, imageData.data[4]) + assert.equal(255, imageData.data[5]) + assert.equal(255, imageData.data[6]) + assert.equal(255, imageData.data[7]) // (0,1) white - assert.equal(255, imageData.data[8]); - assert.equal(255, imageData.data[9]); - assert.equal(255, imageData.data[10]); - assert.equal(255, imageData.data[11]); + assert.equal(255, imageData.data[8]) + assert.equal(255, imageData.data[9]) + assert.equal(255, imageData.data[10]) + assert.equal(255, imageData.data[11]) // (1,1) black - assert.equal(0, imageData.data[12]); - assert.equal(0, imageData.data[13]); - assert.equal(0, imageData.data[14]); - assert.equal(255, imageData.data[15]); + assert.equal(0, imageData.data[12]) + assert.equal(0, imageData.data[13]) + assert.equal(0, imageData.data[14]) + assert.equal(255, imageData.data[15]) var canvas = createCanvas(20, 20) - , ctx = canvas.getContext('2d') - , pattern = ctx.createPattern(pattern); - - ctx.fillStyle = pattern; - ctx.fillRect(0,0,20,20); - - var imageData = ctx.getImageData(0,0,20,20); - assert.equal(20, imageData.width); - assert.equal(20, imageData.height); - assert.equal(1600, imageData.data.length); - - var i=0, b = true; - while(i { var canvas = createCanvas(20, 20) - , ctx = canvas.getContext('2d') - , pattern = ctx.createPattern(img); - ctx.fillStyle = pattern; - ctx.fillRect(0,0,20,20); + var ctx = canvas.getContext('2d') + + var pattern = ctx.createPattern(img) + + ctx.fillStyle = pattern + ctx.fillRect(0, 0, 20, 20) - var imageData = ctx.getImageData(0,0,20,20); - assert.equal(20, imageData.width); - assert.equal(20, imageData.height); - assert.equal(1600, imageData.data.length); + var imageData = ctx.getImageData(0, 0, 20, 20) + assert.equal(20, imageData.width) + assert.equal(20, imageData.height) + assert.equal(1600, imageData.data.length) - var i=0, b = true; - while (i= 6) - obj[Symbol.toPrimitive] = function () { return 0.89; }; - else - obj.valueOf = function () { return 0.89; }; - ctx.resetTransform(); - testAngle(obj, 0.89); + var obj = Object.create(null) + if (+process.version.match(/\d+/) >= 6) { obj[Symbol.toPrimitive] = function () { return 0.89 } } else { obj.valueOf = function () { return 0.89 } } + ctx.resetTransform() + testAngle(obj, 0.89) // NaN - ctx.resetTransform(); - ctx.rotate(0.91); - testAngle(NaN, 0.91); + ctx.resetTransform() + ctx.rotate(0.91) + testAngle(NaN, 0.91) // Infinite value - ctx.resetTransform(); - ctx.rotate(0.94); - testAngle(-Infinity, 0.94); + ctx.resetTransform() + ctx.rotate(0.94) + testAngle(-Infinity, 0.94) - function testAngle(angle, expected){ - ctx.rotate(angle); + function testAngle (angle, expected) { + ctx.rotate(angle) - var mat = ctx.currentTransform; - var sin = Math.sin(expected); - var cos = Math.cos(expected); + var mat = ctx.currentTransform + var sin = Math.sin(expected) + var cos = Math.cos(expected) - assert.ok(Math.abs(mat.m11 - cos) < Number.EPSILON); - assert.ok(Math.abs(mat.m12 - sin) < Number.EPSILON); - assert.ok(Math.abs(mat.m21 + sin) < Number.EPSILON); - assert.ok(Math.abs(mat.m22 - cos) < Number.EPSILON); + assert.ok(Math.abs(mat.m11 - cos) < Number.EPSILON) + assert.ok(Math.abs(mat.m12 - sin) < Number.EPSILON) + assert.ok(Math.abs(mat.m21 + sin) < Number.EPSILON) + assert.ok(Math.abs(mat.m22 - cos) < Number.EPSILON) } - }); + }) it('Context2d#drawImage()', function () { - var canvas = createCanvas(500, 500); - var ctx = canvas.getContext('2d'); + var canvas = createCanvas(500, 500) + var ctx = canvas.getContext('2d') // Drawing canvas to itself - ctx.fillStyle = 'white'; - ctx.fillRect(0, 0, 500, 500); - ctx.fillStyle = 'black'; - ctx.fillRect(5, 5, 10, 10); - ctx.drawImage(canvas, 20, 20); - - var imgd = ctx.getImageData(0, 0, 500, 500); - var data = imgd.data; - var count = 0; - - for(var i = 0; i < 500 * 500 * 4; i += 4){ - if(data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0) - count++; + ctx.fillStyle = 'white' + ctx.fillRect(0, 0, 500, 500) + ctx.fillStyle = 'black' + ctx.fillRect(5, 5, 10, 10) + ctx.drawImage(canvas, 20, 20) + + var imgd = ctx.getImageData(0, 0, 500, 500) + var data = imgd.data + var count = 0 + + for (var i = 0; i < 500 * 500 * 4; i += 4) { + if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0) { count++ } } - assert.strictEqual(count, 10 * 10 * 2); + assert.strictEqual(count, 10 * 10 * 2) // Drawing zero-width image - ctx.drawImage(canvas, 0, 0, 0, 0, 0, 0, 0, 0); - ctx.drawImage(canvas, 0, 0, 0, 0, 1, 1, 1, 1); - ctx.drawImage(canvas, 1, 1, 1, 1, 0, 0, 0, 0); - ctx.fillStyle = 'white'; - ctx.fillRect(0, 0, 500, 500); - - imgd = ctx.getImageData(0, 0, 500, 500); - data = imgd.data; - count = 0; - - for(i = 0; i < 500 * 500 * 4; i += 4){ - if(data[i] === 255 && data[i + 1] === 255 && data[i + 2] === 255) - count++; + ctx.drawImage(canvas, 0, 0, 0, 0, 0, 0, 0, 0) + ctx.drawImage(canvas, 0, 0, 0, 0, 1, 1, 1, 1) + ctx.drawImage(canvas, 1, 1, 1, 1, 0, 0, 0, 0) + ctx.fillStyle = 'white' + ctx.fillRect(0, 0, 500, 500) + + imgd = ctx.getImageData(0, 0, 500, 500) + data = imgd.data + count = 0 + + for (i = 0; i < 500 * 500 * 4; i += 4) { + if (data[i] === 255 && data[i + 1] === 255 && data[i + 2] === 255) { count++ } } - assert.strictEqual(count, 500 * 500); - }); + assert.strictEqual(count, 500 * 500) + }) it('Context2d#SetFillColor()', function () { - var canvas = createCanvas(2, 2); - var ctx = canvas.getContext('2d'); + var canvas = createCanvas(2, 2) + var ctx = canvas.getContext('2d') - ctx.fillStyle = ['#808080']; - ctx.fillRect(0, 0, 2, 2); - var data = ctx.getImageData(0, 0, 2, 2).data; + ctx.fillStyle = ['#808080'] + ctx.fillRect(0, 0, 2, 2) + var data = ctx.getImageData(0, 0, 2, 2).data data.forEach(function (byte, index) { - if (index + 1 & 3) - assert.strictEqual(byte, 128); - else - assert.strictEqual(byte, 255); - }); + if (index + 1 & 3) { assert.strictEqual(byte, 128) } else { assert.strictEqual(byte, 255) } + }) assert.throws(function () { - ctx.fillStyle = Object.create(null); - }); - }); - -}); + ctx.fillStyle = Object.create(null) + }) + }) +}) diff --git a/test/dommatrix.test.js b/test/dommatrix.test.js index 212ce40a4..0386417b7 100644 --- a/test/dommatrix.test.js +++ b/test/dommatrix.test.js @@ -9,22 +9,22 @@ const assert = require('assert') // This doesn't need to be precise; we're not testing the engine's trig // implementations. const TOLERANCE = 0.001 -function assertApprox(actual, expected, tolerance) { +function assertApprox (actual, expected, tolerance) { if (typeof tolerance !== 'number') tolerance = TOLERANCE assert.ok(expected > actual - tolerance && expected < actual + tolerance, `Expected ${expected} to equal ${actual} +/- ${tolerance}`) } -function assertApproxDeep(actual, expected, tolerance) { +function assertApproxDeep (actual, expected, tolerance) { expected.forEach(function (value, index) { assertApprox(actual[index], value) }) } describe('DOMMatrix', function () { - var Avals = [4,5,1,8, 0,3,6,1, 3,5,0,9, 2,4,6,1] - var Bvals = [1,5,1,0, 0,3,6,1, 3,5,7,2, 2,0,6,1] - var AxB = [7,25,31,22, 20,43,24,58, 37,73,45,94, 28,44,8,71] - var BxA = [23,40,89,15, 20,39,66,16, 21,30,87,14, 22,52,74,17] + var Avals = [4, 5, 1, 8, 0, 3, 6, 1, 3, 5, 0, 9, 2, 4, 6, 1] + var Bvals = [1, 5, 1, 0, 0, 3, 6, 1, 3, 5, 7, 2, 2, 0, 6, 1] + var AxB = [7, 25, 31, 22, 20, 43, 24, 58, 37, 73, 45, 94, 28, 44, 8, 71] + var BxA = [23, 40, 89, 15, 20, 39, 66, 16, 21, 30, 87, 14, 22, 52, 74, 17] describe('constructor, general', function () { it('aliases a,b,c,d,e,f properly', function () { @@ -374,7 +374,7 @@ describe('DOMMatrix', function () { }) describe('skewYSelf', function () {}) - + describe('flipX', function () { it('works', function () { var x = new DOMMatrix() @@ -409,14 +409,14 @@ describe('DOMMatrix', function () { describe('transformPoint', function () { it('works', function () { var x = new DOMMatrix() - var r = x.transformPoint({x: 1, y: 2, z: 3}) + var r = x.transformPoint({ x: 1, y: 2, z: 3 }) assert.strictEqual(r.x, 1) assert.strictEqual(r.y, 2) assert.strictEqual(r.z, 3) assert.strictEqual(r.w, 1) x.rotateSelf(70) - r = x.transformPoint({x: 2, y: 3, z: 4}) + r = x.transformPoint({ x: 2, y: 3, z: 4 }) assertApprox(r.x, -2.13503) assertApprox(r.y, 2.905445) assert.strictEqual(r.z, 4) @@ -437,7 +437,7 @@ describe('DOMMatrix', function () { ]) }) }) - + describe('toFloat64Array', function () { it('works', function () { var x = new DOMMatrix() diff --git a/test/image.test.js b/test/image.test.js index 8e79cae2f..e06642c65 100644 --- a/test/image.test.js +++ b/test/image.test.js @@ -6,7 +6,7 @@ * Module dependencies. */ -const {createCanvas, loadImage} = require('../'); +const { createCanvas, loadImage } = require('../') const Image = require('../').Image const assert = require('assert') @@ -23,12 +23,12 @@ const bmp_dir = `${__dirname}/fixtures/bmp` describe('Image', function () { it('Prototype and ctor are well-shaped, don\'t hit asserts on accessors (GH-803)', function () { - var img = new Image(); - assert.throws(function () { Image.prototype.width; }, /incompatible receiver/); - assert(!img.hasOwnProperty('width')); - assert('width' in img); - assert(Image.prototype.hasOwnProperty('width')); - }); + var img = new Image() + assert.throws(function () { Image.prototype.width }, /incompatible receiver/) + assert(!img.hasOwnProperty('width')) + assert('width' in img) + assert(Image.prototype.hasOwnProperty('width')) + }) it('loads JPEG image', function () { return loadImage(jpg_face).then((img) => { @@ -87,7 +87,7 @@ describe('Image', function () { it('loads SVG data URL base64', function () { const base64Enc = fs.readFileSync(svg_tree, 'base64') const dataURL = `data:image/svg+xml;base64,${base64Enc}` - return loadImage(dataURL).then((img) => { + return loadImage(dataURL).then((img) => { assert.strictEqual(img.onerror, null) assert.strictEqual(img.onload, null) assert.strictEqual(img.width, 200) @@ -99,7 +99,7 @@ describe('Image', function () { it('loads SVG data URL utf8', function () { const utf8Encoded = fs.readFileSync(svg_tree, 'utf8') const dataURL = `data:image/svg+xml;utf8,${utf8Encoded}` - return loadImage(dataURL).then((img) => { + return loadImage(dataURL).then((img) => { assert.strictEqual(img.onerror, null) assert.strictEqual(img.onload, null) assert.strictEqual(img.width, 200) @@ -154,7 +154,7 @@ describe('Image', function () { it('captures errors from libjpeg', function (done) { const img = new Image() img.onerror = err => { - assert.equal(err.message, "JPEG datastream contains no image") + assert.equal(err.message, 'JPEG datastream contains no image') done() } img.src = `${__dirname}/fixtures/159-crash1.jpg` @@ -213,7 +213,7 @@ describe('Image', function () { }) }) - it('should unbind Image#onload', function() { + it('should unbind Image#onload', function () { return loadImage(png_clock).then((img) => { let onloadCalled = 0 @@ -240,7 +240,7 @@ describe('Image', function () { }) }) - it('should unbind Image#onerror', function() { + it('should unbind Image#onerror', function () { return loadImage(png_clock).then((img) => { let onloadCalled = 0 let onerrorCalled = 0 @@ -297,53 +297,53 @@ describe('Image', function () { }) it('does not contain `source` property', function () { - var keys = Reflect.ownKeys(Image.prototype); - assert.ok(!keys.includes('source')); - assert.ok(!keys.includes('getSource')); - assert.ok(!keys.includes('setSource')); - }); + var keys = Reflect.ownKeys(Image.prototype) + assert.ok(!keys.includes('source')) + assert.ok(!keys.includes('getSource')) + assert.ok(!keys.includes('setSource')) + }) describe('supports BMP', function () { it('parses 1-bit image', function (done) { - let img = new Image(); + let img = new Image() img.onload = () => { - assert.strictEqual(img.width, 111); - assert.strictEqual(img.height, 72); - done(); - }; + assert.strictEqual(img.width, 111) + assert.strictEqual(img.height, 72) + done() + } - img.onerror = err => { throw err; }; - img.src = path.join(bmp_dir, '1-bit.bmp'); - }); + img.onerror = err => { throw err } + img.src = path.join(bmp_dir, '1-bit.bmp') + }) it('parses 24-bit image', function (done) { - let img = new Image(); + let img = new Image() img.onload = () => { - assert.strictEqual(img.width, 2); - assert.strictEqual(img.height, 2); + assert.strictEqual(img.width, 2) + assert.strictEqual(img.height, 2) testImgd(img, [ 0, 0, 255, 255, 0, 255, 0, 255, 255, 0, 0, 255, - 255, 255, 255, 255, - ]); + 255, 255, 255, 255 + ]) - done(); - }; + done() + } - img.onerror = err => { throw err; }; - img.src = path.join(bmp_dir, '24-bit.bmp'); - }); + img.onerror = err => { throw err } + img.src = path.join(bmp_dir, '24-bit.bmp') + }) it('parses 32-bit image', function (done) { - let img = new Image(); + let img = new Image() img.onload = () => { - assert.strictEqual(img.width, 4); - assert.strictEqual(img.height, 2); + assert.strictEqual(img.width, 4) + assert.strictEqual(img.height, 2) testImgd(img, [ 0, 0, 255, 255, @@ -353,88 +353,88 @@ describe('Image', function () { 0, 0, 255, 127, 0, 255, 0, 127, 255, 0, 0, 127, - 255, 255, 255, 127, - ]); - - done(); - }; + 255, 255, 255, 127 + ]) - img.onerror = err => { throw err; }; - img.src = fs.readFileSync(path.join(bmp_dir, '32-bit.bmp')); // Also tests loading from buffer - }); + done() + } + + img.onerror = err => { throw err } + img.src = fs.readFileSync(path.join(bmp_dir, '32-bit.bmp')) // Also tests loading from buffer + }) it('parses minimal BMP', function (done) { - let img = new Image(); + let img = new Image() img.onload = () => { - assert.strictEqual(img.width, 1); - assert.strictEqual(img.height, 1); + assert.strictEqual(img.width, 1) + assert.strictEqual(img.height, 1) testImgd(img, [ - 255, 0, 0, 255, - ]); - - done(); - }; + 255, 0, 0, 255 + ]) + + done() + } - img.onerror = err => { throw err; }; - img.src = path.join(bmp_dir, 'min.bmp'); - }); + img.onerror = err => { throw err } + img.src = path.join(bmp_dir, 'min.bmp') + }) it('properly handles negative height', function (done) { - let img = new Image(); + let img = new Image() img.onload = () => { - assert.strictEqual(img.width, 1); - assert.strictEqual(img.height, 2); + assert.strictEqual(img.width, 1) + assert.strictEqual(img.height, 2) testImgd(img, [ 255, 0, 0, 255, - 0, 255, 0, 255, - ]); - - done(); - }; + 0, 255, 0, 255 + ]) - img.onerror = err => { throw err; }; - img.src = path.join(bmp_dir, 'negative-height.bmp'); - }); + done() + } + + img.onerror = err => { throw err } + img.src = path.join(bmp_dir, 'negative-height.bmp') + }) it('catches BMP errors', function (done) { - let img = new Image(); + let img = new Image() img.onload = () => { - throw new Error('Invalid image should not be loaded properly'); - }; + throw new Error('Invalid image should not be loaded properly') + } img.onerror = err => { - let msg = 'Error while processing file header - unexpected end of file'; - assert.strictEqual(err.message, msg); - done(); - }; + let msg = 'Error while processing file header - unexpected end of file' + assert.strictEqual(err.message, msg) + done() + } - img.src = Buffer.from('BM'); - }); + img.src = Buffer.from('BM') + }) it('BMP bomb', function (done) { - let img = new Image(); + let img = new Image() img.onload = () => { - throw new Error('Invalid image should not be loaded properly'); - }; + throw new Error('Invalid image should not be loaded properly') + } img.onerror = err => { - done(); - }; + done() + } - img.src = path.join(bmp_dir, 'bomb.bmp'); - }); + img.src = path.join(bmp_dir, 'bomb.bmp') + }) - function testImgd(img, data){ - let ctx = createCanvas(img.width, img.height).getContext('2d'); - ctx.drawImage(img, 0, 0); - var actualData = ctx.getImageData(0, 0, img.width, img.height).data; - assert.strictEqual(String(actualData), String(data)); + function testImgd (img, data) { + let ctx = createCanvas(img.width, img.height).getContext('2d') + ctx.drawImage(img, 0, 0) + var actualData = ctx.getImageData(0, 0, img.width, img.height).data + assert.strictEqual(String(actualData), String(data)) } - }); + }) }) diff --git a/test/imageData.test.js b/test/imageData.test.js index cfffe9aaf..26acd61f4 100644 --- a/test/imageData.test.js +++ b/test/imageData.test.js @@ -3,14 +3,14 @@ 'use strict' const createImageData = require('../').createImageData -const ImageData = require('../').ImageData; +const ImageData = require('../').ImageData const assert = require('assert') describe('ImageData', function () { it('Prototype and ctor are well-shaped, don\'t hit asserts on accessors (GH-803)', function () { - assert.throws(function () { ImageData.prototype.width; }, /incompatible receiver/); - }); + assert.throws(function () { ImageData.prototype.width }, /incompatible receiver/) + }) it('should throw with invalid numeric arguments', function () { assert.throws(() => { createImageData(0, 0) }, /width is zero/) @@ -33,7 +33,7 @@ describe('ImageData', function () { assert.throws(() => { createImageData(new Uint8ClampedArray(3), 0) }, /source width is zero/) // Note: Some errors thrown by browsers are not thrown by node-canvas // because our ImageData can support different BPPs. - }); + }) it('should construct with Uint8ClampedArray', function () { let data, imageData @@ -51,7 +51,7 @@ describe('ImageData', function () { assert.strictEqual(imageData.height, 4) assert(imageData.data instanceof Uint8ClampedArray) assert.strictEqual(imageData.data.length, 48) - }); + }) it('should construct with Uint16Array', function () { let data = new Uint16Array(2 * 3 * 2) @@ -67,5 +67,5 @@ describe('ImageData', function () { assert.strictEqual(imagedata.height, 4) assert(imagedata.data instanceof Uint16Array) assert.strictEqual(imagedata.data.length, 24) - }); -}); + }) +}) diff --git a/util/win_jpeg_lookup.js b/util/win_jpeg_lookup.js index 82869945a..40a1e03cf 100644 --- a/util/win_jpeg_lookup.js +++ b/util/win_jpeg_lookup.js @@ -5,17 +5,17 @@ if (process.arch === 'x64') { paths.unshift('C:/libjpeg-turbo64') } -paths.forEach(function(path){ +paths.forEach(function (path) { if (exists(path)) { process.stdout.write(path) process.exit() } }) -function exists(path) { +function exists (path) { try { return fs.lstatSync(path).isDirectory() - } catch(e) { + } catch (e) { return false } } From 9ebb62487988b4660ed844e10e186fac9ead0c45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s-Combarro=20=27piranna?= Date: Tue, 20 Nov 2018 05:47:16 +0100 Subject: [PATCH 03/26] Move `getFormat()` implementation out of header file --- src/backend/Backend.cc | 6 ++++++ src/backend/Backend.h | 5 ++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/backend/Backend.cc b/src/backend/Backend.cc index 0ea3b7af2..28e7fc95d 100644 --- a/src/backend/Backend.cc +++ b/src/backend/Backend.cc @@ -3,6 +3,7 @@ Backend::Backend(string name) : name(name) + , format(CAIRO_FORMAT_INVALID) , width(0) , height(0) , surface(NULL) @@ -10,6 +11,7 @@ Backend::Backend(string name) {} Backend::Backend(string name, int width, int height) : name(name) + , format(CAIRO_FORMAT_INVALID) , width(width) , height(height) , surface(NULL) @@ -86,6 +88,10 @@ void Backend::setHeight(int height_) this->recreateSurface(); } +cairo_format_t Backend::getFormat() +{ + return this->format; +} bool Backend::isSurfaceValid(){ bool hadSurface = surface != NULL; bool isValid = true; diff --git a/src/backend/Backend.h b/src/backend/Backend.h index 5bbc80f4e..31a67d57d 100644 --- a/src/backend/Backend.h +++ b/src/backend/Backend.h @@ -21,6 +21,7 @@ class Backend : public Nan::ObjectWrap private: const string name; const char* error = NULL; + cairo_format_t format; protected: int width; @@ -53,9 +54,7 @@ class Backend : public Nan::ObjectWrap virtual void setHeight(int height); // Overridden by ImageBackend. SVG and PDF thus always return INVALID. - virtual cairo_format_t getFormat() { - return CAIRO_FORMAT_INVALID; - } + virtual cairo_format_t getFormat(); bool isSurfaceValid(); inline const char* getError(){ return error; } From b37d64813ce44ad91be05083c6c852668158afa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s-Combarro=20=27piranna?= Date: Tue, 20 Nov 2018 05:47:59 +0100 Subject: [PATCH 04/26] Added `Backend::setFormat()` method --- src/backend/Backend.cc | 6 ++++++ src/backend/Backend.h | 1 + 2 files changed, 7 insertions(+) diff --git a/src/backend/Backend.cc b/src/backend/Backend.cc index 28e7fc95d..1379a9339 100644 --- a/src/backend/Backend.cc +++ b/src/backend/Backend.cc @@ -92,6 +92,12 @@ cairo_format_t Backend::getFormat() { return this->format; } +void Backend::setFormat(cairo_format_t format) +{ + this->format = format; + this->recreateSurface(); +} + bool Backend::isSurfaceValid(){ bool hadSurface = surface != NULL; bool isValid = true; diff --git a/src/backend/Backend.h b/src/backend/Backend.h index 31a67d57d..ddb74c985 100644 --- a/src/backend/Backend.h +++ b/src/backend/Backend.h @@ -55,6 +55,7 @@ class Backend : public Nan::ObjectWrap // Overridden by ImageBackend. SVG and PDF thus always return INVALID. virtual cairo_format_t getFormat(); + virtual void setFormat(cairo_format_t format); bool isSurfaceValid(); inline const char* getError(){ return error; } From 2aa765cbc42086ce04c3e1fc3cca8311ea71b394 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s-Combarro=20=27piranna?= Date: Tue, 20 Nov 2018 05:49:48 +0100 Subject: [PATCH 05/26] Added `FBDevBackend::setFormat()` method --- src/backend/FBDevBackend.cc | 21 +++++++++++++++++++++ src/backend/FBDevBackend.h | 1 + 2 files changed, 22 insertions(+) diff --git a/src/backend/FBDevBackend.cc b/src/backend/FBDevBackend.cc index 581be177c..b1cc44829 100644 --- a/src/backend/FBDevBackend.cc +++ b/src/backend/FBDevBackend.cc @@ -113,6 +113,27 @@ void FBDevBackend::setHeight(int height) Backend::setHeight(width); } +void FBDevBackend::setFormat(cairo_format_t format) +{ + struct fb_var_screeninfo fb_vinfo; + + this->FbDevIoctlHelper(FBIOGET_VSCREENINFO, &fb_vinfo, + "Error reading variable framebuffer information"); + + switch(format) + { + case CAIRO_FORMAT_RGB16_565: fb_vinfo.bits_per_pixel = 16; break; + case CAIRO_FORMAT_ARGB32: fb_vinfo.bits_per_pixel = 32; break; + + default: + throw FBDevBackendException("Only valid formats are RGB16_565 & ARGB32"); + } + + this->FbDevIoctlHelper(FBIOPUT_VSCREENINFO, &fb_vinfo, + "Error setting variable framebuffer information"); + + Backend::setFormat(format); +} Nan::Persistent FBDevBackend::constructor; diff --git a/src/backend/FBDevBackend.h b/src/backend/FBDevBackend.h index 55e90e51c..51d51400b 100644 --- a/src/backend/FBDevBackend.h +++ b/src/backend/FBDevBackend.h @@ -17,6 +17,7 @@ cairo_surface_t* createSurface(); void setWidth(int width); void setHeight(int height); + void setFormat(cairo_format_t format); public: FBDevBackend(string deviceName); static Nan::Persistent constructor; From 2e60f78beaa21a1a85e49574b176bc340cae1a3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s-Combarro=20=27piranna?= Date: Tue, 20 Nov 2018 05:51:53 +0100 Subject: [PATCH 06/26] [FBDevBackend] Get `width` and `height` in constructor --- src/backend/FBDevBackend.cc | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/backend/FBDevBackend.cc b/src/backend/FBDevBackend.cc index b1cc44829..723a0ebb3 100644 --- a/src/backend/FBDevBackend.cc +++ b/src/backend/FBDevBackend.cc @@ -37,6 +37,14 @@ FBDevBackend::FBDevBackend(string deviceName) if(this->fb_data == MAP_FAILED) throw FBDevBackendException("Failed to map framebuffer device to memory"); + + struct fb_var_screeninfo fb_vinfo; + + this->FbDevIoctlHelper(FBIOGET_VSCREENINFO, &fb_vinfo, + "Error reading variable framebuffer information"); + + Backend::setWidth(fb_vinfo.xres); + Backend::setHeight(fb_vinfo.yres); } FBDevBackend::~FBDevBackend() @@ -63,9 +71,6 @@ cairo_surface_t* FBDevBackend::createSurface() this->FbDevIoctlHelper(FBIOGET_VSCREENINFO, &fb_vinfo, "Error reading variable framebuffer information"); - this->width = fb_vinfo.xres; - this->height = fb_vinfo.yres; - // switch through bpp and decide on which format for the cairo surface to use cairo_format_t format; switch(fb_vinfo.bits_per_pixel) From c29ef89113130e100f203dbd48e4850cb24df5b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s-Combarro=20=27piranna?= Date: Tue, 20 Nov 2018 05:52:58 +0100 Subject: [PATCH 07/26] [FBDevBackend] Set `format` in constructor --- src/backend/FBDevBackend.cc | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/backend/FBDevBackend.cc b/src/backend/FBDevBackend.cc index 723a0ebb3..1c6b19a2c 100644 --- a/src/backend/FBDevBackend.cc +++ b/src/backend/FBDevBackend.cc @@ -15,6 +15,20 @@ using namespace v8; +// switch through bpp and decide on which format for the cairo surface to use +cairo_format_t bits2format(__u32 bits_per_pixel) +{ + switch(bits_per_pixel) + { + case 16: return CAIRO_FORMAT_RGB16_565; + case 32: return CAIRO_FORMAT_ARGB32; + + default: + throw FBDevBackendException("Only valid formats are RGB16_565 & ARGB32"); + } +} + + FBDevBackend::FBDevBackend(string deviceName) : Backend("fbdev") { @@ -45,6 +59,7 @@ FBDevBackend::FBDevBackend(string deviceName) Backend::setWidth(fb_vinfo.xres); Backend::setHeight(fb_vinfo.yres); + Backend::setFormat(bits2format(fb_vinfo.bits_per_pixel)); } FBDevBackend::~FBDevBackend() @@ -71,20 +86,10 @@ cairo_surface_t* FBDevBackend::createSurface() this->FbDevIoctlHelper(FBIOGET_VSCREENINFO, &fb_vinfo, "Error reading variable framebuffer information"); - // switch through bpp and decide on which format for the cairo surface to use - cairo_format_t format; - switch(fb_vinfo.bits_per_pixel) - { - case 16: format = CAIRO_FORMAT_RGB16_565; break; - case 32: format = CAIRO_FORMAT_ARGB32; break; - - default: - throw FBDevBackendException("Only valid formats are RGB16_565 & ARGB32"); - } - // create cairo surface from data - this->surface = cairo_image_surface_create_for_data(this->fb_data, format, - this->width, this->height, fb_finfo.line_length); + this->surface = cairo_image_surface_create_for_data(this->fb_data, + bits2format(fb_vinfo.bits_per_pixel), fb_vinfo.xres, fb_vinfo.yres, + fb_finfo.line_length); return this->surface; } From 851ffb208602e2596e1fd7e0b828c639a645e0c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s-Combarro=20=27piranna?= Date: Tue, 20 Nov 2018 05:54:18 +0100 Subject: [PATCH 08/26] [FBDevBackend][fix] Wrongly copy&pasted variable --- src/backend/FBDevBackend.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/FBDevBackend.cc b/src/backend/FBDevBackend.cc index 1c6b19a2c..601696464 100644 --- a/src/backend/FBDevBackend.cc +++ b/src/backend/FBDevBackend.cc @@ -121,7 +121,7 @@ void FBDevBackend::setHeight(int height) this->FbDevIoctlHelper(FBIOPUT_VSCREENINFO, &fb_vinfo, "Error setting variable framebuffer information"); - Backend::setHeight(width); + Backend::setHeight(height); } void FBDevBackend::setFormat(cairo_format_t format) { From ad2ddc011398fdfd7f8faa15a1c441f4a256961c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s-Combarro=20=27piranna?= Date: Wed, 28 Nov 2018 20:26:59 +0100 Subject: [PATCH 09/26] [FbDev] Set default FbDev device as constant --- examples/simple_fbdev.js | 2 +- src/backend/FBDevBackend.cc | 2 +- src/backend/FBDevBackend.h | 7 ++++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/examples/simple_fbdev.js b/examples/simple_fbdev.js index a1f28df5c..6c8832495 100644 --- a/examples/simple_fbdev.js +++ b/examples/simple_fbdev.js @@ -11,7 +11,7 @@ const { backends: { FBDevBackend }, Canvas } = require('..') const squareSize = 100 -var device = process.argv[2] || '/dev/fb0' +var device = process.argv[2] var backend = new FBDevBackend(device) var canvas = new Canvas(backend) diff --git a/src/backend/FBDevBackend.cc b/src/backend/FBDevBackend.cc index 601696464..7042572c2 100644 --- a/src/backend/FBDevBackend.cc +++ b/src/backend/FBDevBackend.cc @@ -161,7 +161,7 @@ void FBDevBackend::Initialize(Handle target) NAN_METHOD(FBDevBackend::New) { - string fbDevice = "/dev/fb0"; + string fbDevice = DEFAULT_DEVICE; if(info[0]->IsString()) fbDevice = *String::Utf8Value(info[0].As()); FBDevBackend* backend = new FBDevBackend(fbDevice); diff --git a/src/backend/FBDevBackend.h b/src/backend/FBDevBackend.h index 51d51400b..17b6a686f 100644 --- a/src/backend/FBDevBackend.h +++ b/src/backend/FBDevBackend.h @@ -6,7 +6,12 @@ #include #include "Backend.h" using namespace std; - class FBDevBackend : public Backend + + +const string DEFAULT_DEVICE = "/dev/fb0"; + + +class FBDevBackend : public Backend { private: int fb_fd; From 3e4f681a74b14391e1578107735b163f38ce3c0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s-Combarro=20=27piranna?= Date: Wed, 28 Nov 2018 20:33:22 +0100 Subject: [PATCH 10/26] [FbDev] Move initialization of framebuffer device to `initFbDev()` method --- src/backend/FBDevBackend.cc | 36 +++++++++++++++++++++--------------- src/backend/FBDevBackend.h | 11 +++++++---- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src/backend/FBDevBackend.cc b/src/backend/FBDevBackend.cc index 7042572c2..e82e32a51 100644 --- a/src/backend/FBDevBackend.cc +++ b/src/backend/FBDevBackend.cc @@ -31,6 +31,26 @@ cairo_format_t bits2format(__u32 bits_per_pixel) FBDevBackend::FBDevBackend(string deviceName) : Backend("fbdev") +{ + struct fb_var_screeninfo fb_vinfo; + + this->initFbDev(deviceName, &fb_vinfo); + + Backend::setWidth(fb_vinfo.xres); + Backend::setHeight(fb_vinfo.yres); + Backend::setFormat(bits2format(fb_vinfo.bits_per_pixel)); +} + +FBDevBackend::~FBDevBackend() +{ + this->destroySurface(); + + munmap(this->fb_data, this->fb_finfo.smem_len); + close(this->fb_fd); +} + + +void FBDevBackend::initFbDev(string deviceName, struct fb_var_screeninfo* fb_vinfo) { // Open the file for reading and writing this->fb_fd = open(deviceName.c_str(), O_RDWR); @@ -52,22 +72,8 @@ FBDevBackend::FBDevBackend(string deviceName) if(this->fb_data == MAP_FAILED) throw FBDevBackendException("Failed to map framebuffer device to memory"); - struct fb_var_screeninfo fb_vinfo; - - this->FbDevIoctlHelper(FBIOGET_VSCREENINFO, &fb_vinfo, + this->FbDevIoctlHelper(FBIOGET_VSCREENINFO, fb_vinfo, "Error reading variable framebuffer information"); - - Backend::setWidth(fb_vinfo.xres); - Backend::setHeight(fb_vinfo.yres); - Backend::setFormat(bits2format(fb_vinfo.bits_per_pixel)); -} - -FBDevBackend::~FBDevBackend() -{ - this->destroySurface(); - - munmap(this->fb_data, this->fb_finfo.smem_len); - close(this->fb_fd); } diff --git a/src/backend/FBDevBackend.h b/src/backend/FBDevBackend.h index 17b6a686f..962d703d0 100644 --- a/src/backend/FBDevBackend.h +++ b/src/backend/FBDevBackend.h @@ -17,10 +17,13 @@ class FBDevBackend : public Backend int fb_fd; struct fb_fix_screeninfo fb_finfo; unsigned char* fb_data; - ~FBDevBackend(); - void FbDevIoctlHelper(unsigned long request, void* data, string errmsg); - cairo_surface_t* createSurface(); - void setWidth(int width); + + ~FBDevBackend(); + + void initFbDev(string deviceName, struct fb_var_screeninfo* fb_vinfo); + void FbDevIoctlHelper(unsigned long request, void* data, string errmsg); + cairo_surface_t* createSurface(); + void setWidth(int width); void setHeight(int height); void setFormat(cairo_format_t format); public: From cf99d46d7817a92660dce9a2efa71354dfe79c71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s-Combarro=20=27piranna?= Date: Wed, 28 Nov 2018 20:38:37 +0100 Subject: [PATCH 11/26] [FbDev] Allow to define framebuffer device from `Canvas` constructor --- src/Canvas.cc | 8 +++++++- src/backend/FBDevBackend.cc | 10 ++++++++++ src/backend/FBDevBackend.h | 1 + 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/Canvas.cc b/src/Canvas.cc index 549705ad9..58727c5a2 100644 --- a/src/Canvas.cc +++ b/src/Canvas.cc @@ -94,7 +94,13 @@ NAN_METHOD(Canvas::New) { if (info[1]->IsNumber()) height = Nan::To(info[1]).FromMaybe(0); if (info[2]->IsString()) { - if (0 == strcmp("pdf", *Nan::Utf8String(info[2]))) + if (0 == strcmp("fbdev", *Nan::Utf8String(info[2]))) { + if (info[3]->IsString()) + backend = new FBDevBackend(width, height, *Nan::Utf8String(info[3])); + else + backend = new FBDevBackend(width, height); + } + else if (0 == strcmp("pdf", *Nan::Utf8String(info[2]))) backend = new PdfBackend(width, height); else if (0 == strcmp("svg", *Nan::Utf8String(info[2]))) backend = new SvgBackend(width, height); diff --git a/src/backend/FBDevBackend.cc b/src/backend/FBDevBackend.cc index e82e32a51..65bda45a0 100644 --- a/src/backend/FBDevBackend.cc +++ b/src/backend/FBDevBackend.cc @@ -29,6 +29,16 @@ cairo_format_t bits2format(__u32 bits_per_pixel) } +FBDevBackend::FBDevBackend(int width, int height, string deviceName) + : Backend("fbdev", width, height) +{ + struct fb_var_screeninfo fb_vinfo; + + this->initFbDev(deviceName, &fb_vinfo); + + Backend::setFormat(bits2format(fb_vinfo.bits_per_pixel)); +} + FBDevBackend::FBDevBackend(string deviceName) : Backend("fbdev") { diff --git a/src/backend/FBDevBackend.h b/src/backend/FBDevBackend.h index 962d703d0..3ed436333 100644 --- a/src/backend/FBDevBackend.h +++ b/src/backend/FBDevBackend.h @@ -27,6 +27,7 @@ class FBDevBackend : public Backend void setHeight(int height); void setFormat(cairo_format_t format); public: + FBDevBackend(int width, int height, string deviceName = DEFAULT_DEVICE); FBDevBackend(string deviceName); static Nan::Persistent constructor; static void Initialize(v8::Handle target); From 5cc863151b64b12ebfcda667ee6838dc1b1cf226 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s-Combarro=20=27piranna?= Date: Wed, 28 Nov 2018 21:02:39 +0100 Subject: [PATCH 12/26] [FbDev] Don't regenerate surface on creation for non-standard color modes --- src/backend/Backend.h | 2 +- src/backend/FBDevBackend.cc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/backend/Backend.h b/src/backend/Backend.h index ddb74c985..12debc196 100644 --- a/src/backend/Backend.h +++ b/src/backend/Backend.h @@ -21,11 +21,11 @@ class Backend : public Nan::ObjectWrap private: const string name; const char* error = NULL; - cairo_format_t format; protected: int width; int height; + cairo_format_t format; cairo_surface_t* surface; Canvas* canvas; diff --git a/src/backend/FBDevBackend.cc b/src/backend/FBDevBackend.cc index 65bda45a0..44dc285ce 100644 --- a/src/backend/FBDevBackend.cc +++ b/src/backend/FBDevBackend.cc @@ -36,7 +36,6 @@ FBDevBackend::FBDevBackend(int width, int height, string deviceName) this->initFbDev(deviceName, &fb_vinfo); - Backend::setFormat(bits2format(fb_vinfo.bits_per_pixel)); } FBDevBackend::FBDevBackend(string deviceName) @@ -48,7 +47,6 @@ FBDevBackend::FBDevBackend(string deviceName) Backend::setWidth(fb_vinfo.xres); Backend::setHeight(fb_vinfo.yres); - Backend::setFormat(bits2format(fb_vinfo.bits_per_pixel)); } FBDevBackend::~FBDevBackend() @@ -84,6 +82,8 @@ void FBDevBackend::initFbDev(string deviceName, struct fb_var_screeninfo* fb_vin this->FbDevIoctlHelper(FBIOGET_VSCREENINFO, fb_vinfo, "Error reading variable framebuffer information"); + + this->format = bits2format(fb_vinfo->bits_per_pixel); } From 9a583f369ea8d47f71964af40d53600c7af8edb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s-Combarro=20=27piranna?= Date: Wed, 28 Nov 2018 21:03:43 +0100 Subject: [PATCH 13/26] [FbDev] Don't regenerate surface on creation to define the width and height --- src/backend/FBDevBackend.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/FBDevBackend.cc b/src/backend/FBDevBackend.cc index 44dc285ce..9a1e725b9 100644 --- a/src/backend/FBDevBackend.cc +++ b/src/backend/FBDevBackend.cc @@ -45,8 +45,8 @@ FBDevBackend::FBDevBackend(string deviceName) this->initFbDev(deviceName, &fb_vinfo); - Backend::setWidth(fb_vinfo.xres); - Backend::setHeight(fb_vinfo.yres); + this->width = fb_vinfo.xres; + this->height = fb_vinfo.yres; } FBDevBackend::~FBDevBackend() From 58382524c9be869588aab9fa1770d4c2ac09e043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s-Combarro=20=27piranna?= Date: Wed, 28 Nov 2018 21:04:32 +0100 Subject: [PATCH 14/26] [FbDev][fix] Set framebuffer dimensions if specified --- src/backend/FBDevBackend.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/backend/FBDevBackend.cc b/src/backend/FBDevBackend.cc index 9a1e725b9..450c2bd56 100644 --- a/src/backend/FBDevBackend.cc +++ b/src/backend/FBDevBackend.cc @@ -36,6 +36,11 @@ FBDevBackend::FBDevBackend(int width, int height, string deviceName) this->initFbDev(deviceName, &fb_vinfo); + fb_vinfo.xres = width; + fb_vinfo.yres = height; + + this->FbDevIoctlHelper(FBIOPUT_VSCREENINFO, &fb_vinfo, + "Error setting variable framebuffer information"); } FBDevBackend::FBDevBackend(string deviceName) From 848ab2e09e5d72550d3ac2c6fa2109fbf4ffede0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s-Combarro=20=27piranna?= Date: Sat, 1 Dec 2018 11:32:51 +0100 Subject: [PATCH 15/26] [Backend] Use default arguments to remove duplicated constructor --- src/backend/Backend.cc | 8 -------- src/backend/Backend.h | 3 +-- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/backend/Backend.cc b/src/backend/Backend.cc index 1379a9339..a4991c69c 100644 --- a/src/backend/Backend.cc +++ b/src/backend/Backend.cc @@ -1,14 +1,6 @@ #include "Backend.h" -Backend::Backend(string name) - : name(name) - , format(CAIRO_FORMAT_INVALID) - , width(0) - , height(0) - , surface(NULL) - , canvas(NULL) -{} Backend::Backend(string name, int width, int height) : name(name) , format(CAIRO_FORMAT_INVALID) diff --git a/src/backend/Backend.h b/src/backend/Backend.h index 12debc196..c53b9daae 100644 --- a/src/backend/Backend.h +++ b/src/backend/Backend.h @@ -29,8 +29,7 @@ class Backend : public Nan::ObjectWrap cairo_surface_t* surface; Canvas* canvas; - Backend(string name); - Backend(string name, int width, int height); + Backend(string name, int width = 0, int height = 0); static void init(const Nan::FunctionCallbackInfo &info); static Backend *construct(int width, int height){ return nullptr; } From 0e8be2246aa27db45e2e41b60ef9f6210db61152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s-Combarro=20=27piranna?= Date: Fri, 7 Dec 2018 20:35:03 +0100 Subject: [PATCH 16/26] clean-up --- src/backend/FBDevBackend.h | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/backend/FBDevBackend.h b/src/backend/FBDevBackend.h index 3ed436333..73abcb919 100644 --- a/src/backend/FBDevBackend.h +++ b/src/backend/FBDevBackend.h @@ -1,11 +1,16 @@ #ifndef __FBDEV_BACKEND_H__ #define __FBDEV_BACKEND_H__ - #include + +#include #include - #include - #include - #include "Backend.h" - using namespace std; + +#include +#include + +#include "Backend.h" + + +using namespace std; const string DEFAULT_DEVICE = "/dev/fb0"; @@ -29,17 +34,23 @@ class FBDevBackend : public Backend public: FBDevBackend(int width, int height, string deviceName = DEFAULT_DEVICE); FBDevBackend(string deviceName); - static Nan::Persistent constructor; + + static Nan::Persistent constructor; static void Initialize(v8::Handle target); static NAN_METHOD(New); }; - class FBDevBackendException : public std::exception + + +class FBDevBackendException : public std::exception { private: string err_msg; - public: + + public: FBDevBackendException(const string msg) : err_msg(msg) {}; ~FBDevBackendException() throw() {}; - const char *what() const throw() { return this->err_msg.c_str(); }; + + const char *what() const throw() { return this->err_msg.c_str(); }; }; - #endif + +#endif From 81fa69f90b94770706ed4a7af406f995e78a2c41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s-Combarro=20=27piranna?= Date: Fri, 7 Dec 2018 20:41:40 +0100 Subject: [PATCH 17/26] [FbDev] Show used pixel format in `simple_fbdev` example --- examples/simple_fbdev.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/simple_fbdev.js b/examples/simple_fbdev.js index 6c8832495..f8b440322 100644 --- a/examples/simple_fbdev.js +++ b/examples/simple_fbdev.js @@ -32,7 +32,8 @@ ctx.fillRect(0, offsetY, squareSize, squareSize) ctx.fillStyle = '#FFFFFF' ctx.fillRect(offsetX, offsetY, squareSize, squareSize) -console.log('Width: ' + canvas.width + ', Height: ' + canvas.height) +console.log('Width: ' + canvas.width + ', Height: ' + canvas.height + + 'Pixel format: ' + ctx.pixelFormat) var outPath = join(__dirname, 'rectangle.png') From 6a22cb2c118adc991a10a73a3080f62c38e195d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s-Combarro=20=27piranna?= Date: Mon, 24 Dec 2018 17:27:14 +0100 Subject: [PATCH 18/26] Fixed documentation --- Readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Readme.md b/Readme.md index c76a77fb7..2cdbccf65 100644 --- a/Readme.md +++ b/Readme.md @@ -97,7 +97,7 @@ This project is an implementation of the Web Canvas API and implements that API ### createCanvas() > ```ts -> createCanvas(width: number, height: number, type?: 'PDF'|'SVG') => Canvas +> createCanvas(width: number, height: number, type?: 'fbdev'|'pdf'|'svg') => Canvas > ``` Creates a Canvas instance. This method works in both Node.js and Web browsers, where there is no Canvas constructor. (See `browser.js` for the implementation that runs in browsers.) @@ -463,7 +463,7 @@ See also: ## SVG Output Support -node-canvas can create SVG documents instead of images. The canva type must be set when creating the canvas as follows: +node-canvas can create SVG documents instead of images. The canvas type must be set when creating the canvas as follows: ```js const canvas = createCanvas(200, 500, 'svg') From bfb4f1d469096da8648e16631ca8456e825cb80f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s-Combarro=20=27piranna?= Date: Sat, 21 Mar 2020 15:00:03 +0100 Subject: [PATCH 19/26] Fixes after merge --- lib/image.js | 17 ++++++----------- src/backend/FBDevBackend.cc | 8 +++++--- src/backend/FBDevBackend.h | 2 +- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/lib/image.js b/lib/image.js index 15cd6a18e..a09bcc8e0 100644 --- a/lib/image.js +++ b/lib/image.js @@ -17,12 +17,7 @@ const util = require('util') // Lazily loaded simple-get let get; -const proto = Image.prototype -const _getSource = proto.getSource -const _setSource = proto.setSource - -delete proto.getSource -delete proto.setSource +const {GetSource, SetSource} = bindings; Object.defineProperty(Image.prototype, 'src', { /** @@ -88,11 +83,11 @@ Image.prototype[util.inspect.custom || 'inspect'] = function(){ + ']'; }; -function getSource (img) { - return img._originalSource || _getSource.call(img) +function getSource(img){ + return img._originalSource || GetSource.call(img); } -function setSource (img, src, origSrc) { - _setSource.call(img, src) - img._originalSource = origSrc +function setSource(img, src, origSrc){ + SetSource.call(img, src); + img._originalSource = origSrc; } diff --git a/src/backend/FBDevBackend.cc b/src/backend/FBDevBackend.cc index 450c2bd56..8117d1165 100644 --- a/src/backend/FBDevBackend.cc +++ b/src/backend/FBDevBackend.cc @@ -169,7 +169,7 @@ void FBDevBackend::setFormat(cairo_format_t format) Nan::Persistent FBDevBackend::constructor; -void FBDevBackend::Initialize(Handle target) +void FBDevBackend::Initialize(Local target) { Nan::HandleScope scope; @@ -177,13 +177,15 @@ void FBDevBackend::Initialize(Handle target) FBDevBackend::constructor.Reset(ctor); ctor->InstanceTemplate()->SetInternalFieldCount(1); ctor->SetClassName(Nan::New("FBDevBackend").ToLocalChecked()); - target->Set(Nan::New("FBDevBackend").ToLocalChecked(), ctor->GetFunction()); + Nan::Set(target, + Nan::New("FBDevBackend").ToLocalChecked(), + Nan::GetFunction(ctor).ToLocalChecked()).Check(); } NAN_METHOD(FBDevBackend::New) { string fbDevice = DEFAULT_DEVICE; - if(info[0]->IsString()) fbDevice = *String::Utf8Value(info[0].As()); + if(info[0]->IsString()) fbDevice = *Nan::Utf8String(info[0]); FBDevBackend* backend = new FBDevBackend(fbDevice); diff --git a/src/backend/FBDevBackend.h b/src/backend/FBDevBackend.h index 73abcb919..902024164 100644 --- a/src/backend/FBDevBackend.h +++ b/src/backend/FBDevBackend.h @@ -36,7 +36,7 @@ class FBDevBackend : public Backend FBDevBackend(string deviceName); static Nan::Persistent constructor; - static void Initialize(v8::Handle target); + static void Initialize(v8::Local target); static NAN_METHOD(New); }; From b5f5dd07bd44d565e42b8ecd70d2118cd2df8c8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s-Combarro=20=27piranna?= Date: Sat, 21 Mar 2020 15:00:33 +0100 Subject: [PATCH 20/26] Fixed linting --- test/public/tests.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/test/public/tests.js b/test/public/tests.js index 31ee6aaca..2b67ad680 100644 --- a/test/public/tests.js +++ b/test/public/tests.js @@ -585,7 +585,7 @@ tests['createRadialGradient()'] = function (ctx) { ctx.fillRect(0, 0, 150, 150) } -tests['globalAlpha'] = function (ctx) { +tests.globalAlpha = function (ctx) { ctx.globalAlpha = 0.5 ctx.fillStyle = 'rgba(0,0,0,0.5)' ctx.strokeRect(0, 0, 50, 50) @@ -618,7 +618,7 @@ tests['globalAlpha 2'] = function (ctx) { } } -tests['fillStyle'] = function (ctx) { +tests.fillStyle = function (ctx) { for (var i = 0; i < 6; i++) { for (var j = 0; j < 6; j++) { ctx.fillStyle = 'rgb(' + Math.floor(255 - 42.5 * i) + ',' + Math.floor(255 - 42.5 * j) + ',0)' @@ -627,7 +627,7 @@ tests['fillStyle'] = function (ctx) { } } -tests['strokeStyle'] = 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) + ',' + @@ -662,7 +662,7 @@ tests['floating point coordinates'] = function (ctx) { ctx.stroke() } -tests['lineWidth'] = function (ctx) { +tests.lineWidth = function (ctx) { for (var i = 0; i < 10; i++) { ctx.lineWidth = 1 + i ctx.beginPath() @@ -718,7 +718,7 @@ tests['lineCap default'] = function (ctx) { ctx.stroke() } -tests['lineCap'] = function (ctx) { +tests.lineCap = function (ctx) { ctx.beginPath() ctx.lineWidth = 10.0 ctx.lineCap = 'round' @@ -728,7 +728,7 @@ tests['lineCap'] = function (ctx) { ctx.stroke() } -tests['lineJoin'] = function (ctx) { +tests.lineJoin = function (ctx) { ctx.beginPath() ctx.lineWidth = 10.0 ctx.lineJoin = 'round' @@ -738,7 +738,7 @@ tests['lineJoin'] = function (ctx) { ctx.stroke() } -tests['states'] = function (ctx) { +tests.states = function (ctx) { ctx.save() ctx.rect(50, 50, 100, 100) ctx.stroke() @@ -1325,7 +1325,7 @@ tests['known bug #416'] = function (ctx, done) { img1.src = imageSrc('existing.png') } -tests['shadowBlur'] = function (ctx) { +tests.shadowBlur = function (ctx) { ctx.fillRect(150, 10, 20, 20) ctx.lineTo(20, 5) @@ -1351,7 +1351,7 @@ tests['shadowBlur'] = function (ctx) { ctx.fillRect(150, 150, 20, 20) } -tests['shadowColor'] = function (ctx) { +tests.shadowColor = function (ctx) { ctx.fillRect(150, 10, 20, 20) ctx.lineTo(20, 5) @@ -2249,7 +2249,7 @@ tests['putImageData() png data 3'] = function (ctx, done) { img.src = imageSrc('state.png') } -tests['setLineDash'] = function (ctx) { +tests.setLineDash = function (ctx) { ctx.setLineDash([10, 5, 25, 15]) ctx.lineWidth = 14 @@ -2283,7 +2283,7 @@ tests['setLineDash'] = function (ctx) { line([0, 3, 0, 0], 'green') // should be empty } -tests['lineDashOffset'] = function (ctx) { +tests.lineDashOffset = function (ctx) { ctx.setLineDash([10, 5, 25, 15]) ctx.lineWidth = 4 @@ -2310,7 +2310,7 @@ tests['lineDashOffset'] = function (ctx) { line(60, 'orange') line(-Infinity) line(70, 'purple') - line(void 0) + line(undefined) line(80, 'black') line(ctx.lineDashOffset + 10) From 64fb2e53ed9ec900fd7d2ac8529fc585afdbc521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s-Combarro=20=27piranna?= Date: Sat, 21 Mar 2020 15:01:33 +0100 Subject: [PATCH 21/26] Isolated `lint` script --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index fe619b0ea..61697ae78 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,8 @@ "scripts": { "prebenchmark": "node-gyp build", "benchmark": "node benchmarks/run.js", - "pretest": "standard examples/*.js test/server.js test/public/*.js benchmarks/run.js lib/context2d.js util/has_lib.js browser.js index.js && node-gyp build", + "lint": "standard examples/*.js test/server.js test/public/*.js benchmarks/run.js lib/context2d.js util/has_lib.js browser.js index.js", + "pretest": "npm run lint && node-gyp build", "test": "mocha test/*.test.js", "pretest-server": "node-gyp build", "test-server": "node test/server.js", From 45021a7bfd8d664ec788ece85084e86e7c77c7d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s-Combarro=20=27piranna?= Date: Sat, 21 Mar 2020 15:06:34 +0100 Subject: [PATCH 22/26] Throw exception for unknown canvas type instead create `Image` by default --- src/Canvas.cc | 2 +- test/canvas.test.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Canvas.cc b/src/Canvas.cc index 5fd4fa64a..c689c1a2f 100644 --- a/src/Canvas.cc +++ b/src/Canvas.cc @@ -114,7 +114,7 @@ NAN_METHOD(Canvas::New) { else if (0 == strcmp("svg", *Nan::Utf8String(info[2]))) backend = new SvgBackend(width, height); else - backend = new ImageBackend(width, height); + return Nan::ThrowRangeError("Unknown canvas type"); } else backend = new ImageBackend(width, height); diff --git a/test/canvas.test.js b/test/canvas.test.js index 2e1b7cf51..107132814 100644 --- a/test/canvas.test.js +++ b/test/canvas.test.js @@ -260,8 +260,8 @@ describe('Canvas', function () { assert.equal(canvas.type, 'pdf') var canvas = createCanvas(10, 10, 'svg') assert.equal(canvas.type, 'svg') - var canvas = createCanvas(10, 10, 'hey') - assert.equal(canvas.type, 'image') + + assert.throws(function () { createCanvas(10, 10, 'hey'); }, /RangeError/); }) it('Canvas#getContext("2d")', function () { From 601a415721de3d9141cac0d9c155da27f8f6d85c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s-Combarro=20=27piranna?= Date: Sat, 21 Mar 2020 15:06:46 +0100 Subject: [PATCH 23/26] Updated dependencies --- package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 61697ae78..f7c64fbc3 100644 --- a/package.json +++ b/package.json @@ -50,17 +50,17 @@ "types": "types/index.d.ts", "dependencies": { "nan": "^2.14.0", - "node-pre-gyp": "^0.11.0", + "node-pre-gyp": "^0.14.0", "simple-get": "^3.0.3" }, "devDependencies": { "@types/node": "^10.12.18", "assert-rejects": "^1.0.0", "dtslint": "^0.5.3", - "express": "^4.16.3", - "mocha": "^5.2.0", - "pixelmatch": "^4.0.2", - "standard": "^12.0.1" + "express": "^4.17.1", + "mocha": "^7.1.1", + "pixelmatch": "^5.1.0", + "standard": "^14.3.3" }, "engines": { "node": ">=6" From fdf14bcafa2346e70109873c0aeec9edec0defb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s-Combarro?= Date: Sun, 22 Mar 2020 20:57:57 +0100 Subject: [PATCH 24/26] Remove unmaintained Node.js v6 and v8 --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index d9975ab93..10b3db9ae 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,8 +5,6 @@ node_js: - '13' - '12' - '10' - - '8' - - '6' addons: apt: sources: From 0688eff0549b22cbf524a8cfa1f5c6149918b823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s-Combarro=20=27piranna?= Date: Sat, 17 Oct 2020 19:30:45 +0200 Subject: [PATCH 25/26] Update `devDependencies` to fix linting errors # Conflicts: # package.json --- package.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 5b71db4e4..8f5c17d09 100644 --- a/package.json +++ b/package.json @@ -54,13 +54,14 @@ "simple-get": "^3.0.3" }, "devDependencies": { - "@types/node": "^10.12.18", + "@types/node": "^14.11.10", "assert-rejects": "^1.0.0", - "dtslint": "^0.5.3", + "dtslint": "^4.0.4", "express": "^4.17.1", "mocha": "^7.1.1", "pixelmatch": "^5.1.0", - "standard": "^14.3.3" + "standard": "^14.3.3", + "typescript": "^4.0.3" }, "engines": { "node": ">=6" From 6f7876a04fda2c16debe40cf7acb083e6bd76142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Legan=C3=A9s-Combarro=20=27piranna?= Date: Sat, 17 Oct 2020 21:17:37 +0200 Subject: [PATCH 26/26] Enable FbDev backend only on Linux --- binding.gyp | 2 +- src/Canvas.cc | 26 +++++++++++++++++--------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/binding.gyp b/binding.gyp index bc26603f2..65777141e 100644 --- a/binding.gyp +++ b/binding.gyp @@ -145,7 +145,7 @@ 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES' } }], - ['has_FBDev=="true"', + ['OS=="linux" and has_FBDev=="true"', { 'defines': ['HAS_FBDEV'], 'sources': ['src/backend/FBDevBackend.cc'] diff --git a/src/Canvas.cc b/src/Canvas.cc index b84baa8f3..8015c3061 100644 --- a/src/Canvas.cc +++ b/src/Canvas.cc @@ -26,11 +26,14 @@ #include "JPEGStream.h" #endif -#include "backend/FBDevBackend.h" #include "backend/ImageBackend.h" #include "backend/PdfBackend.h" #include "backend/SvgBackend.h" +#ifdef HAS_FBDEV +#include "backend/FBDevBackend.h" +#endif + #define GENERIC_FACE_ERROR \ "The second argument to registerFont is required, and should be an object " \ "with at least a family (string) and optionally weight (string/number) " \ @@ -103,16 +106,18 @@ NAN_METHOD(Canvas::New) { if (info[1]->IsNumber()) height = Nan::To(info[1]).FromMaybe(0); if (info[2]->IsString()) { - if (0 == strcmp("fbdev", *Nan::Utf8String(info[2]))) { + if (0 == strcmp("pdf", *Nan::Utf8String(info[2]))) + backend = new PdfBackend(width, height); + else if (0 == strcmp("svg", *Nan::Utf8String(info[2]))) + backend = new SvgBackend(width, height); +#ifdef HAS_FBDEV + else if (0 == strcmp("fbdev", *Nan::Utf8String(info[2]))) { if (info[3]->IsString()) backend = new FBDevBackend(width, height, *Nan::Utf8String(info[3])); else backend = new FBDevBackend(width, height); } - else if (0 == strcmp("pdf", *Nan::Utf8String(info[2]))) - backend = new PdfBackend(width, height); - else if (0 == strcmp("svg", *Nan::Utf8String(info[2]))) - backend = new SvgBackend(width, height); +#endif else return Nan::ThrowRangeError("Unknown canvas type"); } @@ -120,10 +125,13 @@ NAN_METHOD(Canvas::New) { backend = new ImageBackend(width, height); } else if (info[0]->IsObject()) { - if (Nan::New(FBDevBackend::constructor)->HasInstance(info[0]) || - Nan::New(ImageBackend::constructor)->HasInstance(info[0]) || + if (Nan::New(ImageBackend::constructor)->HasInstance(info[0]) || Nan::New(PdfBackend::constructor)->HasInstance(info[0]) || - Nan::New(SvgBackend::constructor)->HasInstance(info[0])) { + Nan::New(SvgBackend::constructor)->HasInstance(info[0]) +#ifdef HAS_FBDEV + || Nan::New(FBDevBackend::constructor)->HasInstance(info[0]) +#endif + ) { backend = Nan::ObjectWrap::Unwrap(Nan::To(info[0]).ToLocalChecked()); }else{ return Nan::ThrowTypeError("Invalid arguments");