Skip to content

Commit

Permalink
Apply transformation for the stroke on the canvas so the skew is also… (
Browse files Browse the repository at this point in the history
#2531)

I noticed that the Android implementation had trouble with applying a skew on a stroke, so this attempts to fix that issue.

The issue was that since the transformation was applied to the path, it didn’t take any properties from the paint into consideration. As a result, the skew was not applied to the width of the line. By applying the transformation on the canvas, this issue is resolved.

Example Lottie which wasn’t rendered correctly (should have skewed lines):
[Skewed-Stroke.json](https://github.com/user-attachments/files/16495718/Skewed-Stroke.json)

Co-authored-by: Mirko <m.volkers@wearetriple.com>
  • Loading branch information
Mirko-Volkers and MirkoVolkers authored Aug 6, 2024
1 parent 1ffaac8 commit 5a71516
Show file tree
Hide file tree
Showing 3 changed files with 12 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -166,15 +166,15 @@ public abstract class BaseStrokeContent
}
int alpha = (int) ((parentAlpha / 255f * ((IntegerKeyframeAnimation) opacityAnimation).getIntValue() / 100f) * 255);
paint.setAlpha(clamp(alpha, 0, 255));
paint.setStrokeWidth(((FloatKeyframeAnimation) widthAnimation).getFloatValue() * Utils.getScale(parentMatrix));
paint.setStrokeWidth(((FloatKeyframeAnimation) widthAnimation).getFloatValue());
if (paint.getStrokeWidth() <= 0) {
// Android draws a hairline stroke for 0, After Effects doesn't.
if (L.isTraceEnabled()) {
L.endSection("StrokeContent#draw");
}
return;
}
applyDashPatternIfNeeded(parentMatrix);
applyDashPatternIfNeeded();

if (colorFilterAnimation != null) {
paint.setColorFilter(colorFilterAnimation.getValue());
Expand All @@ -194,19 +194,21 @@ public abstract class BaseStrokeContent
dropShadowAnimation.applyTo(paint, parentMatrix, Utils.mixOpacities(parentAlpha, alpha));
}

canvas.save();
canvas.concat(parentMatrix);
for (int i = 0; i < pathGroups.size(); i++) {
PathGroup pathGroup = pathGroups.get(i);


if (pathGroup.trimPath != null) {
applyTrimPath(canvas, pathGroup, parentMatrix);
applyTrimPath(canvas, pathGroup);
} else {
if (L.isTraceEnabled()) {
L.beginSection("StrokeContent#buildPath");
}
path.reset();
for (int j = pathGroup.paths.size() - 1; j >= 0; j--) {
path.addPath(pathGroup.paths.get(j).getPath(), parentMatrix);
path.addPath(pathGroup.paths.get(j).getPath());
}
if (L.isTraceEnabled()) {
L.endSection("StrokeContent#buildPath");
Expand All @@ -218,12 +220,13 @@ public abstract class BaseStrokeContent
}
}
}
canvas.restore();
if (L.isTraceEnabled()) {
L.endSection("StrokeContent#draw");
}
}

