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

Remove canvas dependency #194

Merged
merged 6 commits into from
Sep 11, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 0 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@ before_install:
- npm install -g npm
- npm install -g grunt-cli karma-cli

# install the dev packages we need
- ./misc/install.sh

# Set up Xfvb for Firefox headless testing
- "export DISPLAY=:99.0"
- "sh -e /etc/init.d/xvfb start"
Expand Down
2 changes: 0 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ roslibjs Build Setup
$ cd /path/to/roslibjs/
$ [sudo] npm install
```
3.5. If that doesn't work, install the [Cairo](http://cairographics.org/) graphics library manually for your os and retry
- [System specific instructions](https://github.com/Automattic/node-canvas/wiki/_pages)

### Build with Grunt

Expand Down
10 changes: 0 additions & 10 deletions misc/check.sh

This file was deleted.

18 changes: 0 additions & 18 deletions misc/install.sh

This file was deleted.

29 changes: 11 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@
"version": "0.18.0-SNAPSHOT",
"license": "BSD",
"main": "./src/RosLibNode.js",
"browser": "./src/RosLib.js",
"browser": {
"./src/RosLibNode.js": "./src/RosLib.js",
"eventemitter2": "./src/util/shim/EventEmitter2.js",
"canvas": "./src/util/shim/canvas.js",
"ws": "./src/util/shim/WebSocket.js",
"xmldom": "./src/util/shim/xmldom.js",
"./src/util/decompressPng.js": "./src/util/shim/decompressPng.js"
},
"devDependencies": {
"grunt": "~0.4.1",
"grunt-browserify": "^3.0.1",
Expand All @@ -16,33 +23,20 @@
"grunt-jsdoc": "~0.6.0",
"grunt-karma": "^0.12.0",
"grunt-mocha-test": "^0.11.0",
"karma": "^0.13.9",
"karma-chai": "^0.1.0",
"karma-firefox-launcher": "~0.1.3",
"karma-mocha": "^0.1.9",
"load-grunt-tasks": "^0.6.0",
"time-grunt": "^1.0.0"
},
"dependencies": {
"aliasify-patch": "^1.4.1",
"canvas": "~1.2.8",
"eventemitter2": "~0.4.13",
"object-assign": "^2.0.0",
"pngparse": "^2.0.1",
"ws": "^0.4.32",
"xmldom": "^0.1.19"
},
"browserify": {
"transform": [
"aliasify-patch"
]
},
"aliasify": {
"aliases": {
"canvas": "./src/util/shim/canvas.js",
"eventemitter2": "./src/util/shim/EventEmitter2.js",
"ws": "./src/util/shim/WebSocket.js",
"xmldom": "./src/util/shim/xmldom.js"
}
},
"directories": {
"example": "examples",
"test": "test"
Expand All @@ -54,8 +48,7 @@
"test": "grunt test",
"test-examples": "grunt mochaTest:examples && karma start test/examples/karma.conf.js",
"test-tcp": "grunt mochaTest:tcp",
"publish": "grunt build",
"preinstall": "sh misc/check.sh"
"publish": "grunt build"
},
"repository": {
"type": "git",
Expand Down
99 changes: 29 additions & 70 deletions src/core/SocketAdapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,59 +8,13 @@
*/
'use strict';

var Canvas = require('canvas');
var Image = Canvas.Image || global.Image;
var decompressPng = require('../util/decompressPng');
var WebSocket = require('ws');
var BSON = null;
if(typeof bson !== 'undefined'){
BSON = bson().BSON;
}

/**
* If a message was compressed as a PNG image (a compression hack since
* gzipping over WebSockets * is not supported yet), this function places the
* "image" in a canvas element then decodes the * "image" as a Base64 string.
*
* @private
* @param data - object containing the PNG data.
* @param callback - function with params:
* * data - the uncompressed data
*/
function decompressPng(data, callback) {
// Uncompresses the data before sending it through (use image/canvas to do so).
var image = new Image();
// When the image loads, extracts the raw data (JSON message).
image.onload = function() {
// Creates a local canvas to draw on.
var canvas = new Canvas();
var context = canvas.getContext('2d');

// Sets width and height.
canvas.width = image.width;
canvas.height = image.height;

// Prevents anti-aliasing and loosing data
context.imageSmoothingEnabled = false;
context.webkitImageSmoothingEnabled = false;
context.mozImageSmoothingEnabled = false;

// Puts the data into the image.
context.drawImage(image, 0, 0);
// Grabs the raw, uncompressed data.
var imageData = context.getImageData(0, 0, image.width, image.height).data;

// Constructs the JSON.
var jsonData = '';
for (var i = 0; i < imageData.length; i += 4) {
// RGB
jsonData += String.fromCharCode(imageData[i], imageData[i + 1], imageData[i + 2]);
}
callback(JSON.parse(jsonData));
};
// Sends the image data to load.
image.src = 'data:image/png;base64,' + data.data;
}

/**
* Events listeners for a WebSocket or TCP socket to a JavaScript
* ROS Client. Sets up Messages for a given topic to trigger an
Expand All @@ -78,6 +32,27 @@ function SocketAdapter(client) {
}
}

function handlePng(message, callback) {
if (message.op === 'png') {
decompressPng(message.data, callback);
} else {
callback(message);
}
}

function decodeBSON(data, callback) {
if (!BSON) {
throw 'Cannot process BSON encoded message without BSON header.';
}
var reader = new FileReader();
reader.onload = function() {
var uint8Array = new Uint8Array(this.result);
var msg = BSON.deserialize(uint8Array);
callback(msg);
};
reader.readAsArrayBuffer(data);
}

return {
/**
* Emits a 'connection' event on WebSocket connection.
Expand Down Expand Up @@ -118,30 +93,14 @@ function SocketAdapter(client) {
* @param message - the raw JSON message from rosbridge.
* @memberof SocketAdapter
*/
onmessage: function onMessage(message) {
if(typeof Blob !== 'undefined' && message.data instanceof Blob) {
if(!BSON){
throw 'Cannot process BSON encoded message without BSON header.';
}
var reader = new FileReader();
reader.onload = function() {
var uint8Array = new Uint8Array(this.result);
var msg = BSON.deserialize(uint8Array);

if (msg.op === 'png') {
decompressPng(msg, handleMessage);
} else {
handleMessage(msg);
}
};
reader.readAsArrayBuffer(message.data);
onmessage: function onMessage(data) {
if (typeof Blob !== 'undefined' && data.data instanceof Blob) {
decodeBSON(data.data, function (message) {
handlePng(message, handleMessage);
});
} else {
var data = JSON.parse(typeof message === 'string' ? message : message.data);
if (data.op === 'png') {
decompressPng(data, handleMessage);
} else {
handleMessage(data);
}
var message = JSON.parse(typeof data === 'string' ? data : data.data);
handlePng(message, handleMessage);
}
}
};
Expand Down
33 changes: 33 additions & 0 deletions src/util/decompressPng.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* @fileOverview
* @author Ramon Wijnands - rayman747@hotmail.com
*/

'use strict';

var pngparse = require('pngparse');

/**
* If a message was compressed as a PNG image (a compression hack since
* gzipping over WebSockets * is not supported yet), this function decodes
* the "image" as a Base64 string.
*
* @private
* @param data - object containing the PNG data.
* @param callback - function with params:
* * data - the uncompressed data
*/
function decompressPng(data, callback) {
var buffer = new Buffer(data, 'base64');

pngparse.parse(buffer, function(err, data) {
if(err) {
console.warn('Cannot process PNG encoded message ');
} else {
var jsonData = data.data.toString();
callback(JSON.parse(jsonData));
}
});
}

module.exports = decompressPng;
56 changes: 56 additions & 0 deletions src/util/shim/decompressPng.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* @fileOverview
* @author Graeme Yeates - github.com/megawac
*/

'use strict';

var Canvas = require('canvas');
var Image = Canvas.Image || global.Image;

/**
* If a message was compressed as a PNG image (a compression hack since
* gzipping over WebSockets * is not supported yet), this function places the
* "image" in a canvas element then decodes the * "image" as a Base64 string.
*
* @private
* @param data - object containing the PNG data.
* @param callback - function with params:
* * data - the uncompressed data
*/
function decompressPng(data, callback) {
// Uncompresses the data before sending it through (use image/canvas to do so).
var image = new Image();
// When the image loads, extracts the raw data (JSON message).
image.onload = function() {
// Creates a local canvas to draw on.
var canvas = new Canvas();
var context = canvas.getContext('2d');

// Sets width and height.
canvas.width = image.width;
canvas.height = image.height;

// Prevents anti-aliasing and loosing data
context.imageSmoothingEnabled = false;
context.webkitImageSmoothingEnabled = false;
context.mozImageSmoothingEnabled = false;

// Puts the data into the image.
context.drawImage(image, 0, 0);
// Grabs the raw, uncompressed data.
var imageData = context.getImageData(0, 0, image.width, image.height).data;

// Constructs the JSON.
var jsonData = '';
for (var i = 0; i < imageData.length; i += 4) {
// RGB
jsonData += String.fromCharCode(imageData[i], imageData[i + 1], imageData[i + 2]);
}
callback(JSON.parse(jsonData));
};
// Sends the image data to load.
image.src = 'data:image/png;base64,' + data;
}

module.exports = decompressPng;