-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix gradient parsing #5836
Fix gradient parsing #5836
Changes from 5 commits
d391c63
f23bf1a
001218d
d029fc8
fe3d5a8
2cdc443
78f594b
46f60e4
69f9dff
05aec06
01fe62a
5e2e845
54f20de
695c13a
c2a697a
7931ee6
8843f53
14db4d7
b8a8c32
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -95,18 +95,59 @@ | |
*/ | ||
offsetY: 0, | ||
|
||
/** | ||
* A transform matrix to apply to the gradient before paiting. | ||
* It shares the same name with the svg property but behaves differently. | ||
* @type Array[Number] | ||
* @default null | ||
*/ | ||
gradientTransform: null, | ||
|
||
/** | ||
* coordinates units for coords. | ||
* If `pixels`, the number of cords are in the same unit of width/ height. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. coords |
||
* If set as `percentage` the coords are still a number, but 1 means 100% of width | ||
* for the X and 100% of the height for the y. It can be bigger than 1 and negative. | ||
* @type String pixels || percentage | ||
* @default 'pixels' | ||
*/ | ||
gradientUnits: 'pixels', | ||
|
||
/** | ||
* Gradient type | ||
* @type String linear || radial | ||
* @default 'pixels' | ||
*/ | ||
type: 'linear', | ||
|
||
/** | ||
* Constructor | ||
* @param {Object} [options] Options object with type, coords, gradientUnits and colorStops | ||
* @param {Object} options Options object with type, coords, gradientUnits and colorStops | ||
* @param {Object} [options.type] gradient type | ||
* @param {Object} [options.gradientUnits] gradient units | ||
* @param {Object} [options.offsetX] SVG import compatibility | ||
* @param {Object} [options.offsetY] SVG import compatibility | ||
* @param {Object} options.coords contains the coords of the gradient | ||
* @param {Array[Object]} options.colorStops contains the colorstops. | ||
* @param {Number} [options.coords.x1] | ||
* @param {Number} [options.coords.y1] | ||
* @param {Number} [options.coords.x2] | ||
* @param {Number} [options.coords.y2] | ||
* @param {Number} [options.coords.r1] | ||
* @param {Number} [options.coords.r2] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. options.coords should probably be next to options.coords.x1. Move colorstops up one line? |
||
* @return {fabric.Gradient} thisArg | ||
*/ | ||
initialize: function(options) { | ||
options || (options = { }); | ||
|
||
var coords = { }; | ||
var coords = { }, _this = this; | ||
|
||
this.id = fabric.Object.__uid++; | ||
this.type = options.type || 'linear'; | ||
// sets everything, then coords and colorstops get sets again | ||
Object.keys(options).forEach(function(option) { | ||
_this[option] = options[option]; | ||
}); | ||
|
||
!this.id && (this.id = fabric.Object.__uid++); | ||
|
||
coords = { | ||
x1: options.coords.x1 || 0, | ||
|
@@ -119,13 +160,9 @@ | |
coords.r1 = options.coords.r1 || 0; | ||
coords.r2 = options.coords.r2 || 0; | ||
} | ||
|
||
this.coords = coords; | ||
this.colorStops = options.colorStops.slice(); | ||
if (options.gradientTransform) { | ||
this.gradientTransform = options.gradientTransform; | ||
} | ||
this.offsetX = options.offsetX || this.offsetX; | ||
this.offsetY = options.offsetY || this.offsetY; | ||
}, | ||
|
||
/** | ||
|
@@ -307,7 +344,7 @@ | |
* @see http://www.w3.org/TR/SVG/pservers.html#LinearGradientElement | ||
* @see http://www.w3.org/TR/SVG/pservers.html#RadialGradientElement | ||
*/ | ||
fromElement: function(el, instance, opacityAttr) { | ||
fromElement: function(el, instance, opacityAttr, svgOptions) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You might as well add a javadoc line for svgOptions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sure, i m not done yet, svg export is now broken and needs to be adequate to the new logic. |
||
/** | ||
* @example: | ||
* | ||
|
@@ -349,43 +386,45 @@ | |
|
||
var colorStopEls = el.getElementsByTagName('stop'), | ||
type, | ||
gradientUnits = el.getAttribute('gradientUnits') || 'objectBoundingBox', | ||
gradientTransform = el.getAttribute('gradientTransform'), | ||
gradientUnits = el.getAttribute('gradientUnits') === 'userSpaceOnUse' ? | ||
'pixels' : 'percentage', | ||
gradientTransform = el.getAttribute('gradientTransform') || '', | ||
colorStops = [], | ||
coords, ellipseMatrix, i; | ||
|
||
coords, i, offsetX = 0, offsetY = 0, | ||
transformMatrix; | ||
if (el.nodeName === 'linearGradient' || el.nodeName === 'LINEARGRADIENT') { | ||
type = 'linear'; | ||
coords = getLinearCoords(el); | ||
} | ||
else { | ||
type = 'radial'; | ||
} | ||
|
||
if (type === 'linear') { | ||
coords = getLinearCoords(el); | ||
} | ||
else if (type === 'radial') { | ||
coords = getRadialCoords(el); | ||
} | ||
|
||
for (i = colorStopEls.length; i--; ) { | ||
colorStops.push(getColorStop(colorStopEls[i], multiplier)); | ||
} | ||
|
||
ellipseMatrix = _convertPercentUnitsToValues(instance, coords, gradientUnits); | ||
transformMatrix = fabric.parseTransformAttribute(gradientTransform); | ||
|
||
__convertPercentUnitsToValues(instance, coords, svgOptions, gradientUnits); | ||
|
||
if (gradientUnits === 'pixels') { | ||
offsetX = -instance.left; | ||
offsetY = -instance.top; | ||
} | ||
|
||
var gradient = new fabric.Gradient({ | ||
id: el.getAttribute('id'), | ||
type: type, | ||
coords: coords, | ||
colorStops: colorStops, | ||
offsetX: -instance.left, | ||
offsetY: -instance.top | ||
gradientUnits: gradientUnits, | ||
gradientTransform: transformMatrix, | ||
offsetX: offsetX, | ||
offsetY: offsetY, | ||
}); | ||
|
||
if (gradientTransform || ellipseMatrix !== '') { | ||
gradient.gradientTransform = fabric.parseTransformAttribute((gradientTransform || '') + ellipseMatrix); | ||
} | ||
|
||
return gradient; | ||
}, | ||
/* _FROM_SVG_END_ */ | ||
|
@@ -399,54 +438,40 @@ | |
*/ | ||
forObject: function(obj, options) { | ||
options || (options = { }); | ||
_convertPercentUnitsToValues(obj, options.coords, 'userSpaceOnUse'); | ||
__convertPercentUnitsToValues(obj, options.coords); | ||
return new fabric.Gradient(options); | ||
} | ||
}); | ||
|
||
/** | ||
* @private | ||
*/ | ||
function _convertPercentUnitsToValues(object, options, gradientUnits) { | ||
var propValue, addFactor = 0, multFactor = 1, ellipseMatrix = ''; | ||
for (var prop in options) { | ||
if (options[prop] === 'Infinity') { | ||
options[prop] = 1; | ||
} | ||
else if (options[prop] === '-Infinity') { | ||
options[prop] = 0; | ||
function __convertPercentUnitsToValues(instance, options, svgOptions, gradientUnits) { | ||
var propValue, finalValue; | ||
Object.keys(options).forEach(function(prop) { | ||
propValue = options[prop]; | ||
if (propValue === 'Infinity') { | ||
finalValue = 1; | ||
} | ||
propValue = parseFloat(options[prop], 10); | ||
if (typeof options[prop] === 'string' && /^(\d+\.\d+)%|(\d+)%$/.test(options[prop])) { | ||
multFactor = 0.01; | ||
else if (propValue === '-Infinity') { | ||
finalValue = 0; | ||
} | ||
else { | ||
multFactor = 1; | ||
} | ||
if (prop === 'x1' || prop === 'x2' || prop === 'r2') { | ||
multFactor *= gradientUnits === 'objectBoundingBox' ? object.width : 1; | ||
addFactor = gradientUnits === 'objectBoundingBox' ? object.left || 0 : 0; | ||
} | ||
else if (prop === 'y1' || prop === 'y2') { | ||
multFactor *= gradientUnits === 'objectBoundingBox' ? object.height : 1; | ||
addFactor = gradientUnits === 'objectBoundingBox' ? object.top || 0 : 0; | ||
} | ||
options[prop] = propValue * multFactor + addFactor; | ||
} | ||
if (object.type === 'ellipse' && | ||
options.r2 !== null && | ||
gradientUnits === 'objectBoundingBox' && | ||
object.rx !== object.ry) { | ||
|
||
var scaleFactor = object.ry / object.rx; | ||
ellipseMatrix = ' scale(1, ' + scaleFactor + ')'; | ||
if (options.y1) { | ||
options.y1 /= scaleFactor; | ||
} | ||
if (options.y2) { | ||
options.y2 /= scaleFactor; | ||
finalValue = parseFloat(options[prop], 10); | ||
if (typeof propValue === 'string' && /^(\d+\.\d+)%|(\d+)%$/.test(propValue)) { | ||
finalValue *= 0.01; | ||
if (gradientUnits === 'pixels') { | ||
// then we need to fix those percentages here in svg parsing | ||
if (prop === 'x1' || prop === 'x2' || prop === 'r2') { | ||
finalValue *= svgOptions.viewBoxWidth || svgOptions.width; | ||
} | ||
if (prop === 'y1' || prop === 'y2') { | ||
finalValue *= svgOptions.viewBoxHeight || svgOptions.height; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
return ellipseMatrix; | ||
options[prop] = finalValue; | ||
}); | ||
} | ||
})(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I know, JavaDoc is probably insufficient for this (I've never seen a 2-D array defined in JavaDoc in detail), but some documentation of the members is better than none.
Even better would be to make this an instance of an explicit unit-testable matrix class. I realize that would be a significant refactor, but I'd be very surprised if you didn't have a need for it by now elsewhere in the code. I do see there's a fabric.ColorMatrix class, but that appears to be something else.