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

Apply blend modes on layer level and add Multiply blend mode #2519

Merged
merged 4 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -11,7 +11,6 @@
import android.graphics.RectF;

import androidx.annotation.Nullable;
import androidx.core.graphics.PaintCompat;

import com.airbnb.lottie.L;
import com.airbnb.lottie.LottieDrawable;
Expand Down Expand Up @@ -68,8 +67,6 @@ public FillContent(final LottieDrawable lottieDrawable, BaseLayer layer, ShapeFi
return;
}

PaintCompat.setBlendMode(paint, layer.getBlendMode().toNativeBlendMode());

path.setFillType(fill.getFillType());

colorAnimation = fill.getColor().createAnimation();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,20 @@ public BlendModeCompat toNativeBlendMode() {
switch (this) {
case NORMAL:
return null;
case MULTIPLY:
// BlendModeCompat.MULTIPLY does not exist on Android < Q. Instead, there's
// BlendModeCompat.MODULATE, which maps to PorterDuff.Mode.MODULATE and not
// PorterDuff.Mode.MULTIPLY.
//
// MODULATE differs from MULTIPLY in that it doesn't perform
// any alpha blending. It just does a component-wise multiplication
// of the colors.
//
// For proper results on all platforms, we will map the MULTIPLY
// blend mode to MODULATE, and then do a slight adjustment to
// how we render such layers to still achieve the correct result.
// See BaseLayer.draw().
return BlendModeCompat.MODULATE;
case SCREEN:
return BlendModeCompat.SCREEN;
case OVERLAY:
Expand All @@ -48,7 +62,6 @@ public BlendModeCompat toNativeBlendMode() {
// To prevent unexpected issues where animations look correct
// during development but silently break for users with older devices
// we won't support any of these until Q is widely used.
case MULTIPLY:
case COLOR_DODGE:
case COLOR_BURN:
case HARD_LIGHT:
Expand Down
25 changes: 23 additions & 2 deletions lottie/src/main/java/com/airbnb/lottie/model/layer/BaseLayer.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import androidx.annotation.CallSuper;
import androidx.annotation.FloatRange;
import androidx.annotation.Nullable;
import androidx.core.graphics.PaintCompat;

import com.airbnb.lottie.L;
import com.airbnb.lottie.LottieComposition;
Expand Down Expand Up @@ -117,6 +118,8 @@ static BaseLayer forModel(
float blurMaskFilterRadius = 0f;
@Nullable BlurMaskFilter blurMaskFilter;

@Nullable LPaint solidWhitePaint;

BaseLayer(LottieDrawable lottieDrawable, Layer layerModel) {
this.lottieDrawable = lottieDrawable;
this.layerModel = layerModel;
Expand Down Expand Up @@ -258,7 +261,7 @@ public void draw(Canvas canvas, Matrix parentMatrix, int parentAlpha) {
}
}
int alpha = (int) ((parentAlpha / 255f * (float) opacity / 100f) * 255);
if (!hasMatteOnThisLayer() && !hasMasksOnThisLayer()) {
if (!hasMatteOnThisLayer() && !hasMasksOnThisLayer() && getBlendMode() == LBlendMode.NORMAL) {
matrix.preConcat(transform.getMatrix());
if (L.isTraceEnabled()) {
L.beginSection("Layer#drawLayer");
Expand Down Expand Up @@ -307,13 +310,31 @@ public void draw(Canvas canvas, Matrix parentMatrix, int parentAlpha) {
L.beginSection("Layer#saveLayer");
}
contentPaint.setAlpha(255);
PaintCompat.setBlendMode(contentPaint, getBlendMode().toNativeBlendMode());
Utils.saveLayerCompat(canvas, rect, contentPaint);
if (L.isTraceEnabled()) {
L.endSection("Layer#saveLayer");
}

// Clear the off screen buffer. This is necessary for some phones.
clearCanvas(canvas);
if (getBlendMode() != LBlendMode.MULTIPLY) {
clearCanvas(canvas);
} else {
// Due to the difference between PorterDuffMode.MULTIPLY (which we use for compatibility
// with Android < Q) and BlendMode.MULTIPLY (which is the correct, alpha-blended mode),
// we will alpha-blend the contents of this layer on top of a white background before
// we multiply it with the opaque substrate below (with canvas.restore()).
//
// Since white is the identity color for multiplication, this will behave as if we
// had correctly performed an alpha-blended multiply (such as BlendMode.MULTIPLY), but
// will work pre-Q as well.
if (solidWhitePaint == null) {
solidWhitePaint = new LPaint();
solidWhitePaint.setColor(0xffffffff);
}
canvas.drawRect(rect.left - 1, rect.top - 1, rect.right + 1, rect.bottom + 1, solidWhitePaint);
}

if (L.isTraceEnabled()) {
L.beginSection("Layer#drawLayer");
}
Expand Down
Loading