Skip to content

Commit

Permalink
Added support for drop shadows (#1860)
Browse files Browse the repository at this point in the history
  • Loading branch information
gpeal authored Aug 17, 2021
1 parent ea36953 commit 7d80f43
Show file tree
Hide file tree
Showing 15 changed files with 388 additions and 15 deletions.
33 changes: 33 additions & 0 deletions lottie/src/main/java/com/airbnb/lottie/LottieProperty.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public interface LottieProperty {
* [0,100]
*/
Integer OPACITY = 4;
Integer DROP_SHADOW_COLOR = 5;
/**
* In Px
*/
Expand Down Expand Up @@ -167,6 +168,38 @@ public interface LottieProperty {
* In Dp
*/
Float TEXT_SIZE = 14f;
/**
* [0,100]
* Lottie Android resolved drop shadows on drawing content such as fills and strokes.
* If a drop shadow is applied to a layer, the dynamic properties must be set on all
* of its child elements that draw. The easiest way to do this is to append "**" to your
* Keypath after the layer name.
*/
Float DROP_SHADOW_OPACITY = 15f;
/**
* Degrees from 12 o'clock.
* Lottie Android resolved drop shadows on drawing content such as fills and strokes.
* If a drop shadow is applied to a layer, the dynamic properties must be set on all
* of its child elements that draw. The easiest way to do this is to append "**" to your
* Keypath after the layer name.
*/
Float DROP_SHADOW_DIRECTION = 16f;
/**
* In Px
* Lottie Android resolved drop shadows on drawing content such as fills and strokes.
* If a drop shadow is applied to a layer, the dynamic properties must be set on all
* of its child elements that draw. The easiest way to do this is to append "**" to your
* Keypath after the layer name.
*/
Float DROP_SHADOW_DISTANCE = 17f;
/**
* In Px
* Lottie Android resolved drop shadows on drawing content such as fills and strokes.
* If a drop shadow is applied to a layer, the dynamic properties must be set on all
* of its child elements that draw. The easiest way to do this is to append "**" to your
* Keypath after the layer name.
*/
Float DROP_SHADOW_RADIUS = 18f;

ColorFilter COLOR_FILTER = new ColorFilter();
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.airbnb.lottie.LottieProperty;
import com.airbnb.lottie.animation.LPaint;
import com.airbnb.lottie.animation.keyframe.BaseKeyframeAnimation;
import com.airbnb.lottie.animation.keyframe.DropShadowKeyframeAnimation;
import com.airbnb.lottie.animation.keyframe.FloatKeyframeAnimation;
import com.airbnb.lottie.animation.keyframe.IntegerKeyframeAnimation;
import com.airbnb.lottie.animation.keyframe.ValueCallbackKeyframeAnimation;
Expand Down Expand Up @@ -57,6 +58,8 @@ public abstract class BaseStrokeContent
@Nullable private BaseKeyframeAnimation<Float, Float> blurAnimation;
float blurMaskFilterRadius = 0f;

@Nullable private DropShadowKeyframeAnimation dropShadowAnimation;

BaseStrokeContent(final LottieDrawable lottieDrawable, BaseLayer layer, Paint.Cap cap,
Paint.Join join, float miterLimit, AnimatableIntegerValue opacity, AnimatableFloatValue width,
List<AnimatableFloatValue> dashPattern, AnimatableFloatValue offset) {
Expand Down Expand Up @@ -102,13 +105,14 @@ public abstract class BaseStrokeContent
dashPatternOffsetAnimation.addUpdateListener(this);
}

if (layer.getBlurEffect() == null) {
blurAnimation = null;
} else {
if (layer.getBlurEffect() != null) {
blurAnimation = layer.getBlurEffect().getBlurriness().createAnimation();
blurAnimation.addUpdateListener(this);
layer.addAnimation(blurAnimation);
}
if (layer.getDropShadowEffect() != null) {
dropShadowAnimation = new DropShadowKeyframeAnimation(this, layer, layer.getDropShadowEffect());
}
}

@Override public void onValueChanged() {
Expand Down Expand Up @@ -180,6 +184,9 @@ public abstract class BaseStrokeContent
}
blurMaskFilterRadius = blurRadius;
}
if (dropShadowAnimation != null) {
dropShadowAnimation.applyTo(paint);
}

for (int i = 0; i < pathGroups.size(); i++) {
PathGroup pathGroup = pathGroups.get(i);
Expand Down Expand Up @@ -359,6 +366,16 @@ public <T> void addValueCallback(T property, @Nullable LottieValueCallback<T> ca
blurAnimation.addUpdateListener(this);
layer.addAnimation(blurAnimation);
}
} else if (property == LottieProperty.DROP_SHADOW_COLOR && dropShadowAnimation != null) {
dropShadowAnimation.setColorCallback((LottieValueCallback<Integer>) callback);
} else if (property == LottieProperty.DROP_SHADOW_OPACITY && dropShadowAnimation != null) {
dropShadowAnimation.setOpacityCallback((LottieValueCallback<Float>) callback);
} else if (property == LottieProperty.DROP_SHADOW_DIRECTION && dropShadowAnimation != null) {
dropShadowAnimation.setDirectionCallback((LottieValueCallback<Float>) callback);
} else if (property == LottieProperty.DROP_SHADOW_DISTANCE && dropShadowAnimation != null) {
dropShadowAnimation.setDistanceCallback((LottieValueCallback<Float>) callback);
} else if (property == LottieProperty.DROP_SHADOW_RADIUS && dropShadowAnimation != null) {
dropShadowAnimation.setRadiusCallback((LottieValueCallback<Float>) callback);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import com.airbnb.lottie.animation.LPaint;
import com.airbnb.lottie.animation.keyframe.BaseKeyframeAnimation;
import com.airbnb.lottie.animation.keyframe.ColorKeyframeAnimation;
import com.airbnb.lottie.animation.keyframe.FloatKeyframeAnimation;
import com.airbnb.lottie.animation.keyframe.DropShadowKeyframeAnimation;
import com.airbnb.lottie.animation.keyframe.ValueCallbackKeyframeAnimation;
import com.airbnb.lottie.model.KeyPath;
import com.airbnb.lottie.model.content.ShapeFill;
Expand All @@ -44,18 +44,21 @@ public class FillContent
@Nullable private BaseKeyframeAnimation<Float, Float> blurAnimation;
float blurMaskFilterRadius;

@Nullable private DropShadowKeyframeAnimation dropShadowAnimation;

public FillContent(final LottieDrawable lottieDrawable, BaseLayer layer, ShapeFill fill) {
this.layer = layer;
name = fill.getName();
hidden = fill.isHidden();
this.lottieDrawable = lottieDrawable;
if (layer.getBlurEffect() == null) {
blurAnimation = null;
} else {
if (layer.getBlurEffect() != null) {
blurAnimation = layer.getBlurEffect().getBlurriness().createAnimation();
blurAnimation.addUpdateListener(this);
layer.addAnimation(blurAnimation);
}
if (layer.getDropShadowEffect() != null) {
dropShadowAnimation = new DropShadowKeyframeAnimation(this, layer, layer.getDropShadowEffect());
}

if (fill.getColor() == null || fill.getOpacity() == null) {
colorAnimation = null;
Expand Down Expand Up @@ -107,12 +110,15 @@ public FillContent(final LottieDrawable lottieDrawable, BaseLayer layer, ShapeFi
float blurRadius = blurAnimation.getValue();
if (blurRadius == 0f) {
paint.setMaskFilter(null);
} else if (blurRadius != blurMaskFilterRadius){
} else if (blurRadius != blurMaskFilterRadius) {
BlurMaskFilter blur = layer.getBlurMaskFilter(blurRadius);
paint.setMaskFilter(blur);
}
blurMaskFilterRadius = blurRadius;
}
if (dropShadowAnimation != null) {
dropShadowAnimation.applyTo(paint);
}

path.reset();
for (int i = 0; i < paths.size(); i++) {
Expand Down Expand Up @@ -173,6 +179,16 @@ public <T> void addValueCallback(T property, @Nullable LottieValueCallback<T> ca
blurAnimation.addUpdateListener(this);
layer.addAnimation(blurAnimation);
}
} else if (property == LottieProperty.DROP_SHADOW_COLOR && dropShadowAnimation != null) {
dropShadowAnimation.setColorCallback((LottieValueCallback<Integer>) callback);
} else if (property == LottieProperty.DROP_SHADOW_OPACITY && dropShadowAnimation != null) {
dropShadowAnimation.setOpacityCallback((LottieValueCallback<Float>) callback);
} else if (property == LottieProperty.DROP_SHADOW_DIRECTION && dropShadowAnimation != null) {
dropShadowAnimation.setDirectionCallback((LottieValueCallback<Float>) callback);
} else if (property == LottieProperty.DROP_SHADOW_DISTANCE && dropShadowAnimation != null) {
dropShadowAnimation.setDistanceCallback((LottieValueCallback<Float>) callback);
} else if (property == LottieProperty.DROP_SHADOW_RADIUS && dropShadowAnimation != null) {
dropShadowAnimation.setRadiusCallback((LottieValueCallback<Float>) callback);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.airbnb.lottie.LottieProperty;
import com.airbnb.lottie.animation.LPaint;
import com.airbnb.lottie.animation.keyframe.BaseKeyframeAnimation;
import com.airbnb.lottie.animation.keyframe.DropShadowKeyframeAnimation;
import com.airbnb.lottie.animation.keyframe.FloatKeyframeAnimation;
import com.airbnb.lottie.animation.keyframe.ValueCallbackKeyframeAnimation;
import com.airbnb.lottie.model.KeyPath;
Expand Down Expand Up @@ -62,6 +63,7 @@ public class GradientFillContent
private final int cacheSteps;
@Nullable private BaseKeyframeAnimation<Float, Float> blurAnimation;
float blurMaskFilterRadius = 0f;
@Nullable private DropShadowKeyframeAnimation dropShadowAnimation;

public GradientFillContent(final LottieDrawable lottieDrawable, BaseLayer layer, GradientFill fill) {
this.layer = layer;
Expand All @@ -88,13 +90,14 @@ public GradientFillContent(final LottieDrawable lottieDrawable, BaseLayer layer,
endPointAnimation.addUpdateListener(this);
layer.addAnimation(endPointAnimation);

if (layer.getBlurEffect() == null) {
blurAnimation = null;
} else {
if (layer.getBlurEffect() != null) {
blurAnimation = layer.getBlurEffect().getBlurriness().createAnimation();
blurAnimation.addUpdateListener(this);
layer.addAnimation(blurAnimation);
}
if (layer.getDropShadowEffect() != null) {
dropShadowAnimation = new DropShadowKeyframeAnimation(this, layer, layer.getDropShadowEffect());
}
}

@Override public void onValueChanged() {
Expand Down Expand Up @@ -145,6 +148,9 @@ public GradientFillContent(final LottieDrawable lottieDrawable, BaseLayer layer,
}
blurMaskFilterRadius = blurRadius;
}
if (dropShadowAnimation != null) {
dropShadowAnimation.applyTo(paint);
}

int alpha = (int) ((parentAlpha / 255f * opacityAnimation.getValue() / 100f) * 255);
paint.setAlpha(clamp(alpha, 0, 255));
Expand Down Expand Up @@ -295,6 +301,16 @@ public <T> void addValueCallback(T property, @Nullable LottieValueCallback<T> ca
blurAnimation.addUpdateListener(this);
layer.addAnimation(blurAnimation);
}
} else if (property == LottieProperty.DROP_SHADOW_COLOR && dropShadowAnimation != null) {
dropShadowAnimation.setColorCallback((LottieValueCallback<Integer>) callback);
} else if (property == LottieProperty.DROP_SHADOW_OPACITY && dropShadowAnimation != null) {
dropShadowAnimation.setOpacityCallback((LottieValueCallback<Float>) callback);
} else if (property == LottieProperty.DROP_SHADOW_DIRECTION && dropShadowAnimation != null) {
dropShadowAnimation.setDirectionCallback((LottieValueCallback<Float>) callback);
} else if (property == LottieProperty.DROP_SHADOW_DISTANCE && dropShadowAnimation != null) {
dropShadowAnimation.setDistanceCallback((LottieValueCallback<Float>) callback);
} else if (property == LottieProperty.DROP_SHADOW_RADIUS && dropShadowAnimation != null) {
dropShadowAnimation.setRadiusCallback((LottieValueCallback<Float>) callback);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package com.airbnb.lottie.animation.keyframe;

import android.graphics.Color;
import android.graphics.Paint;

import androidx.annotation.Nullable;

import com.airbnb.lottie.model.layer.BaseLayer;
import com.airbnb.lottie.parser.DropShadowEffect;
import com.airbnb.lottie.value.LottieFrameInfo;
import com.airbnb.lottie.value.LottieValueCallback;

public class DropShadowKeyframeAnimation implements BaseKeyframeAnimation.AnimationListener {
private static final double DEG_TO_RAD = Math.PI / 180.0;

private final BaseKeyframeAnimation.AnimationListener listener;
private final BaseKeyframeAnimation<Integer, Integer> color;
private final BaseKeyframeAnimation<Float, Float> opacity;
private final BaseKeyframeAnimation<Float, Float> direction;
private final BaseKeyframeAnimation<Float, Float> distance;
private final BaseKeyframeAnimation<Float, Float> radius;

private boolean isDirty = true;

public DropShadowKeyframeAnimation(BaseKeyframeAnimation.AnimationListener listener, BaseLayer layer, DropShadowEffect dropShadowEffect) {
this.listener = listener;
color = dropShadowEffect.getColor().createAnimation();
color.addUpdateListener(this);
layer.addAnimation(color);
opacity = dropShadowEffect.getOpacity().createAnimation();
opacity.addUpdateListener(this);
layer.addAnimation(opacity);
direction = dropShadowEffect.getDirection().createAnimation();
direction.addUpdateListener(this);
layer.addAnimation(direction);
distance = dropShadowEffect.getDistance().createAnimation();
distance.addUpdateListener(this);
layer.addAnimation(distance);
radius = dropShadowEffect.getRadius().createAnimation();
radius.addUpdateListener(this);
layer.addAnimation(radius);
}

@Override public void onValueChanged() {
isDirty = true;
listener.onValueChanged();
}

public void applyTo(Paint paint) {
if (!isDirty) {
return;
}
isDirty = false;

double directionRad = ((double) direction.getValue()) * DEG_TO_RAD;
float distance = this.distance.getValue();
float x = ((float) Math.sin(directionRad)) * distance;
float y = ((float) Math.cos(directionRad + Math.PI)) * distance;
int baseColor = color.getValue();
int opacity = Math.round(this.opacity.getValue());
int color = Color.argb(opacity, Color.red(baseColor), Color.green(baseColor), Color.blue(baseColor));
float radius = this.radius.getValue();
paint.setShadowLayer(radius, x, y, color);
}

public void setColorCallback(@Nullable LottieValueCallback<Integer> callback) {
color.setValueCallback(callback);
}

public void setOpacityCallback(@Nullable final LottieValueCallback<Float> callback) {
if (callback == null) {
opacity.setValueCallback(null);
return;
}
opacity.setValueCallback(new LottieValueCallback<Float>() {
@Nullable
@Override
public Float getValue(LottieFrameInfo<Float> frameInfo) {
Float value = callback.getValue(frameInfo);
if (value == null) {
return null;
}
// Convert [0,100] to [0,255] because other dynamic properties use [0,100].
return value * 2.55f;
}
});
}

public void setDirectionCallback(@Nullable LottieValueCallback<Float> callback) {
direction.setValueCallback(callback);
}

public void setDistanceCallback(@Nullable LottieValueCallback<Float> callback) {
distance.setValueCallback(callback);
}

public void setRadiusCallback(@Nullable LottieValueCallback<Float> callback) {
radius.setValueCallback(callback);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import com.airbnb.lottie.model.content.BlurEffect;
import com.airbnb.lottie.model.content.Mask;
import com.airbnb.lottie.model.content.ShapeData;
import com.airbnb.lottie.parser.DropShadowEffect;
import com.airbnb.lottie.utils.Logger;
import com.airbnb.lottie.utils.Utils;
import com.airbnb.lottie.value.LottieValueCallback;
Expand Down Expand Up @@ -606,6 +607,11 @@ public BlurMaskFilter getBlurMaskFilter(float radius) {
return blurMaskFilter;
}

@Nullable
public DropShadowEffect getDropShadowEffect() {
return layerModel.getDropShadowEffect();
}

@Override
public void setContents(List<Content> contentsBefore, List<Content> contentsAfter) {
// Do nothing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class CompositionLayer extends BaseLayer {
private final List<BaseLayer> layers = new ArrayList<>();
private final RectF rect = new RectF();
private final RectF newClipRect = new RectF();
private Paint layerPaint = new Paint();
private final Paint layerPaint = new Paint();

@Nullable private Boolean hasMatte;
@Nullable private Boolean hasMasks;
Expand Down
Loading

0 comments on commit 7d80f43

Please sign in to comment.