diff options
author | Bo Duan <boduan402@gmail.com> | 2019-11-10 21:25:55 -0800 |
---|---|---|
committer | Gabriel Peal <gpeal@users.noreply.github.com> | 2019-11-10 21:25:55 -0800 |
commit | 9abda9fd97800d02897513a7a19bb3f050e02bad (patch) | |
tree | 7be738d31f9e9f1c95dad54cdf40697708c5cd31 | |
parent | 3a5d706bad862a4eec9e3b4bc10a0c693077987e (diff) | |
download | lottie-9abda9fd97800d02897513a7a19bb3f050e02bad.tar.gz |
Support ScaleType.FIT_XY. (#1418)
Fixes #801
Fixes #1384
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; |