Skip to content

Commit

Permalink
Merge pull request #194 from Rayman/remove-canvas-dependency
Browse files Browse the repository at this point in the history
Remove canvas dependency
  • Loading branch information
rctoris committed Sep 11, 2015
2 parents cafd9b9 + 1cb6e01 commit 1ca42c2
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 121 deletions.
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;

0 comments on commit 1ca42c2

Please sign in to comment.