mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-08-28 22:08:54 -04:00
Fix how gradient matrices are handled when Item#applyMatrix = false
Closes #1238
This commit is contained in:
parent
920cbaca99
commit
5291043a5f
6 changed files with 92 additions and 41 deletions
|
@ -3358,18 +3358,19 @@ new function() { // Injection scope for hit-test functions shared with project
|
|||
if (matrix && matrix.isIdentity())
|
||||
matrix = null;
|
||||
var _matrix = this._matrix,
|
||||
transform = matrix && !matrix.isIdentity(),
|
||||
applyMatrix = (_applyMatrix || this._applyMatrix)
|
||||
// Don't apply _matrix if the result of concatenating with
|
||||
// matrix would be identity.
|
||||
&& ((!_matrix.isIdentity() || matrix)
|
||||
&& ((!_matrix.isIdentity() || transform)
|
||||
// Even if it's an identity matrix, we still need to
|
||||
// recursively apply the matrix to children.
|
||||
|| _applyMatrix && _applyRecursively && this._children);
|
||||
// Bail out if there is nothing to do.
|
||||
if (!matrix && !applyMatrix)
|
||||
if (!transform && !applyMatrix)
|
||||
return this;
|
||||
// Simply prepend the internal matrix with the passed one:
|
||||
if (matrix) {
|
||||
if (transform) {
|
||||
// Keep a backup of the last valid state before the matrix becomes
|
||||
// non-invertible. This is then used again in setBounds to restore.
|
||||
if (!matrix.isInvertible() && _matrix.isInvertible())
|
||||
|
@ -3380,28 +3381,39 @@ new function() { // Injection scope for hit-test functions shared with project
|
|||
// internal _matrix transformations to the item's content.
|
||||
// Application is not possible on Raster, PointText, SymbolItem, since
|
||||
// the matrix is where the actual transformation state is stored.
|
||||
if (applyMatrix = applyMatrix && this._transformContent(_matrix,
|
||||
_applyRecursively, _setApplyMatrix)) {
|
||||
// When the _matrix could be applied, we also need to transform
|
||||
// color styles (only gradients so far) and pivot point:
|
||||
var pivot = this._pivot,
|
||||
style = this._style,
|
||||
// pass true for _dontMerge so we don't recursively transform
|
||||
if (applyMatrix) {
|
||||
if (this._transformContent(_matrix, _applyRecursively,
|
||||
_setApplyMatrix)) {
|
||||
var pivot = this._pivot;
|
||||
if (pivot)
|
||||
_matrix._transformPoint(pivot, pivot, true);
|
||||
// Reset the internal matrix to the identity transformation if
|
||||
// it was possible to apply it.
|
||||
_matrix.reset(true);
|
||||
// Set the internal _applyMatrix flag to true if we're told to
|
||||
// do so
|
||||
if (_setApplyMatrix && this._canApplyMatrix)
|
||||
this._applyMatrix = true;
|
||||
} else {
|
||||
applyMatrix = transform = false;
|
||||
}
|
||||
}
|
||||
if (transform) {
|
||||
// When a new matrix was applied, we also need to transform gradient
|
||||
// color points. These always need transforming, regardless of
|
||||
// #applyMatrix, as they are defined in the parent's coordinate
|
||||
// system.
|
||||
// TODO: Introduce options to control whether fills should be
|
||||
// transformed or not.
|
||||
var style = this._style,
|
||||
// Pass true for _dontMerge so we don't recursively transform
|
||||
// styles on groups' children.
|
||||
fillColor = style.getFillColor(true),
|
||||
strokeColor = style.getStrokeColor(true);
|
||||
if (pivot)
|
||||
_matrix._transformPoint(pivot, pivot, true);
|
||||
if (fillColor)
|
||||
fillColor.transform(_matrix);
|
||||
fillColor.transform(matrix);
|
||||
if (strokeColor)
|
||||
strokeColor.transform(_matrix);
|
||||
// Reset the internal matrix to the identity transformation if it
|
||||
// was possible to apply it.
|
||||
_matrix.reset(true);
|
||||
// Set the internal _applyMatrix flag to true if we're told to do so
|
||||
if (_setApplyMatrix && this._canApplyMatrix)
|
||||
this._applyMatrix = true;
|
||||
strokeColor.transform(matrix);
|
||||
}
|
||||
// Calling _changed will clear _bounds and _position, but depending
|
||||
// on matrix we can calculate and set them again, so preserve them.
|
||||
|
@ -4094,12 +4106,13 @@ new function() { // Injection scope for hit-test functions shared with project
|
|||
_setStyles: function(ctx, param, viewMatrix) {
|
||||
// We can access internal properties since we're only using this on
|
||||
// items without children, where styles would be merged.
|
||||
var style = this._style;
|
||||
var style = this._style,
|
||||
matrix = this._matrix;
|
||||
if (style.hasFill()) {
|
||||
ctx.fillStyle = style.getFillColor().toCanvasStyle(ctx);
|
||||
ctx.fillStyle = style.getFillColor().toCanvasStyle(ctx, matrix);
|
||||
}
|
||||
if (style.hasStroke()) {
|
||||
ctx.strokeStyle = style.getStrokeColor().toCanvasStyle(ctx);
|
||||
ctx.strokeStyle = style.getStrokeColor().toCanvasStyle(ctx, matrix);
|
||||
ctx.lineWidth = style.getStrokeWidth();
|
||||
var strokeJoin = style.getStrokeJoin(),
|
||||
strokeCap = style.getStrokeCap(),
|
||||
|
@ -4245,8 +4258,8 @@ new function() { // Injection scope for hit-test functions shared with project
|
|||
// on the temporary canvas.
|
||||
ctx.translate(-itemOffset.x, -itemOffset.y);
|
||||
}
|
||||
// Apply globalMatrix when drawing into temporary canvas.
|
||||
if (transform) {
|
||||
// Apply viewMatrix when drawing into temporary canvas.
|
||||
(direct ? matrix : viewMatrix).applyToContext(ctx);
|
||||
}
|
||||
if (clip) {
|
||||
|
|
|
@ -28,8 +28,8 @@ var Shape = Item.extend(/** @lends Shape# */{
|
|||
radius: null
|
||||
},
|
||||
|
||||
initialize: function Shape(props) {
|
||||
this._initialize(props);
|
||||
initialize: function Shape(props, point) {
|
||||
this._initialize(props, point);
|
||||
},
|
||||
|
||||
_equals: function(item) {
|
||||
|
@ -386,11 +386,11 @@ new function() { // Scope for _contains() and _hitTestSelf() code.
|
|||
// Mess with indentation in order to get more line-space below:
|
||||
statics: new function() {
|
||||
function createShape(type, point, size, radius, args) {
|
||||
var item = new Shape(Base.getNamed(args));
|
||||
var item = new Shape(Base.getNamed(args), point);
|
||||
item._type = type;
|
||||
item._size = size;
|
||||
item._radius = radius;
|
||||
return item.translate(point);
|
||||
return item;
|
||||
}
|
||||
|
||||
return /** @lends Shape */{
|
||||
|
|
|
@ -834,7 +834,7 @@ var Color = Base.extend(new function() {
|
|||
+ components.join(',') + ')';
|
||||
},
|
||||
|
||||
toCanvasStyle: function(ctx) {
|
||||
toCanvasStyle: function(ctx, matrix) {
|
||||
if (this._canvasStyle)
|
||||
return this._canvasStyle;
|
||||
// Normal colors are simply represented by their CSS string.
|
||||
|
@ -846,10 +846,20 @@ var Color = Base.extend(new function() {
|
|||
stops = gradient._stops,
|
||||
origin = components[1],
|
||||
destination = components[2],
|
||||
highlight = components[3],
|
||||
inverse = matrix && matrix.inverted(),
|
||||
canvasGradient;
|
||||
// If the item's content is transformed by a matrix, we need to
|
||||
// inverse transform the gradient points, as they are defined in
|
||||
// item's parent coordinate system.
|
||||
if (inverse) {
|
||||
origin = inverse._transformPoint(origin);
|
||||
destination = inverse._transformPoint(destination);
|
||||
if (highlight)
|
||||
highlight = inverse._transformPoint(highlight);
|
||||
}
|
||||
if (gradient._radial) {
|
||||
var radius = destination.getDistance(origin),
|
||||
highlight = components[3];
|
||||
var radius = destination.getDistance(origin);
|
||||
if (highlight) {
|
||||
var vector = highlight.subtract(origin);
|
||||
if (vector.getLength() > radius)
|
||||
|
|
|
@ -69,9 +69,9 @@ var GradientStop = Base.extend(/** @lends GradientStop# */{
|
|||
* Called by various setters whenever a value changes
|
||||
*/
|
||||
_changed: function() {
|
||||
// Loop through the gradients that use this stop and notify them about
|
||||
// the change, so they can notify their gradient colors, which in turn
|
||||
// will notify the items they are used in:
|
||||
// Notify the graident that uses this stop about the change, so it can
|
||||
// notify its gradient colors, which in turn will notify the items they
|
||||
// are used in:
|
||||
if (this._owner)
|
||||
this._owner._changed(/*#=*/Change.STYLE);
|
||||
},
|
||||
|
|
|
@ -389,13 +389,6 @@ new function() {
|
|||
.translate(bounds.getPoint())
|
||||
.scale(bounds.getSize()));
|
||||
}
|
||||
if (item instanceof Shape) {
|
||||
// When applying gradient colors to shapes, we need
|
||||
// to offset the shape's initial position to get the
|
||||
// same results as SVG.
|
||||
color.transform(new Matrix().translate(
|
||||
item.getPosition(true).negate()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -248,3 +248,38 @@ test('Gradient', function() {
|
|||
equals(function() { return stop3.offset; }, 1);
|
||||
equals(function() { return stop4.offset; }, 0.5);
|
||||
});
|
||||
|
||||
test('Gradients with applyMatrix', function() {
|
||||
var topLeft = [100, 100];
|
||||
var bottomRight = [400, 400];
|
||||
var gradientColor = {
|
||||
gradient: {
|
||||
stops: ['yellow', 'red', 'blue']
|
||||
},
|
||||
origin: topLeft,
|
||||
destination: bottomRight
|
||||
}
|
||||
|
||||
var path = new Path.Rectangle({
|
||||
topLeft: topLeft,
|
||||
bottomRight: bottomRight,
|
||||
fillColor: gradientColor,
|
||||
applyMatrix: true
|
||||
});
|
||||
|
||||
var shape = new Shape.Rectangle({
|
||||
topLeft: topLeft,
|
||||
bottomRight: bottomRight,
|
||||
fillColor: gradientColor,
|
||||
applyMatrix: false
|
||||
});
|
||||
|
||||
comparePixels(path, shape);
|
||||
|
||||
path.scale(2);
|
||||
path.rotate(45);
|
||||
shape.scale(2);
|
||||
shape.rotate(45);
|
||||
|
||||
comparePixels(path, shape);
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue