mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-09 06:12:34 -05:00
727 lines
25 KiB
JavaScript
727 lines
25 KiB
JavaScript
/*
|
|
* Paper.js - The Swiss Army Knife of Vector Graphics Scripting.
|
|
* http://paperjs.org/
|
|
*
|
|
* Copyright (c) 2011 - 2016, Juerg Lehni & Jonathan Puckey
|
|
* http://scratchdisk.com/ & http://jonathanpuckey.com/
|
|
*
|
|
* Distributed under the MIT license. See LICENSE file for details.
|
|
*
|
|
* All rights reserved.
|
|
*/
|
|
|
|
/**
|
|
* @name Raster
|
|
*
|
|
* @class The Raster item represents an image in a Paper.js project.
|
|
*
|
|
* @extends Item
|
|
*/
|
|
var Raster = Item.extend(/** @lends Raster# */{
|
|
_class: 'Raster',
|
|
_applyMatrix: false,
|
|
_canApplyMatrix: false,
|
|
// Raster doesn't make the distinction between the different bounds,
|
|
// so use the same name for all of them
|
|
_boundsOptions: { stroke: false, handle: false },
|
|
_boundsSelected: true,
|
|
_serializeFields: {
|
|
crossOrigin: null, // NOTE: Needs to be set before source to work!
|
|
source: null
|
|
},
|
|
|
|
// TODO: Implement type, width, height.
|
|
// TODO: Have SymbolItem & Raster inherit from a shared class?
|
|
/**
|
|
* Creates a new raster item from the passed argument, and places it in the
|
|
* active layer. `object` can either be a DOM Image, a Canvas, or a string
|
|
* describing the URL to load the image from, or the ID of a DOM element to
|
|
* get the image from (either a DOM Image or a Canvas).
|
|
*
|
|
* @param {HTMLImageElement|HTMLCanvasElement|String} [source] the source of
|
|
* the raster
|
|
* @param {Point} [position] the center position at which the raster item is
|
|
* placed
|
|
*
|
|
* @example {@paperscript height=300} // Creating a raster using a url
|
|
* var url = 'http://assets.paperjs.org/images/marilyn.jpg';
|
|
* var raster = new Raster(url);
|
|
*
|
|
* // If you create a Raster using a url, you can use the onLoad
|
|
* // handler to do something once it is loaded:
|
|
* raster.onLoad = function() {
|
|
* console.log('The image has loaded.');
|
|
* };
|
|
*
|
|
* @example // Creating a raster using the id of a DOM Image:
|
|
*
|
|
* // Create a raster using the id of the image:
|
|
* var raster = new Raster('art');
|
|
*
|
|
* @example // Creating a raster using a DOM Image:
|
|
*
|
|
* // Find the element using its id:
|
|
* var imageElement = document.getElementById('art');
|
|
*
|
|
* // Create the raster:
|
|
* var raster = new Raster(imageElement);
|
|
*
|
|
* @example {@paperscript height=300}
|
|
* var raster = new Raster({
|
|
* source: 'http://assets.paperjs.org/images/marilyn.jpg',
|
|
* position: view.center
|
|
* });
|
|
*
|
|
* raster.scale(0.5);
|
|
* raster.rotate(10);
|
|
*/
|
|
initialize: function Raster(object, position) {
|
|
// Support two forms of item initialization: Passing one object literal
|
|
// describing all the different properties to be set, or an image
|
|
// (object) and a point where it should be placed (point).
|
|
// If _initialize can set properties through object literal, we're done.
|
|
// Otherwise we need to check the type of object:
|
|
if (!this._initialize(object,
|
|
position !== undefined && Point.read(arguments, 1))) {
|
|
if (typeof object === 'string') {
|
|
// Both data-urls and normal urls are supported here!
|
|
this.setSource(object);
|
|
} else {
|
|
// #setImage() handles both canvas and image types.
|
|
this.setImage(object);
|
|
}
|
|
}
|
|
if (!this._size) {
|
|
this._size = new Size();
|
|
this._loaded = false;
|
|
}
|
|
},
|
|
|
|
_equals: function(item) {
|
|
return this.getSource() === item.getSource();
|
|
},
|
|
|
|
copyContent: function(source) {
|
|
var image = source._image,
|
|
canvas = source._canvas;
|
|
if (image) {
|
|
this._setImage(image);
|
|
} else if (canvas) {
|
|
// If the Raster contains a Canvas object, we need to create a new
|
|
// one and draw this raster's canvas on it.
|
|
var copyCanvas = CanvasProvider.getCanvas(source._size);
|
|
copyCanvas.getContext('2d').drawImage(canvas, 0, 0);
|
|
this._setImage(copyCanvas);
|
|
}
|
|
// TODO: Shouldn't this be copied with attributes instead of content?
|
|
this._crossOrigin = source._crossOrigin;
|
|
},
|
|
|
|
/**
|
|
* The size of the raster in pixels.
|
|
*
|
|
* @bean
|
|
* @type Size
|
|
*/
|
|
getSize: function() {
|
|
var size = this._size;
|
|
return new LinkedSize(size ? size.width : 0, size ? size.height : 0,
|
|
this, 'setSize');
|
|
},
|
|
|
|
setSize: function(/* size */) {
|
|
var size = Size.read(arguments);
|
|
if (!size.equals(this._size)) { // NOTE: this._size could be null
|
|
if (size.width > 0 && size.height > 0) {
|
|
// Get reference to image before changing canvas.
|
|
var element = this.getElement();
|
|
// NOTE: Setting canvas internally sets _size.
|
|
// NOTE: No need to release canvas because #_setImage() does so.
|
|
this._setImage(CanvasProvider.getCanvas(size));
|
|
// Draw element back onto new canvas.
|
|
if (element)
|
|
this.getContext(true).drawImage(element, 0, 0,
|
|
size.width, size.height);
|
|
} else {
|
|
// 0-width / height dimensions do not require the creation of
|
|
// an internal canvas. Just reflect the size for now.
|
|
if (this._canvas)
|
|
CanvasProvider.release(this._canvas);
|
|
this._size = size.clone();
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* The width of the raster in pixels.
|
|
*
|
|
* @bean
|
|
* @type Number
|
|
*/
|
|
getWidth: function() {
|
|
return this._size ? this._size.width : 0;
|
|
},
|
|
|
|
setWidth: function(width) {
|
|
this.setSize(width, this.getHeight());
|
|
},
|
|
|
|
/**
|
|
* The height of the raster in pixels.
|
|
*
|
|
* @bean
|
|
* @type Number
|
|
*/
|
|
getHeight: function() {
|
|
return this._size ? this._size.height : 0;
|
|
},
|
|
|
|
setHeight: function(height) {
|
|
this.setSize(this.getWidth(), height);
|
|
},
|
|
|
|
isEmpty: function() {
|
|
var size = this._size;
|
|
return !size || size.width === 0 && size.height === 0;
|
|
},
|
|
|
|
/**
|
|
* The resolution of the raster at its current size, in PPI (pixels per
|
|
* inch).
|
|
*
|
|
* @bean
|
|
* @type Size
|
|
*/
|
|
getResolution: function() {
|
|
var matrix = this._matrix,
|
|
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);
|
|
return new Size(
|
|
72 / u.getLength(),
|
|
72 / v.getLength()
|
|
);
|
|
},
|
|
|
|
/**
|
|
* @private
|
|
* @bean
|
|
* @deprecated use {@link #getResolution()} instead.
|
|
*/
|
|
getPpi: '#getResolution',
|
|
|
|
/**
|
|
* The HTMLImageElement or Canvas element of the raster, if one is
|
|
* associated.
|
|
* Note that for consistency, a {@link #onLoad} event will be triggered on
|
|
* the raster even if the image has already finished loading before, or if
|
|
* we are setting the raster to a canvas.
|
|
*
|
|
* @bean
|
|
* @type HTMLImageElement|HTMLCanvasElement
|
|
*/
|
|
getImage: function() {
|
|
return this._image;
|
|
},
|
|
|
|
setImage: function(image) {
|
|
var that = this;
|
|
|
|
function emit(event) {
|
|
var view = that.getView(),
|
|
type = event && event.type || 'load';
|
|
if (view && that.responds(type)) {
|
|
paper = view._scope;
|
|
that.emit(type, new Event(event));
|
|
}
|
|
}
|
|
|
|
this._setImage(image);
|
|
if (this._loaded) {
|
|
// Emit load event with a delay, so behavior is the same as when
|
|
// it's actually loaded and we give the code time to install event.
|
|
setTimeout(emit, 0);
|
|
} else if (image) {
|
|
// Trigger the load event on the image once it's loaded
|
|
DomEvent.add(image, {
|
|
load: function(event) {
|
|
that._setImage(image);
|
|
emit(event);
|
|
},
|
|
error: emit
|
|
});
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Internal version of {@link #setImage(image)} that does not trigger
|
|
* events. This is used by #setImage(), but also in other places where
|
|
* underlying canvases are replaced, resized, etc.
|
|
*/
|
|
_setImage: function(image) {
|
|
if (this._canvas)
|
|
CanvasProvider.release(this._canvas);
|
|
// Due to similarities, we can handle both canvas and image types here.
|
|
if (image && image.getContext) {
|
|
// A Canvas object
|
|
this._image = null;
|
|
this._canvas = image;
|
|
this._loaded = true;
|
|
} else {
|
|
// A Image object
|
|
this._image = image;
|
|
this._canvas = null;
|
|
this._loaded = !!(image && image.src && image.complete);
|
|
}
|
|
// Both canvas and image have width / height attributes. Due to IE,
|
|
// naturalWidth / Height needs to be checked for a swell, because it
|
|
// apparently can have width / height set to 0 when the image is
|
|
// invisible in the document.
|
|
this._size = new Size(
|
|
image ? image.naturalWidth || image.width : 0,
|
|
image ? image.naturalHeight || image.height : 0);
|
|
this._context = null;
|
|
this._changed(/*#=*/(Change.GEOMETRY | Change.PIXELS));
|
|
},
|
|
|
|
/**
|
|
* The Canvas object of the raster. If the raster was created from an image,
|
|
* accessing its canvas causes the raster to try and create one and draw the
|
|
* image into it. Depending on security policies, this might fail, in which
|
|
* case `null` is returned instead.
|
|
*
|
|
* @bean
|
|
* @type HTMLCanvasELement
|
|
*/
|
|
getCanvas: function() {
|
|
if (!this._canvas) {
|
|
var ctx = CanvasProvider.getContext(this._size);
|
|
// Since drawImage into canvas might fail based on security policies
|
|
// wrap the call in try-catch and only set _canvas if we succeeded.
|
|
try {
|
|
if (this._image)
|
|
ctx.drawImage(this._image, 0, 0);
|
|
this._canvas = ctx.canvas;
|
|
} catch (e) {
|
|
CanvasProvider.release(ctx);
|
|
}
|
|
}
|
|
return this._canvas;
|
|
},
|
|
|
|
// #setCanvas() is a simple alias to #setImage()
|
|
setCanvas: '#setImage',
|
|
|
|
/**
|
|
* The Canvas 2D drawing context of the raster.
|
|
*
|
|
* @bean
|
|
* @type Context
|
|
*/
|
|
getContext: function(modify) {
|
|
if (!this._context)
|
|
this._context = this.getCanvas().getContext('2d');
|
|
// Support a hidden parameter that indicates if the context will be used
|
|
// to modify the Raster object. We can notify such changes ahead since
|
|
// they are only used afterwards for redrawing.
|
|
if (modify) {
|
|
// Also set _image to null since the Raster stops representing it.
|
|
// NOTE: This should theoretically be in our own _changed() handler
|
|
// for ChangeFlag.PIXELS, but since it's only happening in one place
|
|
// this is fine:
|
|
this._image = null;
|
|
this._changed(/*#=*/Change.PIXELS);
|
|
}
|
|
return this._context;
|
|
},
|
|
|
|
setContext: function(context) {
|
|
this._context = context;
|
|
},
|
|
|
|
/**
|
|
* The source of the raster, which can be set using a DOM Image, a Canvas,
|
|
* a data url, a string describing the URL to load the image from, or the
|
|
* ID of a DOM element to get the image from (either a DOM Image or a
|
|
* Canvas). Reading this property will return the url of the source image or
|
|
* a data-url.
|
|
* Note that for consistency, a {@link #onLoad} event will be triggered on
|
|
* the raster even if the image has already finished loading before.
|
|
*
|
|
* @bean
|
|
* @type HTMLImageElement|HTMLCanvasElement|String
|
|
*
|
|
* @example {@paperscript}
|
|
* var raster = new Raster();
|
|
* raster.source = 'http://paperjs.org/about/paper-js.gif';
|
|
* raster.position = view.center;
|
|
*
|
|
* @example {@paperscript}
|
|
* var raster = new Raster({
|
|
* source: 'http://paperjs.org/about/paper-js.gif',
|
|
* position: view.center
|
|
* });
|
|
*/
|
|
getSource: function() {
|
|
var image = this._image;
|
|
return image && image.src || this.toDataURL();
|
|
},
|
|
|
|
setSource: function(src) {
|
|
var crossOrigin = this._crossOrigin,
|
|
// src can be an URL or a DOM ID to load the image from:
|
|
image = document.getElementById(src) || new window.Image();
|
|
if (crossOrigin)
|
|
image.crossOrigin = crossOrigin;
|
|
// A new image created above? Set the source now.
|
|
if (!image.src)
|
|
image.src = src;
|
|
this.setImage(image);
|
|
},
|
|
|
|
/**
|
|
* The crossOrigin value to be used when loading the image resource, in
|
|
* order to support CORS. Note that this needs to be set before setting the
|
|
* {@link #source} property in order to always work (e.g. when the image is
|
|
* cached in the browser).
|
|
*
|
|
* @bean
|
|
* @type String
|
|
*
|
|
* @example {@paperscript}
|
|
* var raster = new Raster({
|
|
* crossOrigin: 'anonymous',
|
|
* source: 'http://assets.paperjs.org/images/marilyn.jpg',
|
|
* position: view.center
|
|
* });
|
|
*
|
|
* console.log(view.element.toDataURL('image/png').substring(0, 32));
|
|
*/
|
|
getCrossOrigin: function() {
|
|
return this._image && this._image.crossOrigin || this._crossOrigin || '';
|
|
},
|
|
|
|
setCrossOrigin: function(crossOrigin) {
|
|
this._crossOrigin = crossOrigin;
|
|
if (this._image)
|
|
this._image.crossOrigin = crossOrigin;
|
|
},
|
|
|
|
// DOCS: document Raster#getElement
|
|
getElement: function() {
|
|
// Only return the internal element if the content is actually ready.
|
|
return this._canvas || this._loaded && this._image;
|
|
}
|
|
}, /** @lends Raster# */{
|
|
// Explicitly deactivate the creation of beans, as we have functions here
|
|
// that look like bean getters but actually read arguments.
|
|
// See #getSubCanvas(), #getSubRaster(), #getSubRaster(), #getPixel(),
|
|
// #getImageData()
|
|
beans: false,
|
|
|
|
/**
|
|
* Extracts a part of the Raster's content as a sub image, and returns it as
|
|
* a Canvas object.
|
|
*
|
|
* @param {Rectangle} rect the boundaries of the sub image in pixel
|
|
* coordinates
|
|
*
|
|
* @return {HTMLCanvasELement} the sub image as a Canvas object
|
|
*/
|
|
getSubCanvas: function(/* rect */) {
|
|
var rect = Rectangle.read(arguments),
|
|
ctx = CanvasProvider.getContext(rect.getSize());
|
|
ctx.drawImage(this.getCanvas(), rect.x, rect.y,
|
|
rect.width, rect.height, 0, 0, rect.width, rect.height);
|
|
return ctx.canvas;
|
|
},
|
|
|
|
/**
|
|
* Extracts a part of the raster item's content as a new raster item, placed
|
|
* in exactly the same place as the original content.
|
|
*
|
|
* @param {Rectangle} rect the boundaries of the sub raster in pixel
|
|
* coordinates
|
|
*
|
|
* @return {Raster} the sub raster as a newly created raster item
|
|
*/
|
|
getSubRaster: function(/* rect */) {
|
|
var rect = Rectangle.read(arguments),
|
|
raster = new Raster(Item.NO_INSERT);
|
|
raster._setImage(this.getSubCanvas(rect));
|
|
raster.translate(rect.getCenter().subtract(this.getSize().divide(2)));
|
|
raster._matrix.prepend(this._matrix);
|
|
raster.insertAbove(this);
|
|
return raster;
|
|
},
|
|
|
|
/**
|
|
* Returns a Base 64 encoded `data:` URL representation of the raster.
|
|
*
|
|
* @return {String}
|
|
*/
|
|
toDataURL: function() {
|
|
// See if the linked image is base64 encoded already, if so reuse it,
|
|
// otherwise try using canvas.toDataURL()
|
|
var image = this._image,
|
|
src = image && image.src;
|
|
if (/^data:/.test(src))
|
|
return src;
|
|
var canvas = this.getCanvas();
|
|
return canvas ? canvas.toDataURL.apply(canvas, arguments) : null;
|
|
},
|
|
|
|
/**
|
|
* Draws an image on the raster.
|
|
*
|
|
* @param {HTMLImageELement|HTMLCanvasELement} image
|
|
* @param {Point} point the offset of the image as a point in pixel
|
|
* coordinates
|
|
*/
|
|
drawImage: function(image /*, point */) {
|
|
var point = Point.read(arguments, 1);
|
|
this.getContext(true).drawImage(image, point.x, point.y);
|
|
},
|
|
|
|
/**
|
|
* Calculates the average color of the image within the given path,
|
|
* rectangle or point. This can be used for creating raster image
|
|
* effects.
|
|
*
|
|
* @param {Path|Rectangle|Point} object
|
|
* @return {Color} the average color contained in the area covered by the
|
|
* specified path, rectangle or point
|
|
*/
|
|
getAverageColor: function(object) {
|
|
var bounds, path;
|
|
if (!object) {
|
|
bounds = this.getBounds();
|
|
} else if (object instanceof PathItem) {
|
|
// TODO: What if the path is smaller than 1 px?
|
|
// TODO: How about rounding of bounds.size?
|
|
path = object;
|
|
bounds = object.getBounds();
|
|
} else if (object.width) {
|
|
bounds = new Rectangle(object);
|
|
} else if (object.x) {
|
|
// Create a rectangle of 1px size around the specified coordinates
|
|
bounds = new Rectangle(object.x - 0.5, object.y - 0.5, 1, 1);
|
|
}
|
|
// Use a sample size of max 32 x 32 pixels, into which the path is
|
|
// scaled as a clipping path, and then the actual image is drawn in and
|
|
// sampled.
|
|
var sampleSize = 32,
|
|
width = Math.min(bounds.width, sampleSize),
|
|
height = Math.min(bounds.height, sampleSize);
|
|
// Reuse the same sample context for speed. Memory consumption is low
|
|
// since it's only 32 x 32 pixels.
|
|
var ctx = Raster._sampleContext;
|
|
if (!ctx) {
|
|
ctx = Raster._sampleContext = CanvasProvider.getContext(
|
|
new Size(sampleSize));
|
|
} else {
|
|
// Clear the sample canvas:
|
|
ctx.clearRect(0, 0, sampleSize + 1, sampleSize + 1);
|
|
}
|
|
ctx.save();
|
|
// Scale the context so that the bounds ends up at the given sample size
|
|
var matrix = new Matrix()
|
|
.scale(width / bounds.width, height / bounds.height)
|
|
.translate(-bounds.x, -bounds.y);
|
|
matrix.applyToContext(ctx);
|
|
// If a path was passed, draw it as a clipping mask:
|
|
// See Project#draw() for an explanation of new Base()
|
|
if (path)
|
|
path.draw(ctx, new Base({ clip: true, matrices: [matrix] }));
|
|
// Now draw the image clipped into it.
|
|
this._matrix.applyToContext(ctx);
|
|
var element = this.getElement(),
|
|
size = this._size;
|
|
if (element)
|
|
ctx.drawImage(element, -size.width / 2, -size.height / 2);
|
|
ctx.restore();
|
|
// Get pixel data from the context and calculate the average color value
|
|
// from it, taking alpha into account.
|
|
var pixels = ctx.getImageData(0.5, 0.5, Math.ceil(width),
|
|
Math.ceil(height)).data,
|
|
channels = [0, 0, 0],
|
|
total = 0;
|
|
for (var i = 0, l = pixels.length; i < l; i += 4) {
|
|
var alpha = pixels[i + 3];
|
|
total += alpha;
|
|
alpha /= 255;
|
|
channels[0] += pixels[i] * alpha;
|
|
channels[1] += pixels[i + 1] * alpha;
|
|
channels[2] += pixels[i + 2] * alpha;
|
|
}
|
|
for (var i = 0; i < 3; i++)
|
|
channels[i] /= total;
|
|
return total ? Color.read(channels) : null;
|
|
},
|
|
|
|
/**
|
|
* {@grouptitle Pixels}
|
|
* Gets the color of a pixel in the raster.
|
|
*
|
|
* @name Raster#getPixel
|
|
* @function
|
|
* @param x the x offset of the pixel in pixel coordinates
|
|
* @param y the y offset of the pixel in pixel coordinates
|
|
* @return {Color} the color of the pixel
|
|
*/
|
|
/**
|
|
* Gets the color of a pixel in the raster.
|
|
*
|
|
* @name Raster#getPixel
|
|
* @function
|
|
* @param point the offset of the pixel as a point in pixel coordinates
|
|
* @return {Color} the color of the pixel
|
|
*/
|
|
getPixel: function(/* point */) {
|
|
var point = Point.read(arguments);
|
|
var data = this.getContext().getImageData(point.x, point.y, 1, 1).data;
|
|
// Alpha is separate now:
|
|
return new Color('rgb', [data[0] / 255, data[1] / 255, data[2] / 255],
|
|
data[3] / 255);
|
|
},
|
|
|
|
/**
|
|
* Sets the color of the specified pixel to the specified color.
|
|
*
|
|
* @name Raster#setPixel
|
|
* @function
|
|
* @param x the x offset of the pixel in pixel coordinates
|
|
* @param y the y offset of the pixel in pixel coordinates
|
|
* @param color the color that the pixel will be set to
|
|
*/
|
|
/**
|
|
* Sets the color of the specified pixel to the specified color.
|
|
*
|
|
* @name Raster#setPixel
|
|
* @function
|
|
* @param point the offset of the pixel as a point in pixel coordinates
|
|
* @param color the color that the pixel will be set to
|
|
*/
|
|
setPixel: function(/* point, color */) {
|
|
var point = Point.read(arguments),
|
|
color = Color.read(arguments),
|
|
components = color._convert('rgb'),
|
|
alpha = color._alpha,
|
|
ctx = this.getContext(true),
|
|
imageData = ctx.createImageData(1, 1),
|
|
data = imageData.data;
|
|
data[0] = components[0] * 255;
|
|
data[1] = components[1] * 255;
|
|
data[2] = components[2] * 255;
|
|
data[3] = alpha != null ? alpha * 255 : 255;
|
|
ctx.putImageData(imageData, point.x, point.y);
|
|
},
|
|
|
|
// DOCS: document Raster#createImageData
|
|
/**
|
|
* {@grouptitle Image Data}
|
|
* @param {Size} size
|
|
* @return {ImageData}
|
|
*/
|
|
createImageData: function(/* size */) {
|
|
var size = Size.read(arguments);
|
|
return this.getContext().createImageData(size.width, size.height);
|
|
},
|
|
|
|
// DOCS: document Raster#getImageData
|
|
/**
|
|
* @param {Rectangle} rect
|
|
* @return {ImageData}
|
|
*/
|
|
getImageData: function(/* rect */) {
|
|
var rect = Rectangle.read(arguments);
|
|
if (rect.isEmpty())
|
|
rect = new Rectangle(this._size);
|
|
return this.getContext().getImageData(rect.x, rect.y,
|
|
rect.width, rect.height);
|
|
},
|
|
|
|
// DOCS: document Raster#setImageData
|
|
/**
|
|
* @param {ImageData} data
|
|
* @param {Point} point
|
|
*/
|
|
setImageData: function(data /*, point */) {
|
|
var point = Point.read(arguments, 1);
|
|
this.getContext(true).putImageData(data, point.x, point.y);
|
|
},
|
|
|
|
/**
|
|
* {@grouptitle Event Handlers}
|
|
*
|
|
* The event handler function to be called when the underlying image has
|
|
* finished loading and is ready to be used. This is also triggered when
|
|
* the image is already loaded, or when a canvas is used instead of an
|
|
* image.
|
|
*
|
|
* @name Raster#onLoad
|
|
* @property
|
|
* @type Function
|
|
*
|
|
* @example
|
|
* var url = 'http://assets.paperjs.org/images/marilyn.jpg';
|
|
* var raster = new Raster(url);
|
|
*
|
|
* // If you create a Raster using a url, you can use the onLoad
|
|
* // handler to do something once it is loaded:
|
|
* raster.onLoad = function() {
|
|
* console.log('The image has finished loading.');
|
|
* };
|
|
*
|
|
* // As with all events in paper.js, you can also use this notation instead
|
|
* // to install multiple handlers:
|
|
* raster.on('load', function() {
|
|
* console.log('Now the image is definitely ready.');
|
|
* });
|
|
*/
|
|
|
|
/**
|
|
*
|
|
* The event handler function to be called when there is an error loading
|
|
* the underlying image.
|
|
*
|
|
* @name Raster#onError
|
|
* @property
|
|
* @type Function
|
|
*/
|
|
|
|
_getBounds: function(matrix, options) {
|
|
var rect = new Rectangle(this._size).setCenter(0, 0);
|
|
return matrix ? matrix._transformBounds(rect) : rect;
|
|
},
|
|
|
|
_hitTestSelf: function(point) {
|
|
if (this._contains(point)) {
|
|
var that = this;
|
|
return new HitResult('pixel', that, {
|
|
offset: point.add(that._size.divide(2)).round(),
|
|
// Inject as Straps.js accessor, so #toString renders well too
|
|
color: {
|
|
get: function() {
|
|
return that.getPixel(this.offset);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
},
|
|
|
|
_draw: function(ctx) {
|
|
var element = this.getElement();
|
|
if (element) {
|
|
// Handle opacity for Rasters separately from the rest, since
|
|
// Rasters never draw a stroke. See Item#draw().
|
|
ctx.globalAlpha = this._opacity;
|
|
ctx.drawImage(element,
|
|
-this._size.width / 2, -this._size.height / 2);
|
|
}
|
|
},
|
|
|
|
_canComposite: function() {
|
|
return true;
|
|
}
|
|
});
|