Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FbDev vsync #1542

Open
wants to merge 73 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
ad0a41f
Extracted code for FbDev support from #571 to make it easier to revie…
piranna Nov 19, 2018
3a1d55d
Fix linting
piranna Nov 20, 2018
9c8a13d
Merge branch 'linting' into framebuffer
piranna Nov 20, 2018
9ebb624
Move `getFormat()` implementation out of header file
piranna Nov 20, 2018
b37d648
Added `Backend::setFormat()` method
piranna Nov 20, 2018
2aa765c
Added `FBDevBackend::setFormat()` method
piranna Nov 20, 2018
2e60f78
[FBDevBackend] Get `width` and `height` in constructor
piranna Nov 20, 2018
c29ef89
[FBDevBackend] Set `format` in constructor
piranna Nov 20, 2018
851ffb2
[FBDevBackend][fix] Wrongly copy&pasted variable
piranna Nov 20, 2018
4940a7b
Merge branch 'master' of github.com:ventrata/node-canvas into framebu…
piranna Nov 28, 2018
ad2ddc0
[FbDev] Set default FbDev device as constant
piranna Nov 28, 2018
3e4f681
[FbDev] Move initialization of framebuffer device to `initFbDev()` me…
piranna Nov 28, 2018
cf99d46
[FbDev] Allow to define framebuffer device from `Canvas` constructor
piranna Nov 28, 2018
5cc8631
[FbDev] Don't regenerate surface on creation for non-standard color m…
piranna Nov 28, 2018
9a583f3
[FbDev] Don't regenerate surface on creation to define the width and …
piranna Nov 28, 2018
5838252
[FbDev][fix] Set framebuffer dimensions if specified
piranna Nov 28, 2018
848ab2e
[Backend] Use default arguments to remove duplicated constructor
piranna Dec 1, 2018
ffaac6b
[Backend] Add async method `waitVSync()`
piranna Dec 6, 2018
db2878f
Merge branch 'waitVSync' into FbDev_vsync
piranna Dec 6, 2018
c2e07fe
[FbDevBackend] Add support for framebuffer native VSync
piranna Dec 6, 2018
b394d31
[Backend] `swapBuffers()` method
piranna Dec 6, 2018
d617198
Merge branch 'doubleBuffer' into FbDev_vsync
piranna Dec 6, 2018
3618b40
[FbDevBackend] Support for optional double buffering (disabled by def…
piranna Dec 7, 2018
995a3f3
[FbDevBackend] Allow to enable double buffering from Javascript
piranna Dec 7, 2018
0e8be22
clean-up
piranna Dec 7, 2018
81fa69f
[FbDev] Show used pixel format in `simple_fbdev` example
piranna Dec 7, 2018
b79795d
[Backend] Made `destroySurface()` method virtual
piranna Dec 8, 2018
0778ed8
[FbDevBackend] Made `copyBackBuffer()` copy only screen size memory
piranna Dec 8, 2018
9f183c3
[FbDevBackend] Add `destroySurface()` that free in-memory buffer
piranna Dec 8, 2018
73b4b4d
[FbDevBackend] Replaced `forceUseCopyBuffer` for lazy checking of pan…
piranna Dec 8, 2018
6864325
[FbDevBackend] Added support for 24 bits framebuffers
piranna Dec 10, 2018
6bf6f1e
[FbDev] Replaced `useCopyBackBuffer` for `useFlipPages` for readibility
piranna Dec 10, 2018
9c97438
[FbDevBackend] Use `calloc()` to clean data at in-memory buffer
piranna Dec 10, 2018
8b8edd2
[Backends] Don't return `cairo_surface_t` objects in `createSurface()`
piranna Dec 13, 2018
71ee4c1
Merge branch 'protected_surfaces' into FbDev_vsync
piranna Dec 13, 2018
c21b8a5
[FbDevBackend] Don't return surface in `createSurface()`
piranna Dec 13, 2018
1c8cdea
[FbDevBackend][fix] Use pages flipping if driver supports vertical pa…
piranna Dec 13, 2018
b50fe3e
[FbDevBackend] Prevent double free'ing of back buffer memory
piranna Dec 13, 2018
96383b1
[FbDevBackend] Clean-up & improved code readibility
piranna Dec 13, 2018
f07cba4
[FbDevBackend] `mmap()` graphic memory on `createSurface()` & code cl…
piranna Dec 14, 2018
b0f81d7
[FbDevBackend] Option to enable flip buffers (disabled by default)
piranna Dec 15, 2018
e90b95c
[FbDevBackend][fix] Reset vertical panning before creating surface
piranna Dec 15, 2018
d737842
[FbDevBackend][fix] Use `swapBuffers()` always for 24 bits images
piranna Dec 15, 2018
a183067
[FbDevBackend] Code clean-up, better readibility
piranna Dec 15, 2018
a0fd140
Merge remote-tracking branch 'Automattic/master' into FbDev
piranna Dec 24, 2018
6a22cb2
Fixed documentation
piranna Dec 24, 2018
6c2d65d
Merge remote-tracking branch 'Automattic/master' into waitVSync
piranna Dec 24, 2018
ff8b503
Merge remote-tracking branch 'Automattic/master' into FbDev
piranna Mar 21, 2020
bfb4f1d
Fixes after merge
piranna Mar 21, 2020
b5f5dd0
Fixed linting
piranna Mar 21, 2020
64fb2e5
Isolated `lint` script
piranna Mar 21, 2020
45021a7
Throw exception for unknown canvas type instead create `Image` by def…
piranna Mar 21, 2020
601a415
Updated dependencies
piranna Mar 21, 2020
f7d3a76
Merge remote-tracking branch 'Automattic/master' into doubleBuffer
piranna Mar 21, 2020
3978af3
Moved screen-only `swapBuffers()` method to isolated `ScreenBuffer` c…
piranna Mar 21, 2020
6d8c0d2
Merge branch 'waitVSync' into doubleBuffer
piranna Mar 21, 2020
8e24861
Moved screens-only `waitVSync` method to `ScreenBackend` abstract class
piranna Mar 21, 2020
7b8f402
Merge branch 'FbDev' into FbDev_vsync
piranna Mar 21, 2020
aa3a6ae
Fixes after merge
piranna Mar 21, 2020
64fe34b
Merge branch 'doubleBuffer' into FbDev_vsync
piranna Mar 21, 2020
3023b36
Fixes after merge
piranna Mar 21, 2020
fdf14bc
Remove unmaintained Node.js v6 and v8
piranna Mar 22, 2020
3f38854
Remove unmaintained Node.js v6 and v8
piranna Mar 22, 2020
efacea1
Merge remote-tracking branch 'Automattic/master' into doubleBuffer
piranna Oct 17, 2020
6fb4bb4
Merge remote-tracking branch 'Automattic/master' into FbDev
piranna Oct 17, 2020
974cf5d
Merge remote-tracking branch 'Automattic/master' into FbDev_vsync
piranna Oct 17, 2020
173a8e4
Update `devDependencies` to fix linting errors
piranna Oct 17, 2020
0688eff
Update `devDependencies` to fix linting errors
piranna Oct 17, 2020
144337d
Update `devDependencies` to fix linting errors
piranna Oct 17, 2020
6f7876a
Enable FbDev backend only on Linux
piranna Oct 17, 2020
81904df
Enable FbDev backend only on Linux
piranna Oct 17, 2020
1edad18
Merge branch 'FbDev' into FbDev_vsync
piranna Oct 18, 2020
2e54e21
Merge branch 'doubleBuffer' into FbDev_vsync
piranna Oct 18, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.)
Expand Down
11 changes: 10 additions & 1 deletion binding.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
'src/backend/Backend.cc',
'src/backend/ImageBackend.cc',
'src/backend/PdfBackend.cc',
'src/backend/ScreenBackend.cc',
'src/backend/SvgBackend.cc',
'src/bmp/BMPParser.cc',
'src/Backends.cc',
Expand Down Expand Up @@ -145,6 +146,11 @@
'GCC_ENABLE_CPP_EXCEPTIONS': 'YES'
}
}],
['OS=="linux" and has_FBDev=="true"',
{
'defines': ['HAS_FBDEV'],
'sources': ['src/backend/FBDevBackend.cc']
}],
['with_jpeg=="true"', {
'defines': [
'HAVE_JPEG'
Expand Down Expand Up @@ -216,7 +222,10 @@
}]
]
}]
]
],
'variables': {
'has_FBDev%': 'true',
}
}
]
}
40 changes: 40 additions & 0 deletions examples/simple_fbdev.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/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]

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 +
'Pixel format: ' + ctx.pixelFormat)

