Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve drop shadow effect accuracy #2523

Merged
merged 22 commits into from
Aug 4, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ public abstract class BaseStrokeContent
blurMaskFilterRadius = blurRadius;
}
if (dropShadowAnimation != null) {
dropShadowAnimation.applyTo(paint);
dropShadowAnimation.applyTo(paint, parentAlpha);
}

for (int i = 0; i < pathGroups.size(); i++) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public FillContent(final LottieDrawable lottieDrawable, BaseLayer layer, ShapeFi
blurMaskFilterRadius = blurRadius;
}
if (dropShadowAnimation != null) {
dropShadowAnimation.applyTo(paint);
dropShadowAnimation.applyTo(paint, parentAlpha);
}

path.reset();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ public GradientFillContent(final LottieDrawable lottieDrawable, LottieCompositio
blurMaskFilterRadius = blurRadius;
}
if (dropShadowAnimation != null) {
dropShadowAnimation.applyTo(paint);
dropShadowAnimation.applyTo(paint, parentAlpha);
}

int alpha = (int) ((parentAlpha / 255f * opacityAnimation.getValue() / 100f) * 255);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,22 @@
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;

// We scale the parsed distance and softness values by a constant factor so that the Paint.setShadowLayer() call
// gives results that more closely match After Effects
private static final float AFTER_EFFECTS_DISTANCE_SCALE_FACTOR = 1.33f;
private static final float AFTER_EFFECT_SOFTNESS_SCALE_FACTOR = 0.43f;

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();
Expand All @@ -42,24 +46,27 @@ public DropShadowKeyframeAnimation(BaseKeyframeAnimation.AnimationListener liste
}

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

public void applyTo(Paint paint) {
if (!isDirty) {
allenchen1154 marked this conversation as resolved.
Show resolved Hide resolved
return;
}
isDirty = false;

/**
* Applies a shadow to the provided Paint object, which will be applied to the Canvas behind whatever is drawn
* (a shape, bitmap, path, etc.)
* @param parentAlpha A value between 0 and 255 representing the combined alpha of all parents of this drop shadow effect.
* E.g. The layer via transform, the fill/stroke via its opacity, etc.
*/
public void applyTo(Paint paint, int parentAlpha) {
double directionRad = ((double) direction.getValue()) * DEG_TO_RAD;
float distance = this.distance.getValue();
float distance = this.distance.getValue() * AFTER_EFFECTS_DISTANCE_SCALE_FACTOR;
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 opacity = Math.round(this.opacity.getValue() * parentAlpha / 255f);
int color = Color.argb(opacity, Color.red(baseColor), Color.green(baseColor), Color.blue(baseColor));
float radius = this.radius.getValue();

// Paint.setShadowLayer() removes the shadow if radius is 0, so we use a small nonzero value in that case
float radius = Math.max(this.radius.getValue() * AFTER_EFFECT_SOFTNESS_SCALE_FACTOR, Float.MIN_VALUE);
paint.setShadowLayer(radius, x, y, color);
}

Expand Down
10 changes: 10 additions & 0 deletions lottie/src/main/java/com/airbnb/lottie/model/layer/ImageLayer.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,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.ValueCallbackKeyframeAnimation;
import com.airbnb.lottie.utils.Utils;
import com.airbnb.lottie.value.LottieValueCallback;
Expand All @@ -28,10 +29,15 @@ public class ImageLayer extends BaseLayer {
@Nullable private final LottieImageAsset lottieImageAsset;
@Nullable private BaseKeyframeAnimation<ColorFilter, ColorFilter> colorFilterAnimation;
@Nullable private BaseKeyframeAnimation<Bitmap, Bitmap> imageAnimation;
@Nullable private DropShadowKeyframeAnimation dropShadowAnimation;

ImageLayer(LottieDrawable lottieDrawable, Layer layerModel) {
super(lottieDrawable, layerModel);
lottieImageAsset = lottieDrawable.getLottieImageAssetForId(layerModel.getRefId());

if (getDropShadowEffect() != null) {
dropShadowAnimation = new DropShadowKeyframeAnimation(this, this, getDropShadowEffect());
}
}

@Override public void drawLayer(@NonNull Canvas canvas, Matrix parentMatrix, int parentAlpha) {
Expand All @@ -54,6 +60,10 @@ public class ImageLayer extends BaseLayer {
dst.set(0, 0, (int) (bitmap.getWidth() * density), (int) (bitmap.getHeight() * density));
}

if (dropShadowAnimation != null) {
dropShadowAnimation.applyTo(paint, parentAlpha);
}

canvas.drawBitmap(bitmap, src, dst, paint);
canvas.restore();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ private void maybeParseInnerEffect(JsonReader reader, LottieComposition composit
direction = AnimatableValueParser.parseFloat(reader, composition, false);
break;
case "Distance":
distance = AnimatableValueParser.parseFloat(reader, composition);
distance = AnimatableValueParser.parseFloat(reader, composition, false);
break;
case "Softness":
radius = AnimatableValueParser.parseFloat(reader, composition);
radius = AnimatableValueParser.parseFloat(reader, composition, false);
allenchen1154 marked this conversation as resolved.
Show resolved Hide resolved
break;
default:
reader.skipValue();
Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Loading
Loading