aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBo Duan <boduan402@gmail.com>2019-11-10 21:25:55 -0800
committerGabriel Peal <gpeal@users.noreply.github.com>2019-11-10 21:25:55 -0800
commit9abda9fd97800d02897513a7a19bb3f050e02bad (patch)
tree7be738d31f9e9f1c95dad54cdf40697708c5cd31
parent3a5d706bad862a4eec9e3b4bc10a0c693077987e (diff)
downloadlottie-9abda9fd97800d02897513a7a19bb3f050e02bad.tar.gz
Support ScaleType.FIT_XY. (#1418)
Fixes #801 Fixes #1384
-rw-r--r--LottieSample/src/androidTest/java/com/airbnb/lottie/samples/LottieTest.kt58
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java20
-rw-r--r--lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java156
3 files changed, 195 insertions, 39 deletions
diff --git a/LottieSample/src/androidTest/java/com/airbnb/lottie/samples/LottieTest.kt b/LottieSample/src/androidTest/java/com/airbnb/lottie/samples/LottieTest.kt
index d01b40ce..a4ac57e2 100644
--- a/LottieSample/src/androidTest/java/com/airbnb/lottie/samples/LottieTest.kt
+++ b/LottieSample/src/androidTest/java/com/airbnb/lottie/samples/LottieTest.kt
@@ -342,6 +342,26 @@ class LottieTest {
animationView.scaleType = ImageView.ScaleType.CENTER_INSIDE
}
+ withAnimationView("LottieLogo1.json", "Scale Types", "300x300 fitXY") { animationView ->
+ animationView.progress = 1f
+ animationView.updateLayoutParams {
+ width = 300.dp.toInt()
+ height = 300.dp.toInt()
+ }
+ animationView.scaleType = ImageView.ScaleType.FIT_XY
+ }
+
+ withAnimationView("LottieLogo1.json", "Scale Types", "300x300 fitXY DisableExtraScale") {
+ animationView ->
+ animationView.progress = 1f
+ animationView.updateLayoutParams {
+ width = 300.dp.toInt()
+ height = 300.dp.toInt()
+ }
+ animationView.disableExtraScaleModeInFitXY()
+ animationView.scaleType = ImageView.ScaleType.FIT_XY
+ }
+
withAnimationView("LottieLogo1.json", "Scale Types", "300x300 centerInside @2x") { animationView ->
animationView.progress = 1f
animationView.updateLayoutParams {
@@ -371,6 +391,25 @@ class LottieTest {
animationView.scaleType = ImageView.ScaleType.CENTER_INSIDE
}
+ withAnimationView("LottieLogo1.json", "Scale Types", "600x300 fitXY") { animationView ->
+ animationView.progress = 1f
+ animationView.updateLayoutParams {
+ width = 600.dp.toInt()
+ height = 300.dp.toInt()
+ }
+ animationView.scaleType = ImageView.ScaleType.FIT_XY
+ }
+
+ withAnimationView("LottieLogo1.json", "Scale Types", "600x300 fitXY DisableExtraScale") { animationView ->
+ animationView.progress = 1f
+ animationView.updateLayoutParams {
+ width = 600.dp.toInt()
+ height = 300.dp.toInt()
+ }
+ animationView.disableExtraScaleModeInFitXY()
+ animationView.scaleType = ImageView.ScaleType.FIT_XY
+ }
+
withAnimationView("LottieLogo1.json", "Scale Types", "300x600 centerInside") { animationView ->
animationView.progress = 1f
animationView.updateLayoutParams {
@@ -379,6 +418,25 @@ class LottieTest {
}
animationView.scaleType = ImageView.ScaleType.CENTER_INSIDE
}
+
+ withAnimationView("LottieLogo1.json", "Scale Types", "300x600 fitXY") { animationView ->
+ animationView.progress = 1f
+ animationView.updateLayoutParams {
+ width = 300.dp.toInt()
+ height = 600.dp.toInt()
+ }
+ animationView.scaleType = ImageView.ScaleType.FIT_XY
+ }
+
+ withAnimationView("LottieLogo1.json", "Scale Types", "300x600 fitXY DisableExtraScale") { animationView ->
+ animationView.progress = 1f
+ animationView.updateLayoutParams {
+ width = 300.dp.toInt()
+ height = 600.dp.toInt()
+ }
+ animationView.disableExtraScaleModeInFitXY()
+ animationView.scaleType = ImageView.ScaleType.FIT_XY
+ }
}
private suspend fun testDynamicProperties() {
diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java b/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java
index 773a93e2..a8113d5d 100644
--- a/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java
+++ b/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java
@@ -842,6 +842,11 @@ import static com.airbnb.lottie.RenderMode.HARDWARE;
return lottieDrawable.getScale();
}
+ @Override public void setScaleType(ScaleType scaleType) {
+ super.setScaleType(scaleType);
+ lottieDrawable.setScaleType(scaleType);
+ }
+
@MainThread
public void cancelAnimation() {
wasAnimatingWhenNotShown = false;
@@ -955,6 +960,21 @@ import static com.airbnb.lottie.RenderMode.HARDWARE;
lottieDrawable.setApplyingOpacityToLayersEnabled(isApplyingOpacityToLayersEnabled);
}
+ /**
+ * Disable the extraScale mode in {@link #draw(Canvas)} function when scaleType is FitXY. It doesn't affect the rendering with other scaleTypes.
+ *
+ * <p>When there are 2 animation layout side by side, the default extra scale mode might leave 1 pixel not drawn between 2 animation, and
+ * disabling the extraScale mode can fix this problem</p>
+ *
+ * <b>Attention:</b> Disable the extra scale mode can downgrade the performance and may lead to larger memory footprint. Please only disable this
+ * mode when using animation with a reasonable dimension (smaller than screen size).
+ *
+ * @see LottieDrawable#drawWithNewAspectRatio(Canvas)
+ */
+ public void disableExtraScaleModeInFitXY() {
+ lottieDrawable.disableExtraScaleModeInFitXY();
+ }
+
private void enableOrDisableHardwareLayer() {
int layerType = LAYER_TYPE_SOFTWARE;
switch (renderMode) {
diff --git a/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java b/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java
index 17a21ce9..a1ed28d8 100644
--- a/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java
+++ b/lottie/src/main/java/com/airbnb/lottie/LottieDrawable.java
@@ -8,11 +8,13 @@ import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.view.View;
+import android.widget.ImageView;
import androidx.annotation.FloatRange;
import androidx.annotation.IntDef;
@@ -73,6 +75,8 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
}
};
@Nullable
+ private ImageView.ScaleType scaleType;
+ @Nullable
private ImageAssetManager imageAssetManager;
@Nullable
private String imageAssetsFolder;
@@ -90,6 +94,7 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
private int alpha = 255;
private boolean performanceTrackingEnabled;
private boolean isApplyingOpacityToLayersEnabled;
+ private boolean isExtraScaleEnabled = true;
/**
* True if the drawable has not been drawn since the last invalidateSelf.
* We can do this to prevent things like bounds from getting recalculated
@@ -260,6 +265,21 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
this.isApplyingOpacityToLayersEnabled = isApplyingOpacityToLayersEnabled;
}
+ /**
+ * Disable the extraScale mode in {@link #draw(Canvas)} function when scaleType is FitXY. It doesn't affect the rendering with other scaleTypes.
+ *
+ * <p>When there are 2 animation layout side by side, the default extra scale mode might leave 1 pixel not drawn between 2 animation, and
+ * disabling the extraScale mode can fix this problem</p>
+ *
+ * <b>Attention:</b> Disable the extra scale mode can downgrade the performance and may lead to larger memory footprint. Please only disable this
+ * mode when using animation with a reasonable dimension (smaller than screen size).
+ *
+ * @see #drawWithNewAspectRatio(Canvas)
+ */
+ public void disableExtraScaleModeInFitXY() {
+ this.isExtraScaleEnabled = false;
+ }
+
public boolean isApplyingOpacityToLayersEnabled() {
return isApplyingOpacityToLayersEnabled;
}
@@ -316,50 +336,16 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
@Override
public void draw(@NonNull Canvas canvas) {
isDirty = false;
- L.beginSection("Drawable#draw");
- if (compositionLayer == null) {
- return;
- }
- float scale = this.scale;
- float extraScale = 1f;
- float maxScale = getMaxScale(canvas);
- if (scale > maxScale) {
- scale = maxScale;
- extraScale = this.scale / scale;
- }
-
- int saveCount = -1;
- if (extraScale > 1) {
- // This is a bit tricky...
- // We can't draw on a canvas larger than ViewConfiguration.get(context).getScaledMaximumDrawingCacheSize()
- // which works out to be roughly the size of the screen because Android can't generate a
- // bitmap large enough to render to.
- // As a result, we cap the scale such that it will never be wider/taller than the screen
- // and then only render in the top left corner of the canvas. We then use extraScale
- // to scale up the rest of the scale. However, since we rendered the animation to the top
- // left corner, we need to scale up and translate the canvas to zoom in on the top left
- // corner.
- saveCount = canvas.save();
- float halfWidth = composition.getBounds().width() / 2f;
- float halfHeight = composition.getBounds().height() / 2f;
- float scaledHalfWidth = halfWidth * scale;
- float scaledHalfHeight = halfHeight * scale;
+ L.beginSection("Drawable#draw");
- canvas.translate(
- getScale() * halfWidth - scaledHalfWidth,
- getScale() * halfHeight - scaledHalfHeight);
- canvas.scale(extraScale, extraScale, scaledHalfWidth, scaledHalfHeight);
+ if (ImageView.ScaleType.FIT_XY == scaleType) {
+ drawWithNewAspectRatio(canvas);
+ } else {
+ drawWithOriginalAspectRatio(canvas);
}
- matrix.reset();
- matrix.preScale(scale, scale);
- compositionLayer.draw(canvas, matrix, alpha);
L.endSection("Drawable#draw");
-
- if (saveCount > 0) {
- canvas.restoreToCount(saveCount);
- }
}
// <editor-fold desc="animator">
@@ -1066,6 +1052,10 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
callback.unscheduleDrawable(this, what);
}
+ void setScaleType(ImageView.ScaleType scaleType) {
+ this.scaleType = scaleType;
+ }
+
/**
* If the composition is larger than the canvas, we have to use a different method to scale it up.
* See the comments in {@link #draw(Canvas)} for more info.
@@ -1076,6 +1066,94 @@ public class LottieDrawable extends Drawable implements Drawable.Callback, Anima
return Math.min(maxScaleX, maxScaleY);
}
+ private void drawWithNewAspectRatio(Canvas canvas) {
+ if (compositionLayer == null) {
+ return;
+ }
+
+ int saveCount = -1;
+ Rect bounds = getBounds();
+ // In fitXY mode, the scale doesn't take effect.
+ float scaleX = bounds.width() / (float) composition.getBounds().width();
+ float scaleY = bounds.height() / (float) composition.getBounds().height();
+
+ if (isExtraScaleEnabled) {
+ float maxScale = Math.min(scaleX, scaleY);
+ float extraScale = 1f;
+ if (maxScale < 1f) {
+ extraScale = extraScale / maxScale;
+ scaleX = scaleX / extraScale;
+ scaleY = scaleY / extraScale;
+ }
+
+ if (extraScale > 1) {
+ saveCount = canvas.save();
+ float halfWidth = bounds.width() / 2f;
+ float halfHeight = bounds.height() / 2f;
+ float scaledHalfWidth = halfWidth * maxScale;
+ float scaledHalfHeight = halfHeight * maxScale;
+
+ canvas.translate(
+ halfWidth - scaledHalfWidth,
+ halfHeight - scaledHalfHeight);
+ canvas.scale(extraScale, extraScale, scaledHalfWidth, scaledHalfHeight);
+ }
+ }
+
+ matrix.reset();
+ matrix.preScale(scaleX, scaleY);
+ compositionLayer.draw(canvas, matrix, alpha);
+
+ if (saveCount > 0) {
+ canvas.restoreToCount(saveCount);
+ }
+ }
+
+ private void drawWithOriginalAspectRatio(Canvas canvas) {
+ if (compositionLayer == null) {
+ return;
+ }
+
+ float scale = this.scale;
+ float extraScale = 1f;
+ float maxScale = getMaxScale(canvas);
+ if (scale > maxScale) {
+ scale = maxScale;
+ extraScale = this.scale / scale;
+ }
+
+ int saveCount = -1;
+ if (extraScale > 1) {
+ // This is a bit tricky...
+ // We can't draw on a canvas larger than ViewConfiguration.get(context).getScaledMaximumDrawingCacheSize()
+ // which works out to be roughly the size of the screen because Android can't generate a
+ // bitmap large enough to render to.
+ // As a result, we cap the scale such that it will never be wider/taller than the screen
+ // and then only render in the top left corner of the canvas. We then use extraScale
+ // to scale up the rest of the scale. However, since we rendered the animation to the top
+ // left corner, we need to scale up and translate the canvas to zoom in on the top left
+ // corner.
+ saveCount = canvas.save();
+ float halfWidth = composition.getBounds().width() / 2f;
+ float halfHeight = composition.getBounds().height() / 2f;
+ float scaledHalfWidth = halfWidth * scale;
+ float scaledHalfHeight = halfHeight * scale;
+
+ canvas.translate(
+ getScale() * halfWidth - scaledHalfWidth,
+ getScale() * halfHeight - scaledHalfHeight);
+ canvas.scale(extraScale, extraScale, scaledHalfWidth, scaledHalfHeight);
+ }
+
+ matrix.reset();
+ matrix.preScale(scale, scale);
+ compositionLayer.draw(canvas, matrix, alpha);
+
+ if (saveCount > 0) {
+ canvas.restoreToCount(saveCount);
+ }
+ }
+
private static class ColorFilterData {
final String layerName;