var outPath = join(__dirname, 'rectangle.png')

canvas.createPNGStream().pipe(fs.createWriteStream(outPath))
90 changes: 45 additions & 45 deletions lib/DOMMatrix.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const util = require('util')

// 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'")
}
Expand All @@ -22,15 +22,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}`)
Expand All @@ -43,14 +43,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':
Expand Down Expand Up @@ -161,15 +161,15 @@ DOMMatrix.prototype[util.inspect.custom || '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
}
Expand All @@ -178,7 +178,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
Expand All @@ -187,31 +187,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 () {
Expand All @@ -229,15 +229,15 @@ 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
instance._values = 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++) {
Expand Down Expand Up @@ -386,10 +386,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
Expand Down Expand Up @@ -606,4 +606,4 @@ DOMMatrix.prototype.toFloat64Array = function () {
return this._values.slice(0)
}

module.exports = {DOMMatrix, DOMPoint}
module.exports = { DOMMatrix, DOMPoint }
4 changes: 2 additions & 2 deletions lib/bindings.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
'use strict';
'use strict'

module.exports = require('../build/Release/canvas.node');
module.exports = require('../build/Release/canvas.node')
72 changes: 36 additions & 36 deletions lib/canvas.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
'use strict';
'use strict'

/*!
* Canvas
Expand All @@ -21,28 +21,28 @@ Canvas.prototype[util.inspect.custom || '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
}
};
}

Canvas.prototype.pngStream =
Canvas.prototype.createPNGStream = function(options){
return new PNGStream(this, options);
};
Canvas.prototype.createPNGStream = function (options) {
return new PNGStream(this, options)
}

Canvas.prototype.pdfStream =
Canvas.prototype.createPDFStream = function(options){
return new PDFStream(this, options);
};

Canvas.prototype.jpegStream =
Canvas.prototype.createJPEGStream = function(options){
return new JPEGStream(this, options);
};
Canvas.prototype.createJPEGStream = function (options) {
return new JPEGStream(this, options)
}

Canvas.prototype.toDataURL = function(a1, a2, a3){
// valid arg patterns (args -> [type, opts, fn]):
Expand All @@ -63,51 +63,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, opts).toString('base64')}`
}
};
}
Loading