svg import: transformation fixes

This commit is contained in:
honfika@gmail.com
2015-12-26 09:13:02 +01:00
parent e77a10e799
commit 06762009b7
3 changed files with 76 additions and 54 deletions

View File

@@ -246,7 +246,7 @@ public final class Matrix implements Cloneable {
String funcName = transformStr.split("\\(")[0];
transformStr = transformStr.substring(funcName.length() + 1);
String params = transformStr.split("\\)")[0];
transformStr = transformStr.substring(params.length() + 1);
transformStr = transformStr.substring(params.length() + 1).trim();
while (params.contains(" ")) {
params = params.replaceAll(" ", " ");
}
@@ -259,12 +259,12 @@ public final class Matrix implements Cloneable {
switch (funcName) {
case "matrix":
if (args.length == 6) {
double scaleX = Double.parseDouble(args[0].trim()) * unitDivisor;
double rotateSkew0 = Double.parseDouble(args[1].trim()) * unitDivisor;
double rotateSkew1 = Double.parseDouble(args[2].trim()) * unitDivisor;
double scaleY = Double.parseDouble(args[3].trim()) * unitDivisor;
double translateX = Double.parseDouble(args[4].trim()) * translateDivisor;
double translateY = Double.parseDouble(args[5].trim()) * translateDivisor;
double scaleX = Double.parseDouble(args[0].trim());
double rotateSkew0 = Double.parseDouble(args[1].trim());
double rotateSkew1 = Double.parseDouble(args[2].trim());
double scaleY = Double.parseDouble(args[3].trim());
double translateX = Double.parseDouble(args[4].trim());
double translateY = Double.parseDouble(args[5].trim());
Matrix result = new Matrix();
result.translateX = translateX;
result.translateY = translateY;
@@ -277,17 +277,15 @@ public final class Matrix implements Cloneable {
break;
case "translate":
if (args.length == 1 || args.length == 2) {
double translateX = Double.parseDouble(args[0].trim()) * translateDivisor;
double translateX = Double.parseDouble(args[0].trim());
double translateY = 0;
if (args.length == 2) {
translateY = Double.parseDouble(args[1].trim()) * translateDivisor;
translateY = Double.parseDouble(args[1].trim());
}
Matrix result = new Matrix();
result.translateX = translateX;
result.translateY = translateY;
result.scaleX = unitDivisor;
result.scaleY = unitDivisor;
ret = ret.concatenate(result);
}
break;
@@ -339,14 +337,20 @@ public final class Matrix implements Cloneable {
result.rotateSkew1 = Math.sin(angleRad);
result.scaleX = Math.cos(angleRad);
result.scaleY = Math.cos(angleRad);
result = result.preConcatenate(getTranslateInstance(tx * translateDivisor, ty * translateDivisor))
.concatenate(getTranslateInstance(-tx * translateDivisor, -ty * translateDivisor));
result = result.preConcatenate(getTranslateInstance(tx, ty))
.concatenate(getTranslateInstance(-tx, -ty));
ret = ret.concatenate(result);
}
break;
}
}
ret.translateX *= translateDivisor;
ret.translateY *= translateDivisor;
ret.rotateSkew0 *= unitDivisor;
ret.rotateSkew1 *= unitDivisor;
ret.scaleX *= unitDivisor;
ret.scaleY *= unitDivisor;
return ret;
}

View File

@@ -221,7 +221,7 @@ public class ShapeImporter {
SvgStyle style = new SvgStyle();
style = style.apply(rootElement, idMap);
Matrix transform = Matrix.getScaleInstance(SWF.unitDivisor);
Matrix transform = new Matrix();
processSvgObject(idMap, shapeNum, shapes, rootElement, transform, style);
} catch (SAXException | IOException | ParserConfigurationException ex) {
Logger.getLogger(ShapeImporter.class.getName()).log(Level.SEVERE, null, ex);
@@ -257,8 +257,8 @@ public class ShapeImporter {
Element childElement = (Element) childNode;
String tagName = childElement.getTagName();
SvgStyle newStyle = style.apply(childElement, idMap);
Matrix m = Matrix.parseSvgMatrix(childElement.getAttribute("transform"), SWF.unitDivisor, 1);
Matrix m2 = m == null ? transform : m.concatenate(transform);
Matrix m = Matrix.parseSvgMatrix(childElement.getAttribute("transform"), 1, 1);
Matrix m2 = m == null ? transform : transform.concatenate(m);
if ("g".equals(tagName)) {
processSvgObject(idMap, shapeNum, shapes, childElement, m2, newStyle);
} else if ("path".equals(tagName)) {
@@ -286,6 +286,7 @@ public class ShapeImporter {
}
private void processCommands(int shapeNum, SHAPEWITHSTYLE shapes, List<PathCommand> commands, Matrix transform, SvgStyle style) {
Matrix transform2 = transform.preConcatenate(Matrix.getScaleInstance(SWF.unitDivisor));
Point prevPoint = new Point(0, 0);
Point startPoint = prevPoint;
double x0 = 0;
@@ -323,7 +324,7 @@ public class ShapeImporter {
x = command.params[0];
y = command.params[1];
p = transform.transform(x, y);
p = transform2.transform(x, y);
scr.moveDeltaX = (int) Math.round(p.x);
scr.moveDeltaY = (int) Math.round(p.y);
prevPoint = p;
@@ -346,7 +347,7 @@ public class ShapeImporter {
x = command.params[0];
y = command.params[1];
p = transform.transform(x, y);
p = transform2.transform(x, y);
serl.deltaX = (int) Math.round(p.x - prevPoint.x);
serl.deltaY = (int) Math.round(p.y - prevPoint.y);
prevPoint = p;
@@ -358,7 +359,7 @@ public class ShapeImporter {
StraightEdgeRecord serh = new StraightEdgeRecord();
x = command.params[0];
p = transform.transform(x, y);
p = transform2.transform(x, y);
serh.deltaX = (int) Math.round(p.x - prevPoint.x);
prevPoint = p;
shapes.shapeRecords.add(serh);
@@ -367,7 +368,7 @@ public class ShapeImporter {
StraightEdgeRecord serv = new StraightEdgeRecord();
y = command.params[0];
p = transform.transform(x, y);
p = transform2.transform(x, y);
serv.deltaY = (int) Math.round(p.y - prevPoint.y);
prevPoint = p;
serv.vertLineFlag = true;
@@ -378,7 +379,7 @@ public class ShapeImporter {
x = command.params[0];
y = command.params[1];
p = transform.transform(x, y);
p = transform2.transform(x, y);
cer.controlDeltaX = (int) Math.round(p.x - prevPoint.x);
cer.controlDeltaY = (int) Math.round(p.y - prevPoint.y);
prevPoint = p;
@@ -386,7 +387,7 @@ public class ShapeImporter {
x = command.params[2];
y = command.params[3];
p = transform.transform(x, y);
p = transform2.transform(x, y);
cer.anchorDeltaX = (int) Math.round(p.x - prevPoint.x);
cer.anchorDeltaY = (int) Math.round(p.y - prevPoint.y);
prevPoint = p;
@@ -402,24 +403,24 @@ public class ShapeImporter {
x = command.params[0];
y = command.params[1];
pControl1 = transform.transform(x, y);
pControl1 = transform2.transform(x, y);
x = command.params[2];
y = command.params[3];
Point pControl2 = transform.transform(x, y);
Point pControl2 = transform2.transform(x, y);
x = command.params[4];
y = command.params[5];
p = transform.transform(x, y);
p = transform2.transform(x, y);
//StraightEdgeRecord serc = new StraightEdgeRecord();
//serc.generalLineFlag = true;
//serc.deltaX = (int) Math.round(p.x - prevPoint.x);
//serc.deltaY = (int) Math.round(p.y - prevPoint.y);
//shapes.shapeRecords.add(serc);
List<Double> quadCoordinates = new CubicToQuad().cubicToQuad(pStart.x, pStart.y, pControl1.x, pControl1.y, pControl2.x, pControl2.y, p.x, p.y, 0.0006);
List<Double> quadCoordinates = new CubicToQuad().cubicToQuad(pStart.x, pStart.y, pControl1.x, pControl1.y, pControl2.x, pControl2.y, p.x, p.y, 1);
for (int i = 2; i < quadCoordinates.size();) {
CurvedEdgeRecord cerc = new CurvedEdgeRecord();
p = new Point(quadCoordinates.get(i++), quadCoordinates.get(i++));
@@ -443,7 +444,7 @@ public class ShapeImporter {
x0 = x;
y0 = y;
}
applyStyleGradients(shapes.getBounds(), scrStyle, transform, shapeNum, style);
applyStyleGradients(shapes.getBounds(), scrStyle, transform2, shapeNum, style);
}
private void processPath(int shapeNum, SHAPEWITHSTYLE shapes, Element childElement, Matrix transform, SvgStyle style) {
@@ -606,7 +607,6 @@ public class ShapeImporter {
cerc.command = 'C';
cerc.params = new double[]{pControl1.x, pControl1.y, pControl2.x, pControl2.y, x, y};
pathCommands.add(cerc);
break;
case 'A':
double rx = pathReader.readDouble();
@@ -1076,14 +1076,15 @@ public class ShapeImporter {
SvgFill fill = style.getFillWithOpacity();
if (fill != null) {
if (fill instanceof SvgGradient) {
FILLSTYLE fillStyle = scr.fillStyles.fillStyles[0];
SvgGradient gfill = (SvgGradient) fill;
Matrix gradientMatrix = Matrix.parseSvgMatrix(gfill.gradientTransform, SWF.unitDivisor, 1);
gradientMatrix = transform.concatenate(Matrix.getScaleInstance(1 / SWF.unitDivisor)).concatenate(gradientMatrix);
scr.fillStyles.fillStyles[0].gradientMatrix = gradientMatrix.toMATRIX();
fillStyle.gradientMatrix = gradientMatrix.toMATRIX();
if (fill instanceof SvgLinearGradient) {
SvgLinearGradient lgfill = (SvgLinearGradient) fill;
scr.fillStyles.fillStyles[0].fillStyleType = FILLSTYLE.LINEAR_GRADIENT;
scr.fillStyles.fillStyles[0].gradient = new GRADIENT();
fillStyle.fillStyleType = FILLSTYLE.LINEAR_GRADIENT;
fillStyle.gradient = new GRADIENT();
double x1;
if (lgfill.x1.endsWith("%")) {
x1 = Double.parseDouble(lgfill.x1.substring(0, lgfill.x1.length() - 1)) / 100;
@@ -1144,8 +1145,8 @@ public class ShapeImporter {
Point p1 = tMatrix.transform(new Point(-16384, 0));
Point p2 = tMatrix.transform(new Point(16384, 0));
tMatrix = tMatrix.preConcatenate(new Matrix(scr.fillStyles.fillStyles[0].gradientMatrix));
scr.fillStyles.fillStyles[0].gradientMatrix = tMatrix.toMATRIX();
tMatrix = tMatrix.preConcatenate(new Matrix(fillStyle.gradientMatrix));
fillStyle.gradientMatrix = tMatrix.toMATRIX();
} else if (fill instanceof SvgRadialGradient) {
SvgRadialGradient rgfill = (SvgRadialGradient) fill;
double cx;
@@ -1167,7 +1168,9 @@ public class ShapeImporter {
} else {
r = Double.parseDouble(rgfill.r);
}
//TODO: apply cx,cy,r to matrix
fillStyle.gradientMatrix = Matrix.getTranslateInstance(SWF.unitDivisor * cx, SWF.unitDivisor * cy).concatenate(Matrix.getScaleInstance(r / 819.2)).concatenate(new Matrix(fillStyle.gradientMatrix)).toMATRIX();
double fx;
if (rgfill.fx.endsWith("%")) {
@@ -1181,43 +1184,46 @@ public class ShapeImporter {
} else {
fy = Double.parseDouble(rgfill.fy);
}
double f = (fx / cx + fy / cy) / 2;
if (!rgfill.fx.equals(rgfill.cx) || !rgfill.fy.equals(rgfill.cy)) {
scr.fillStyles.fillStyles[0].fillStyleType = FILLSTYLE.FOCAL_RADIAL_GRADIENT;
scr.fillStyles.fillStyles[0].gradient = new FOCALGRADIENT();
FOCALGRADIENT fg = (FOCALGRADIENT) scr.fillStyles.fillStyles[0].gradient;
fillStyle.fillStyleType = FILLSTYLE.FOCAL_RADIAL_GRADIENT;
fillStyle.gradient = new FOCALGRADIENT();
FOCALGRADIENT fg = (FOCALGRADIENT) fillStyle.gradient;
double f = Math.sqrt((fx - cx) * (fx - cx) + (fy - cy) * (fy - cy)) / 819.2;
fg.focalPoint = (float) f;
} else {
scr.fillStyles.fillStyles[0].fillStyleType = FILLSTYLE.RADIAL_GRADIENT;
scr.fillStyles.fillStyles[0].gradient = new GRADIENT();
fillStyle.fillStyleType = FILLSTYLE.RADIAL_GRADIENT;
fillStyle.gradient = new GRADIENT();
}
}
switch (gfill.spreadMethod) {
case PAD:
scr.fillStyles.fillStyles[0].gradient.spreadMode = GRADIENT.SPREAD_PAD_MODE;
fillStyle.gradient.spreadMode = GRADIENT.SPREAD_PAD_MODE;
break;
case REFLECT:
scr.fillStyles.fillStyles[0].gradient.spreadMode = GRADIENT.SPREAD_REFLECT_MODE;
fillStyle.gradient.spreadMode = GRADIENT.SPREAD_REFLECT_MODE;
break;
case REPEAT:
scr.fillStyles.fillStyles[0].gradient.spreadMode = GRADIENT.SPREAD_REPEAT_MODE;
fillStyle.gradient.spreadMode = GRADIENT.SPREAD_REPEAT_MODE;
break;
}
switch (gfill.interpolation) {
case LINEAR_RGB:
scr.fillStyles.fillStyles[0].gradient.interpolationMode = GRADIENT.INTERPOLATION_LINEAR_RGB_MODE;
fillStyle.gradient.interpolationMode = GRADIENT.INTERPOLATION_LINEAR_RGB_MODE;
break;
case SRGB:
scr.fillStyles.fillStyles[0].gradient.interpolationMode = GRADIENT.INTERPOLATION_RGB_MODE;
fillStyle.gradient.interpolationMode = GRADIENT.INTERPOLATION_RGB_MODE;
break;
}
scr.fillStyles.fillStyles[0].gradient.gradientRecords = new GRADRECORD[gfill.stops.size()];
fillStyle.gradient.gradientRecords = new GRADRECORD[gfill.stops.size()];
for (int i = 0; i < gfill.stops.size(); i++) {
scr.fillStyles.fillStyles[0].gradient.gradientRecords[i] = new GRADRECORD();
scr.fillStyles.fillStyles[0].gradient.gradientRecords[i].inShape3 = shapeNum >= 3;
scr.fillStyles.fillStyles[0].gradient.gradientRecords[i].color = shapeNum >= 3 ? new RGBA(gfill.stops.get(i).color) : new RGB(gfill.stops.get(i).color);
scr.fillStyles.fillStyles[0].gradient.gradientRecords[i].ratio = (int) Math.round(gfill.stops.get(i).offset * 255);
SvgStop stop = gfill.stops.get(i);
Color color = stop.color;
color = new Color(color.getRed(), color.getGreen(), color.getBlue(), (int) Math.round(color.getAlpha() * style.opacity));
fillStyle.gradient.gradientRecords[i] = new GRADRECORD();
fillStyle.gradient.gradientRecords[i].inShape3 = shapeNum >= 3;
fillStyle.gradient.gradientRecords[i].color = shapeNum >= 3 ? new RGBA(color) : new RGB(color);
fillStyle.gradient.gradientRecords[i].ratio = (int) Math.round(stop.offset * 255);
}
}
}
@@ -1505,6 +1511,10 @@ public class ShapeImporter {
offset = Double.parseDouble(offsetStr);
}
Color color = newStyle.stopColor;
if (color == null) {
color = Color.BLACK;
}
int alpha = (int) Math.round(newStyle.stopOpacity * 255);
color = new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha);
if (!stopsCleared) { //It has some stop nodes -> remove all inherited stops
@@ -2118,7 +2128,11 @@ public class ShapeImporter {
}
break;
case "stop-color": {
style.stopColor = parseColor(value);
if ("inherit".equals(value) || "currentColor".equals(value)) {
showWarning(value + "StopColorNotSupported", "The stop color value '" + value + "' is not supported.");
} else {
style.stopColor = parseColor(value);
}
}
break;
case "fill-opacity": {
@@ -2127,8 +2141,12 @@ public class ShapeImporter {
}
break;
case "stop-opacity": {
double stopOpacity = Double.parseDouble(value);
style.stopOpacity = stopOpacity;
if ("inherit".equals(value)) {
showWarning(value + "StopOpacityNotSupported", "The stop opacity value '" + value + "' is not supported.");
} else {
double stopOpacity = Double.parseDouble(value);
style.stopOpacity = stopOpacity;
}
}
break;
case "stroke": {
@@ -2208,7 +2226,7 @@ public class ShapeImporter {
String[] styleDefs = element.getAttribute("style").split(";");
for (String styleDef : styleDefs) {
String[] parts = styleDef.split(":", 2);
applyStyle(idMap, result, parts[0], parts[1].trim());
applyStyle(idMap, result, parts[0].trim(), parts[1].trim());
}
}

View File

@@ -256,7 +256,7 @@ public class CubicToQuad {
}
private boolean _isApproximationClose(Point a, Point b, Point c, Point d, List<Point[]> quadCurves, double errorBound) {
double dt = 1 / quadCurves.size();
double dt = 1.0 / quadCurves.size();
for (int i = 0; i < quadCurves.size(); i++) {
Point p1 = quadCurves.get(i)[0];
Point c1 = quadCurves.get(i)[1];