Skip to content

Instantly share code, notes, and snippets.

@senocular
Created November 12, 2013 16:56
Show Gist options
  • Save senocular/7434502 to your computer and use it in GitHub Desktop.
Save senocular/7434502 to your computer and use it in GitHub Desktop.
Subset of AS BitmapData API in JS
function BitmapData(canvas){
this.canvas = canvas;
this.width = this.canvas.width;
this.height = this.canvas.height;
this.context = this.canvas.getContext("2d");
this.imageData = null;
// true if the canvas has been drawn into
// and the image data has not been updated
this.dataInvalid = false;
// true if the image data has changed
// and the image data has not been updated
this.canvasInvalid = false;
this.updateImageData();
}
BitmapData.fromCanvas = function(canvas){
return new BitmapData(canvas);
};
BitmapData.fromImage = function(image){
var canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
var context = canvas.getContext("2d");
context.drawImage(image, 0, 0);
return new BitmapData(canvas);
};
BitmapData.fromSize = function(width, height){
var canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
return new BitmapData(canvas);
};
Object.defineProperty(BitmapData.prototype, "rect", {
get: function(){
return {x:0, y:0, width:this.width, height:this.height};
}
});
BitmapData.prototype.draw = function(source, matrix){
if (source instanceof BitmapData){
// make sure source canvas has all
// data changes drawn into it
if (source.canvasInvalid){
source.updateCanvas();
}
source = source.canvas;
}
// make sure this canvas has all
// data changes drawn into it
if (this.canvasInvalid){
this.updateCanvas();
}
if (matrix !== undefined){
this.context.save();
this.context.setTransform(matrix.a, matrix.b, matrix.c, matrix.d, matrix.tx, matrix.ty);
this.context.drawImage(source, 0, 0);
this.context.restore();
}else{
this.context.drawImage(source, 0, 0);
}
if (!this.dataInvalid){
this.dataInvalid = true;
}
};
BitmapData.prototype.updateImageData = function(){
if (this.context){
this.imageData = this.context.getImageData(0, 0, this.width, this.height);
this.dataInvalid = false;
}
};
BitmapData.prototype.updateCanvas = function(){
if (this.context){
this.context.putImageData(this.imageData, 0, 0);
this.canvasInvalid = false;
}
};
BitmapData.prototype.lock = function(){
// noop
// locking inherent in the api
// with update methods needed
// to apply changes
};
BitmapData.prototype.unlock = function(){
// noop
};
BitmapData.prototype.getPixel = function(x, y){
// make sure data has all updates
// from canvas
if (this.dataInvalid){
this.updateImageData();
}
var i = 4 * (x + y * this.imageData.width);
var data = this.imageData.data;
var r = data[i];
var g = data[i + 1];
var b = data[i + 2];
var a = data[i + 3];
return (a << 24) | (r << 16) | (g << 8) | b;
};
BitmapData.prototype.getPixels = function(rect){
if (!rect){
throw new ReferenceError("Parameter rect in getPixels must be non-null.");
}
// make sure data has all updates
// from canvas
if (this.dataInvalid){
this.updateImageData();
}
var bytes = new ByteArray();
var data = this.imageData.data;
var xMax = this.imageData.width - 1;
var yMax = this.imageData.height - 1;
var rectRight = rect.x + rect.width;
var rectBottom = rect.y + rect.height;
for (var y=rect.y; y<rectBottom; y++){
for (var x=rect.x; x<rectRight; x++){
if (x < 0 || x > xMax || y < 0 || y > yMax){
// is outside of image bounds, fill black
bytes.writeUnsignedInt(0);
}else{
var i = 4 * (x + y * this.imageData.width);
// convert rgba to argb
var r = data[i ];
var g = data[i + 1];
var b = data[i + 2];
var a = data[i + 3];
var pixel = (a << 24) | (r << 16) | (g << 8) | b;
bytes.writeUnsignedInt(pixel);
}
}
}
bytes.position = 0;
return bytes;
};
BitmapData.prototype.setPixels = function(rect, bytes){
if (!rect){
throw new ReferenceError("Parameter rect in getPixels must be non-null.");
}
if (!bytes){
throw new ReferenceError("Parameter bytes in getPixels must be non-null.");
}
// make sure data has all updates
// from canvas
if (this.dataInvalid){
this.updateImageData();
}
var data = this.imageData.data;
var bytesPos = bytes.position;
bytes.position = 0;
var xMax = this.imageData.width - 1;
var yMax = this.imageData.height - 1;
var rectRight = rect.x + rect.width;
var rectBottom = rect.y + rect.height;
for (var y=rect.y; y<rectBottom; y++){
if (y < 0){
// skipping rows until visible
var skipRows = -y;
bytes.position += skipRows * 4 * rect.width; // 4 bytes per pixel uint
y = 0;
}
if (y > yMax){
// no more setting to be done
break;
}
for (var x=rect.x; x<rectRight; x++){
var skipCols;
if (x < 0){
// skip cols up until when visible (0)
skipCols = -x;
bytes.position += 4 * skipCols;
x = 0;
}
if (x > xMax){
// skipping rest of row, moving onto next
skipCols = rectRight - xMax;
bytes.position += 4 * skipCols;
break;
}
var i = 4 * (x + y * this.imageData.width);
var pixel = bytes.readUnsignedInt();
// convert argb to rgba
var a = pixel >>> 24;
var r = (pixel >>> 16) & 0xFF;
var g = (pixel >>> 8) & 0xFF;
var b = pixel & 0xFF;
data[i ] = r;
data[i + 1] = g;
data[i + 2] = b;
data[i + 3] = a;
}
}
bytes.position = bytesPos;
if (!this.canvasInvalid){
this.canvasInvalid = true;
}
};
BitmapData.prototype.setPixel = function(x, y, pixel){
// make sure data has all updates
// from canvas
if (this.dataInvalid){
this.updateImageData();
}
var i = 4 * (x + y * this.imageData.width);
var a = (pixel >>> 24) & 0xFF;
var r = (pixel >>> 16) & 0xFF;
var g = (pixel >>> 8) & 0xFF;
var b = pixel & 0xFF;
var data = this.imageData.data;
data[i ] = r;
data[i + 1] = g;
data[i + 2] = b;
data[i + 3] = a;
if (!this.canvasInvalid){
this.canvasInvalid = true;
}
};
function Matrix(a,b,c,d,tx,ty){
this.a = a;
this.b = b;
this.c = c;
this.d = d;
this.tx = tx;
this.ty = ty;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment