paper.js/src/item/Raster.js

281 lines
7.6 KiB
JavaScript
Raw Normal View History

2011-02-21 12:43:56 -05:00
Raster = Item.extend({
beans: true,
// TODO: implement url / type, width, height
// TODO: have PlacedSymbol & Raster inherit from a shared class?
initialize: function(object) {
var width, height;
2011-02-21 12:43:56 -05:00
this.base();
if (object.getContext) {
this.canvas = object;
width = this.canvas.width;
height = this.canvas.height;
} else {
this._image = object;
// TODO: cross browser compatible?
width = object.naturalWidth;
height = object.naturalHeight;
2011-02-21 12:43:56 -05:00
}
this._size = new Size(width, height);
this._bounds = new Rectangle(-width / 2, -height / 2, width, height);
this.matrix = new Matrix();
2011-02-21 12:43:56 -05:00
},
/**
* The size of the raster in pixels.
*/
getSize: function() {
return this._size;
},
2011-02-21 12:43:56 -05:00
setSize: function() {
var size = Size.read(arguments);
var canvas = CanvasProvider.getCanvas(size);
var context = canvas.getContext('2d');
context.drawImage(this._canvas ? this._canvas : this._image,
0, 0, size.width, size.height);
// If we already had a canvas, return it to be reused.
if (this._canvas)
CanvasProvider.returnCanvas(this._canvas);
this._size = size;
this._context = null;
this._canvas = canvas;
},
2011-02-21 12:43:56 -05:00
/**
* The width of the raster in pixels.
*/
getWidth: function() {
return this._size.width;
2011-02-21 12:43:56 -05:00
},
/**
* The height of the raster in pixels.
*/
getHeight: function() {
return this._size.height;
2011-02-21 12:43:56 -05:00
},
/**
* Pixels per inch of the raster at it's current size.
*/
getPpi: function() {
var matrix = this.matrix;
var orig = new Point(0, 0).transform(matrix);
var u = new Point(1, 0).transform(matrix).subtract(orig);
var v = new Point(0, 1).transform(matrix).subtract(orig);
return new Size(
72 / u.length,
72 / v.length
);
},
getSubImage: function(/* rectangle */) {
var rectangle = Rectangle.read(arguments);
var canvas = CanvasProvider.getCanvas(rectangle.size);
var context = canvas.getContext('2d');
context.drawImage(this.canvas, rectangle.x, rectangle.y,
canvas.width, canvas.height, 0, 0, canvas.width, canvas.height);
return canvas;
},
getImage: function() {
return this._image || this.canvas;
},
// TODO: setImage
// TODO: drawImage(image, point)
drawImage: function(image, x, y) {
var point = center = Point.read(arguments, 1);
this.context.drawImage(image, x, y);
},
2011-02-21 12:43:56 -05:00
/**
* {@grouptitle Pixels}
*
* Gets the color of a pixel in the raster.
* @param x
* @param y
*/
getPixel: function() {
var point = Point.read(arguments);
var ctx = this.context;
var pixels = ctx.getImageData(point.x + 0.5, point.y + 0.5, 1, 1).data;
var channels = [];
for (var i = 0; i < 4; i++)
channels.push(pixels[i] / 255);
2011-02-21 12:43:56 -05:00
return Color.read(channels);
},
// TODO: setPixel(point, color)
setPixel: function(x, y, color) {
color = Color.read(arguments, 2);
var ctx = this.context;
var imageData = ctx.getImageData(x, y, 1, 1);
imageData.data[0] = color.red * 255;
imageData.data[1] = color.green * 255;
imageData.data[2] = color.blue * 255;
imageData.data[3] = color.alpha != -1 ? color.alpha * 255 : 255;
ctx.putImageData(imageData, x, y);
},
2011-02-21 12:43:56 -05:00
getContext: function() {
if (!this._context)
this._context = this.canvas.getContext('2d');
return this._context;
},
setContext: function(context) {
this._context = context;
},
getCanvas: function() {
if (!this._canvas) {
this._canvas = CanvasProvider.getCanvas(this.size);
this.ctx = this._canvas.getContext('2d');
this.ctx.drawImage(this._image, 0, 0);
}
return this._canvas;
},
setCanvas: function(canvas) {
if (this._canvas)
CanvasProvider.returnCanvas(this._canvas);
// TODO: should the width / height of the bounds be reset too?
this._size = new Size(canvas.width, canvas.height);
this._image = null;
this._ctx = null;
this._canvas = canvas;
},
2011-02-21 12:43:56 -05:00
transformContent: function(matrix, flags) {
// In order to set the right context transformation when drawing the
// raster, simply preconcatenate the internal matrix with the provided
// one.
this.matrix.preConcatenate(matrix);
// Now rotate the corner points of the image rectangle do find the
// extremas that define our raster's bounds, and update them straight
// away
var width = this._size.width;
var height = this._size.height;
var x = -width / 2;
var y = -height / 2;
var coords = [
2011-02-28 14:21:21 -05:00
x, y,
x + width, y,
x + width, y + height,
x, y + height
];
this.matrix.transform(coords, 0, coords, 0, 4);
// Loop through all x and y coordinates and update min and max values.
// Start with the first coordinate pair for both (coords.slice(0, 2)).
var min = coords.slice(0, 2), max = min.slice(0);
for (var i = 2; i < 8; i++) {
var c = coords[i], j = i % 2;
if (c < min[j])
min[j] = c;
else if (c > max[j])
max[j] = c;
}
this._bounds.set(min[0], min[1], max[0] - min[0], max[1] - min[1]);
2011-02-21 12:43:56 -05:00
},
getBounds: function() {
return this._bounds;
},
2011-02-25 06:46:45 -05:00
draw: function(ctx, param) {
2011-02-28 12:30:08 -05:00
if (this.blendMode != 'normal' && !param.ignoreBlendMode) {
2011-02-25 06:46:45 -05:00
BlendMode.process(ctx, this, param);
} else {
ctx.save();
// TODO: Documment what ignoreBlendMode is really doing, and why
// this is necessary?
if(param.ignoreBlendMode !== true)
this.matrix.applyToContext(ctx);
2011-02-25 06:46:45 -05:00
ctx.drawImage(this._canvas || this._image,
-this.size.width / 2, -this.size.height / 2);
ctx.restore();
param.ignoreBlendMode = false;
2011-02-25 06:46:45 -05:00
}
2011-02-21 12:43:56 -05:00
}
}, new function() {
function getAverageColor(pixels) {
var channels = [0, 0, 0];
var total = 0;
for (var i = 0, l = pixels.length / 4; i < l; i++) {
var offset = i * 4;
var alpha = pixels[offset + 3] / 255;
total += alpha;
channels[0] += pixels[offset] * alpha;
channels[1] += pixels[offset + 1] * alpha;
channels[2] += pixels[offset + 2] * alpha;
}
for (var i = 0; i < 3; i++)
channels[i] /= total * 255;
return total ? Color.read(channels) : null;
}
return {
/**
* {@grouptitle Average Color}
* Calculates the average color of the image within the given path,
* rectangle or point. This can be used for creating raster image
* effects.
*
* @param object
* @return the average color contained in the area covered by the
* specified path, rectangle or point.
*/
getAverageColor: function(object) {
var image;
if (object) {
var bounds, path;
if (object instanceof Path) {
// TODO: what if the path is smaller than 1 px?
// TODO: how about rounding of bounds.size?
// TODO: test with compound paths.
path = object;
bounds = object.bounds;
} else if (object.width) {
bounds = new Rectangle(object);
} else if (object.x) {
2011-02-28 14:24:15 -05:00
bounds = new Rectangle(object.x - 0.5, object.y - 0.5,
1, 1);
}
var canvas = CanvasProvider.getCanvas(bounds.size);
var ctx = canvas.getContext('2d');
var delta = bounds.topLeft.multiply(-1);
ctx.translate(delta.x, delta.y);
if (path) {
var style = object.style;
path.draw(ctx);
ctx.clip();
path.style = style;
}
var matrix = this.matrix.clone();
var transMatrix = Matrix.getTranslateInstance(delta);
matrix.preConcatenate(transMatrix);
matrix.applyToContext(ctx);
ctx.drawImage(this._canvas || this._image,
-this.size.width / 2, -this.size.height / 2);
image = canvas;
} else {
image = this.image;
}
2011-02-23 20:39:33 -05:00
var size = new Size(32);
var sampleCanvas = CanvasProvider.getCanvas(size);
var ctx = sampleCanvas.getContext('2d');
2011-02-23 20:39:33 -05:00
ctx.drawImage(image, 0, 0, size.width, size.height);
2011-02-28 14:24:15 -05:00
var pixels = ctx.getImageData(0.5, 0.5,
size.width, size.height).data;
var color = getAverageColor(pixels);
2011-02-23 19:51:49 -05:00
CanvasProvider.returnCanvas(sampleCanvas);
if (image instanceof HTMLCanvasElement)
CanvasProvider.returnCanvas(image);
return color;
}
}
2011-02-21 12:43:56 -05:00
});