2011-03-06 19:50:44 -05:00
|
|
|
/*
|
|
|
|
* Paper.js
|
|
|
|
*
|
|
|
|
* This file is part of Paper.js, a JavaScript Vector Graphics Library,
|
|
|
|
* based on Scriptographer.org and designed to be largely API compatible.
|
2011-03-07 20:41:50 -05:00
|
|
|
* http://paperjs.org/
|
2011-03-06 19:50:44 -05:00
|
|
|
* http://scriptographer.org/
|
|
|
|
*
|
2011-03-07 20:41:50 -05:00
|
|
|
* Distributed under the MIT license. See LICENSE file for details.
|
|
|
|
*
|
2011-03-06 19:50:44 -05:00
|
|
|
* Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
|
|
|
|
* http://lehni.org/ & http://jonathanpuckey.com/
|
|
|
|
*
|
2011-03-07 20:41:50 -05:00
|
|
|
* All rights reserved.
|
2011-03-06 19:50:44 -05:00
|
|
|
*/
|
|
|
|
|
2011-03-04 08:34:31 -05:00
|
|
|
var Raster = this.Raster = Item.extend({
|
2011-02-21 12:43:56 -05:00
|
|
|
beans: true,
|
|
|
|
|
|
|
|
// TODO: implement url / type, width, height
|
|
|
|
// TODO: have PlacedSymbol & Raster inherit from a shared class?
|
2011-02-24 07:52:27 -05:00
|
|
|
initialize: function(object) {
|
2011-02-21 12:43:56 -05:00
|
|
|
this.base();
|
2011-02-24 08:15:30 -05:00
|
|
|
if (object.getContext) {
|
2011-03-04 20:26:12 -05:00
|
|
|
this.setCanvas(object);
|
2011-02-24 08:15:30 -05:00
|
|
|
} else {
|
2011-03-05 09:17:32 -05:00
|
|
|
// If it's a string, get the element with this id first.
|
2011-04-28 08:23:17 -04:00
|
|
|
if (typeof object === 'string')
|
2011-03-05 09:17:32 -05:00
|
|
|
object = document.getElementById(object);
|
2011-03-04 20:26:12 -05:00
|
|
|
this.setImage(object);
|
2011-02-21 12:43:56 -05:00
|
|
|
}
|
2011-02-24 07:52:27 -05:00
|
|
|
this.matrix = new Matrix();
|
2011-02-21 12:43:56 -05:00
|
|
|
},
|
2011-02-23 19:28:11 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The size of the raster in pixels.
|
|
|
|
*/
|
|
|
|
getSize: function() {
|
|
|
|
return this._size;
|
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-23 19:28:11 -05:00
|
|
|
setSize: function() {
|
2011-03-08 07:55:34 -05:00
|
|
|
var size = Size.read(arguments),
|
|
|
|
// Get reference to image before changing canvas
|
|
|
|
image = this.getImage();
|
2011-03-04 21:40:38 -05:00
|
|
|
// Setting canvas internally sets _size
|
2011-03-04 20:26:12 -05:00
|
|
|
this.setCanvas(CanvasProvider.getCanvas(size));
|
2011-03-04 21:40:38 -05:00
|
|
|
// Draw image back onto new canvas
|
2011-03-06 09:08:41 -05:00
|
|
|
this.getContext().drawImage(image, 0, 0, size.width, size.height);
|
2011-02-23 19:28:11 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-21 12:43:56 -05:00
|
|
|
/**
|
|
|
|
* The width of the raster in pixels.
|
|
|
|
*/
|
|
|
|
getWidth: function() {
|
2011-02-23 19:28:11 -05:00
|
|
|
return this._size.width;
|
2011-02-21 12:43:56 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-21 12:43:56 -05:00
|
|
|
/**
|
|
|
|
* The height of the raster in pixels.
|
|
|
|
*/
|
|
|
|
getHeight: function() {
|
2011-02-23 19:28:11 -05:00
|
|
|
return this._size.height;
|
2011-02-21 12:43:56 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-24 07:52:27 -05:00
|
|
|
/**
|
|
|
|
* Pixels per inch of the raster at it's current size.
|
|
|
|
*/
|
|
|
|
getPpi: function() {
|
2011-05-02 06:23:42 -04:00
|
|
|
var matrix = this.matrix,
|
2011-03-08 07:55:34 -05:00
|
|
|
orig = new Point(0, 0).transform(matrix),
|
|
|
|
u = new Point(1, 0).transform(matrix).subtract(orig),
|
|
|
|
v = new Point(0, 1).transform(matrix).subtract(orig);
|
2011-02-24 07:52:27 -05:00
|
|
|
return new Size(
|
2011-03-04 21:40:38 -05:00
|
|
|
72 / u.getLength(),
|
|
|
|
72 / v.getLength()
|
2011-02-24 07:52:27 -05:00
|
|
|
);
|
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-03-04 21:40:38 -05:00
|
|
|
getContext: function() {
|
|
|
|
if (!this._context) {
|
|
|
|
this._context = this.getCanvas().getContext('2d');
|
|
|
|
}
|
|
|
|
return this._context;
|
|
|
|
},
|
|
|
|
|
|
|
|
setContext: function(context) {
|
|
|
|
this._context = context;
|
|
|
|
},
|
|
|
|
|
2011-03-04 20:26:12 -05:00
|
|
|
getCanvas: function() {
|
|
|
|
if (!this._canvas) {
|
|
|
|
this._canvas = CanvasProvider.getCanvas(this._size);
|
|
|
|
if (this._image)
|
|
|
|
this.getContext().drawImage(this._image, 0, 0);
|
|
|
|
}
|
|
|
|
return this._canvas;
|
|
|
|
},
|
|
|
|
|
|
|
|
setCanvas: function(canvas) {
|
|
|
|
if (this._canvas)
|
|
|
|
CanvasProvider.returnCanvas(this._canvas);
|
|
|
|
this._canvas = canvas;
|
|
|
|
this._size = new Size(canvas.width, canvas.height);
|
|
|
|
this._image = null;
|
|
|
|
this._context = null;
|
|
|
|
this._bounds = null;
|
|
|
|
},
|
|
|
|
|
|
|
|
getImage: function() {
|
|
|
|
return this._image || this.getCanvas();
|
|
|
|
},
|
|
|
|
|
|
|
|
setImage: function(image) {
|
|
|
|
if (this._canvas)
|
|
|
|
CanvasProvider.returnCanvas(this._canvas);
|
|
|
|
this._image = image;
|
|
|
|
// TODO: cross browser compatible?
|
|
|
|
this._size = new Size(image.naturalWidth, image.naturalHeight);
|
|
|
|
this._canvas = null;
|
|
|
|
this._context = null;
|
|
|
|
this._bounds = null;
|
|
|
|
},
|
|
|
|
|
2011-03-13 13:31:00 -04:00
|
|
|
getSubImage: function(rect) {
|
|
|
|
rect = Rectangle.read(arguments);
|
2011-04-26 10:39:16 -04:00
|
|
|
var canvas = CanvasProvider.getCanvas(rect.getSize());
|
|
|
|
canvas.getContext('2d').drawImage(this.getCanvas(), rect.x, rect.y,
|
2011-03-05 09:17:32 -05:00
|
|
|
canvas.width, canvas.height, 0, 0, canvas.width, canvas.height);
|
2011-02-24 07:52:27 -05:00
|
|
|
return canvas;
|
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-03-04 20:26:12 -05:00
|
|
|
drawImage: function(image, point) {
|
2011-03-13 13:31:00 -04:00
|
|
|
point = Point.read(arguments, 1);
|
2011-03-04 20:26:12 -05:00
|
|
|
this.getContext().drawImage(image, point.x, point.y);
|
2011-02-24 07:52:27 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-23 19:28:11 -05:00
|
|
|
/**
|
|
|
|
* Gets the color of a pixel in the raster.
|
|
|
|
* @param x
|
|
|
|
* @param y
|
|
|
|
*/
|
2011-03-13 13:31:00 -04:00
|
|
|
getPixel: function(point) {
|
|
|
|
point = Point.read(arguments);
|
2011-04-26 10:39:16 -04:00
|
|
|
var pixels = this.getContext().getImageData(point.x, point.y, 1, 1).data,
|
2011-03-09 07:38:58 -05:00
|
|
|
channels = new Array(4);
|
2011-02-23 19:28:11 -05:00
|
|
|
for (var i = 0; i < 4; i++)
|
2011-03-09 07:38:58 -05:00
|
|
|
channels[i] = pixels[i] / 255;
|
|
|
|
return RGBColor.read(channels);
|
2011-02-21 12:43:56 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-03-11 08:23:30 -05:00
|
|
|
setPixel: function(point, color) {
|
|
|
|
var hasPoint = arguments.length == 2;
|
|
|
|
point = Point.read(arguments, 0, hasPoint ? 1 : 2);
|
|
|
|
color = Color.read(arguments, hasPoint ? 1 : 2);
|
2011-03-04 20:26:12 -05:00
|
|
|
var ctx = this.getContext(),
|
2011-03-11 08:23:30 -05:00
|
|
|
imageData = ctx.createImageData(1, 1),
|
2011-03-04 19:16:26 -05:00
|
|
|
alpha = color.getAlpha();
|
|
|
|
imageData.data[0] = color.getRed() * 255;
|
|
|
|
imageData.data[1] = color.getGreen() * 255;
|
|
|
|
imageData.data[2] = color.getBlue() * 255;
|
|
|
|
imageData.data[3] = alpha != null ? alpha * 255 : 255;
|
2011-03-11 08:23:30 -05:00
|
|
|
ctx.putImageData(imageData, point.x, point.y);
|
2011-02-23 19:28:11 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-03-09 13:17:12 -05:00
|
|
|
createData: function(size) {
|
|
|
|
size = Size.read(arguments);
|
|
|
|
return this.getContext().createImageData(size.width, size.height);
|
|
|
|
},
|
|
|
|
|
2011-03-13 13:31:00 -04:00
|
|
|
getData: function(rect) {
|
|
|
|
rect = Rectangle.read(arguments);
|
2011-04-26 10:39:16 -04:00
|
|
|
if (rect.isEmpty())
|
|
|
|
rect = new Rectangle(this.getSize());
|
|
|
|
return this.getContext().getImageData(rect.x, rect.y,
|
|
|
|
rect.width, rect.height);
|
2011-03-09 13:17:12 -05:00
|
|
|
},
|
|
|
|
|
2011-03-13 13:31:00 -04:00
|
|
|
setData: function(data, point) {
|
|
|
|
point = Point.read(arguments, 1);
|
2011-04-26 10:39:16 -04:00
|
|
|
this.getContext().putImageData(data, point.x, point.y);
|
2011-03-09 13:17:12 -05:00
|
|
|
},
|
|
|
|
|
2011-03-03 07:47:55 -05:00
|
|
|
_transform: function(matrix, flags) {
|
2011-02-28 14:17:59 -05:00
|
|
|
// 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);
|
2011-03-04 20:26:12 -05:00
|
|
|
this._bounds = null;
|
2011-02-21 12:43:56 -05:00
|
|
|
},
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-21 12:43:56 -05:00
|
|
|
getBounds: function() {
|
2011-03-04 20:26:12 -05:00
|
|
|
if (!this._bounds) {
|
2011-03-05 16:05:07 -05:00
|
|
|
this._bounds = this.matrix.transformBounds(
|
|
|
|
new Rectangle(this._size).setCenter(0, 0));
|
2011-03-04 20:26:12 -05:00
|
|
|
}
|
2011-02-21 12:43:56 -05:00
|
|
|
return this._bounds;
|
2011-03-03 07:19:43 -05:00
|
|
|
},
|
2011-04-28 06:56:08 -04:00
|
|
|
|
|
|
|
getStrokeBounds: function() {
|
|
|
|
return this.getBounds();
|
|
|
|
},
|
2011-03-03 07:19:43 -05:00
|
|
|
|
|
|
|
draw: function(ctx, param) {
|
2011-04-21 09:48:21 -04:00
|
|
|
if (param.selection) {
|
2011-04-18 12:46:39 -04:00
|
|
|
var bounds = new Rectangle(this._size).setCenter(0, 0);
|
2011-04-21 09:48:21 -04:00
|
|
|
Item.drawSelectedBounds(bounds, ctx, this.matrix);
|
|
|
|
} else {
|
|
|
|
ctx.save();
|
|
|
|
this.matrix.applyToContext(ctx);
|
|
|
|
ctx.drawImage(this._canvas || this._image,
|
|
|
|
-this._size.width / 2, -this._size.height / 2);
|
|
|
|
ctx.restore();
|
2011-04-18 12:46:39 -04:00
|
|
|
}
|
2011-02-21 12:43:56 -05:00
|
|
|
}
|
2011-02-23 19:28:11 -05:00
|
|
|
}, new function() {
|
|
|
|
function getAverageColor(pixels) {
|
2011-03-08 07:55:34 -05:00
|
|
|
var channels = [0, 0, 0],
|
|
|
|
total = 0;
|
2011-03-02 02:52:39 -05:00
|
|
|
for (var i = 0, l = pixels.length; i < l; i += 4) {
|
2011-03-02 02:53:38 -05:00
|
|
|
var alpha = pixels[i + 3];
|
2011-02-23 19:28:11 -05:00
|
|
|
total += alpha;
|
2011-03-02 02:53:38 -05:00
|
|
|
alpha /= 255;
|
2011-03-02 02:52:39 -05:00
|
|
|
channels[0] += pixels[i] * alpha;
|
|
|
|
channels[1] += pixels[i + 1] * alpha;
|
|
|
|
channels[2] += pixels[i + 2] * alpha;
|
2011-02-23 19:28:11 -05:00
|
|
|
}
|
|
|
|
for (var i = 0; i < 3; i++)
|
2011-03-02 02:53:38 -05:00
|
|
|
channels[i] /= total;
|
2011-02-23 19:28:11 -05:00
|
|
|
return total ? Color.read(channels) : null;
|
|
|
|
}
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-02-23 19:28:11 -05:00
|
|
|
return {
|
|
|
|
/**
|
|
|
|
* 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;
|
2011-05-15 08:13:47 -04:00
|
|
|
if (object instanceof PathItem) {
|
2011-02-23 19:28:11 -05:00
|
|
|
// TODO: what if the path is smaller than 1 px?
|
|
|
|
// TODO: how about rounding of bounds.size?
|
|
|
|
// TODO: test with compound paths.
|
|
|
|
path = object;
|
2011-03-04 20:26:12 -05:00
|
|
|
bounds = object.getBounds();
|
2011-02-23 19:28:11 -05:00
|
|
|
} 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);
|
2011-02-23 19:28:11 -05:00
|
|
|
}
|
2011-03-03 17:45:17 -05:00
|
|
|
|
2011-03-08 07:55:34 -05:00
|
|
|
var canvas = CanvasProvider.getCanvas(bounds.getSize()),
|
|
|
|
ctx = canvas.getContext('2d'),
|
|
|
|
delta = bounds.getTopLeft().multiply(-1);
|
2011-02-23 19:28:11 -05:00
|
|
|
ctx.translate(delta.x, delta.y);
|
|
|
|
if (path) {
|
2011-03-04 19:23:16 -05:00
|
|
|
var style = object.getStyle();
|
2011-05-15 08:13:47 -04:00
|
|
|
path.draw(ctx, {});
|
2011-02-23 19:28:11 -05:00
|
|
|
ctx.clip();
|
2011-03-04 19:23:16 -05:00
|
|
|
path.setStyle(style);
|
2011-02-23 19:28:11 -05:00
|
|
|
}
|
2011-05-15 08:13:47 -04:00
|
|
|
this.matrix.applyToContext(ctx);
|
2011-02-24 07:52:27 -05:00
|
|
|
ctx.drawImage(this._canvas || this._image,
|
2011-03-04 20:26:12 -05:00
|
|
|
-this._size.width / 2, -this._size.height / 2);
|
2011-02-23 19:28:11 -05:00
|
|
|
image = canvas;
|
|
|
|
} else {
|
|
|
|
image = this.image;
|
|
|
|
}
|
2011-03-08 07:55:34 -05:00
|
|
|
var size = new Size(32),
|
|
|
|
sampleCanvas = CanvasProvider.getCanvas(size),
|
|
|
|
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,
|
2011-03-08 07:55:34 -05:00
|
|
|
size.width, size.height).data,
|
|
|
|
color = getAverageColor(pixels);
|
2011-02-23 19:51:49 -05:00
|
|
|
CanvasProvider.returnCanvas(sampleCanvas);
|
2011-02-24 07:52:27 -05:00
|
|
|
if (image instanceof HTMLCanvasElement)
|
2011-02-23 19:28:11 -05:00
|
|
|
CanvasProvider.returnCanvas(image);
|
|
|
|
return color;
|
|
|
|
}
|
2011-04-18 12:46:39 -04:00
|
|
|
};
|
2011-03-03 11:32:55 -05:00
|
|
|
});
|