private void applyTrimPath(Canvas canvas, PathGroup pathGroup, Matrix parentMatrix) {
private void applyTrimPath(Canvas canvas, PathGroup pathGroup) {
if (L.isTraceEnabled()) {
L.beginSection("StrokeContent#applyTrimPath");
}
Expand All @@ -235,7 +238,7 @@ private void applyTrimPath(Canvas canvas, PathGroup pathGroup, Matrix parentMatr
}
path.reset();
for (int j = pathGroup.paths.size() - 1; j >= 0; j--) {
path.addPath(pathGroup.paths.get(j).getPath(), parentMatrix);
path.addPath(pathGroup.paths.get(j).getPath());
}
float animStartValue = pathGroup.trimPath.getStart().getValue() / 100f;
float animEndValue = pathGroup.trimPath.getEnd().getValue() / 100f;
Expand All @@ -262,7 +265,6 @@ private void applyTrimPath(Canvas canvas, PathGroup pathGroup, Matrix parentMatr
float currentLength = 0;
for (int j = pathGroup.paths.size() - 1; j >= 0; j--) {
trimPathPath.set(pathGroup.paths.get(j).getPath());
trimPathPath.transform(parentMatrix);
pm.setPath(trimPathPath, false);
float length = pm.getLength();
if (endLength > totalLength && endLength - totalLength < currentLength + length &&
Expand Down Expand Up @@ -336,7 +338,7 @@ private void applyTrimPath(Canvas canvas, PathGroup pathGroup, Matrix parentMatr
}
}

private void applyDashPatternIfNeeded(Matrix parentMatrix) {
private void applyDashPatternIfNeeded() {
if (L.isTraceEnabled()) {
L.beginSection("StrokeContent#applyDashPattern");
}
Expand All @@ -347,7 +349,6 @@ private void applyDashPatternIfNeeded(Matrix parentMatrix) {
return;
}

float scale = Utils.getScale(parentMatrix);
for (int i = 0; i < dashPatternAnimations.size(); i++) {
dashPatternValues[i] = dashPatternAnimations.get(i).getValue();
// If the value of the dash pattern or gap is too small, the number of individual sections
Expand All @@ -363,9 +364,8 @@ private void applyDashPatternIfNeeded(Matrix parentMatrix) {
dashPatternValues[i] = 0.1f;
}
}
dashPatternValues[i] *= scale;
}
float offset = dashPatternOffsetAnimation == null ? 0f : dashPatternOffsetAnimation.getValue() * scale;
float offset = dashPatternOffsetAnimation == null ? 0f : dashPatternOffsetAnimation.getValue();
paint.setPathEffect(new DashPathEffect(dashPatternValues, offset));
if (L.isTraceEnabled()) {
L.endSection("StrokeContent#applyDashPattern");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ public GradientStrokeContent(
} else {
shader = getRadialGradient();
}
shader.setLocalMatrix(parentMatrix);
paint.setShader(shader);

super.draw(canvas, parentMatrix, parentAlpha);
Expand Down
1 change: 1 addition & 0 deletions snapshot-tests/src/main/assets/Tests/SkewedStroke.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{ "v":"4.8.0", "meta":{ "g":"LottieFiles AE 3.5.8", "a":"", "k":"", "d":"", "tc":"" }, "fr":25, "ip":0, "op":50, "w":192, "h":96, "nm":"Comp 1", "ddd":0, "assets":[], "layers":[ { "ddd":0, "ind":1, "ty":4, "nm":"Shape Layer 2", "sr":1, "ks":{ "o":{ "a":0, "k":100, "ix":11 }, "r":{ "a":0, "k":0, "ix":10 }, "p":{ "a":0, "k":[ 86, 70, 0 ], "ix":2 }, "a":{ "a":0, "k":[ 0, 0, 0 ], "ix":1 }, "s":{ "a":0, "k":[ 100, 100, 100 ], "ix":6 } }, "ao":0, "shapes":[ { "ty":"gr", "it":[ { "ind":0, "ty":"sh", "ix":1, "ks":{ "a":0, "k":{ "i":[ [ 0, 0 ], [ 0, 0 ] ], "o":[ [ 0, 0 ], [ 0, 0 ] ], "v":[ [ -72, -3 ], [ -8.5, -3 ] ], "c":false }, "ix":2 }, "nm":"Path 1", "mn":"ADBE Vector Shape - Group", "hd":false }, { "ty":"tm", "s":{ "a":1, "k":[ { "i":{ "x":[ 0.667 ], "y":[ 1 ] }, "o":{ "x":[ 0.333 ], "y":[ 0 ] }, "t":5, "s":[ 94 ] }, { "t":18, "s":[ 0 ] } ], "ix":1 }, "e":{ "a":1, "k":[ { "i":{ "x":[ 0.667 ], "y":[ 1 ] }, "o":{ "x":[ 0.333 ], "y":[ 0 ] }, "t":18, "s":[ 94 ] }, { "t":30, "s":[ 0 ] } ], "ix":2 }, "o":{ "a":0, "k":0, "ix":3 }, "m":1, "ix":2, "nm":"Trim Paths 1", "mn":"ADBE Vector Filter - Trim", "hd":false }, { "ty":"st", "c":{ "a":0, "k":[ 0.937254901961, 0.254901960784, 0.207843137255, 1 ], "ix":3 }, "o":{ "a":0, "k":100, "ix":4 }, "w":{ "a":0, "k":12, "ix":5 }, "lc":1, "lj":1, "ml":4, "bm":0, "nm":"Stroke 1", "mn":"ADBE Vector Graphic - Stroke", "hd":false }, { "ty":"fl", "c":{ "a":0, "k":[ 1, 1, 1, 1 ], "ix":4 }, "o":{ "a":0, "k":100, "ix":5 }, "r":1, "bm":0, "nm":"Fill 1", "mn":"ADBE Vector Graphic - Fill", "hd":false }, { "ty":"tr", "p":{ "a":0, "k":[ 0, -10 ], "ix":2 }, "a":{ "a":0, "k":[ 0, 0 ], "ix":1 }, "s":{ "a":0, "k":[ 100, 100 ], "ix":3 }, "r":{ "a":0, "k":0, "ix":6 }, "o":{ "a":0, "k":100, "ix":7 }, "sk":{ "a":0, "k":12, "ix":4 }, "sa":{ "a":0, "k":0, "ix":5 }, "nm":"Transform" } ], "nm":"Shape 1", "np":4, "cix":2, "bm":0, "ix":1, "mn":"ADBE Vector Group", "hd":false } ], "ip":5, "op":505, "st":5, "bm":0 }, { "ddd":0, "ind":2, "ty":4, "nm":"Shape Layer 1", "sr":1, "ks":{ "o":{ "a":0, "k":100, "ix":11 }, "r":{ "a":0, "k":0, "ix":10 }, "p":{ "a":0, "k":[ 100, 50, 0 ], "ix":2 }, "a":{ "a":0, "k":[ 0, 0, 0 ], "ix":1 }, "s":{ "a":0, "k":[ 100, 100, 100 ], "ix":6 } }, "ao":0, "shapes":[ { "ty":"gr", "it":[ { "ind":0, "ty":"sh", "ix":1, "ks":{ "a":0, "k":{ "i":[ [ 0, 0 ], [ 0, 0 ] ], "o":[ [ 0, 0 ], [ 0, 0 ] ], "v":[ [ -72, -3 ], [ -8.5, -3 ] ], "c":false }, "ix":2 }, "nm":"Path 1", "mn":"ADBE Vector Shape - Group", "hd":false }, { "ty":"tm", "s":{ "a":1, "k":[ { "i":{ "x":[ 0.667 ], "y":[ 1 ] }, "o":{ "x":[ 0.333 ], "y":[ 0 ] }, "t":0, "s":[ 94 ] }, { "t":13, "s":[ 0 ] } ], "ix":1 }, "e":{ "a":1, "k":[ { "i":{ "x":[ 0.667 ], "y":[ 1 ] }, "o":{ "x":[ 0.333 ], "y":[ 0 ] }, "t":13, "s":[ 94 ] }, { "t":25, "s":[ 0 ] } ], "ix":2 }, "o":{ "a":0, "k":0, "ix":3 }, "m":1, "ix":2, "nm":"Trim Paths 1", "mn":"ADBE Vector Filter - Trim", "hd":false }, { "ty":"st", "c":{ "a":0, "k":[ 0.937254901961, 0.254901960784, 0.207843137255, 1 ], "ix":3 }, "o":{ "a":0, "k":100, "ix":4 }, "w":{ "a":0, "k":12, "ix":5 }, "lc":1, "lj":1, "ml":4, "bm":0, "nm":"Stroke 1", "mn":"ADBE Vector Graphic - Stroke", "hd":false }, { "ty":"fl", "c":{ "a":0, "k":[ 1, 1, 1, 1 ], "ix":4 }, "o":{ "a":0, "k":100, "ix":5 }, "r":1, "bm":0, "nm":"Fill 1", "mn":"ADBE Vector Graphic - Fill", "hd":false }, { "ty":"tr", "p":{ "a":0, "k":[ 0, -10 ], "ix":2 }, "a":{ "a":0, "k":[ 0, 0 ], "ix":1 }, "s":{ "a":0, "k":[ 100, 100 ], "ix":3 }, "r":{ "a":0, "k":0, "ix":6 }, "o":{ "a":0, "k":100, "ix":7 }, "sk":{ "a":0, "k":12, "ix":4 }, "sa":{ "a":0, "k":0, "ix":5 }, "nm":"Transform" } ], "nm":"Shape 1", "np":4, "cix":2, "bm":0, "ix":1, "mn":"ADBE Vector Group", "hd":false } ], "ip":0, "op":500, "st":0, "bm":0 } ], "markers":[ ] }

0 comments on commit 5a71516

Please sign in to comment.