Skip to content

Commit 6d23472

Browse files
committed
Support tint list on GifDrawable.
The Java doc of `Drawable#setColorFilter(ColorFilter)` says that "non-null color filter disables tint.". https://developer.android.com/reference/android/graphics/drawable/Drawable#setColorFilter(android.graphics.ColorFilter) This change sets the tint color filter by `draw()` to align with the document. This behavior is the same as the `ColorDrawable`.
1 parent 3bbc390 commit 6d23472

File tree

2 files changed

+107
-8
lines changed

2 files changed

+107
-8
lines changed

library/src/main/java/com/bumptech/glide/load/resource/gif/GifDrawable.java

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,16 @@
33
import static com.bumptech.glide.gifdecoder.GifDecoder.TOTAL_ITERATION_COUNT_FOREVER;
44

55
import android.content.Context;
6+
import android.content.res.ColorStateList;
67
import android.content.res.Resources;
78
import android.graphics.Bitmap;
89
import android.graphics.Canvas;
10+
import android.graphics.Color;
911
import android.graphics.ColorFilter;
1012
import android.graphics.Paint;
1113
import android.graphics.PixelFormat;
14+
import android.graphics.PorterDuff;
15+
import android.graphics.PorterDuffColorFilter;
1216
import android.graphics.Rect;
1317
import android.graphics.drawable.Animatable;
1418
import android.graphics.drawable.Drawable;
@@ -66,6 +70,9 @@ public class GifDrawable extends Drawable
6670

6771
private boolean applyGravity;
6872
private Paint paint;
73+
private ColorStateList tint;
74+
private PorterDuff.Mode tintMode;
75+
private ColorFilter tintFilter;
6976
private Rect destRect;
7077

7178
/** Callbacks to notify loop completion of a gif, where the loop count is explicitly specified. */
@@ -288,7 +295,17 @@ public void draw(@NonNull Canvas canvas) {
288295
}
289296

290297
Bitmap currentFrame = state.frameLoader.getCurrentFrame();
291-
canvas.drawBitmap(currentFrame, null, getDestRect(), getPaint());
298+
Paint paint = getPaint();
299+
ColorFilter colorFilter = paint.getColorFilter();
300+
if (colorFilter != null || tintFilter == null) {
301+
// ColorFilter disables tint list. See Drawable#setColorFilter().
302+
canvas.drawBitmap(currentFrame, null, getDestRect(), paint);
303+
} else {
304+
// Temporary set a tint filter then restore.
305+
paint.setColorFilter(tintFilter);
306+
canvas.drawBitmap(currentFrame, null, getDestRect(), paint);
307+
paint.setColorFilter(colorFilter);
308+
}
292309
}
293310

294311
@Override
@@ -298,7 +315,47 @@ public void setAlpha(int i) {
298315

299316
@Override
300317
public void setColorFilter(ColorFilter colorFilter) {
301-
getPaint().setColorFilter(colorFilter);
318+
if (getColorFilter() != colorFilter) {
319+
getPaint().setColorFilter(colorFilter);
320+
invalidateSelf();
321+
}
322+
}
323+
324+
@Override
325+
public ColorFilter getColorFilter() {
326+
return getPaint().getColorFilter();
327+
}
328+
329+
@Override
330+
public void setTintList(ColorStateList tint) {
331+
this.tint = tint;
332+
updateTintFilter();
333+
invalidateSelf();
334+
}
335+
336+
@Override
337+
public void setTintMode(PorterDuff.Mode tintMode) {
338+
this.tintMode = tintMode;
339+
updateTintFilter();
340+
invalidateSelf();
341+
}
342+
343+
@Override
344+
protected boolean onStateChange(int[] stateSet) {
345+
if (tint != null && tintMode != null) {
346+
updateTintFilter();
347+
return true;
348+
}
349+
return false;
350+
}
351+
352+
private void updateTintFilter() {
353+
if (tint != null && tintMode != null) {
354+
int color = tint.getColorForState(getState(), Color.TRANSPARENT);
355+
tintFilter = new PorterDuffColorFilter(color, tintMode);
356+
} else {
357+
tintFilter = null;
358+
}
302359
}
303360

304361
private Rect getDestRect() {

library/test/src/test/java/com/bumptech/glide/load/resource/gif/GifDrawableTest.java

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import static org.junit.Assert.assertFalse;
77
import static org.junit.Assert.assertNotNull;
88
import static org.junit.Assert.assertTrue;
9+
import static org.mockito.ArgumentMatchers.any;
910
import static org.mockito.ArgumentMatchers.eq;
1011
import static org.mockito.ArgumentMatchers.isA;
1112
import static org.mockito.ArgumentMatchers.isNull;
@@ -16,6 +17,7 @@
1617
import static org.mockito.Mockito.when;
1718

1819
import android.app.Application;
20+
import android.content.res.ColorStateList;
1921
import android.graphics.Bitmap;
2022
import android.graphics.Canvas;
2123
import android.graphics.Color;
@@ -40,7 +42,6 @@
4042
import org.junit.Rule;
4143
import org.junit.Test;
4244
import org.junit.runner.RunWith;
43-
import org.mockito.ArgumentCaptor;
4445
import org.mockito.Mock;
4546
import org.mockito.MockitoAnnotations;
4647
import org.robolectric.RobolectricTestRunner;
@@ -568,12 +569,53 @@ public void testSetAlphaSetsAlphaOnPaint() {
568569
public void testSetColorFilterSetsColorFilterOnPaint() {
569570
ColorFilter colorFilter = new PorterDuffColorFilter(Color.RED, Mode.ADD);
570571
drawable.setColorFilter(colorFilter);
572+
verify(paint).setColorFilter(eq(colorFilter));
573+
}
574+
575+
@Config(sdk = Build.VERSION_CODES.LOLLIPOP)
576+
@Test
577+
public void testDrawSetsTintListColorFilterOnPaint() {
578+
ColorStateList tint =
579+
new ColorStateList(
580+
new int[][] {new int[] {android.R.attr.state_pressed}, new int[0]},
581+
new int[] {Color.RED, Color.GREEN});
582+
drawable.setTintList(tint);
583+
drawable.setTintMode(Mode.ADD);
584+
when(paint.getColorFilter()).thenReturn(null);
585+
drawable.draw(new Canvas());
586+
587+
// draw() temporary sets tint filter then restore.
588+
verify(paint).setColorFilter(eq(new PorterDuffColorFilter(Color.GREEN, Mode.ADD)));
589+
verify(paint).setColorFilter(null);
590+
591+
assertThat(drawable.setState(new int[] {android.R.attr.state_pressed})).isTrue();
592+
drawable.draw(new Canvas());
593+
594+
// Pressed state. draw() temporary sets a red color filter.
595+
verify(paint).setColorFilter(eq(new PorterDuffColorFilter(Color.RED, Mode.ADD)));
596+
verify(paint, times(2)).setColorFilter(null);
597+
}
598+
599+
@Config(sdk = Build.VERSION_CODES.LOLLIPOP)
600+
@Test
601+
public void testDrawUsesColorFilterInsteadOfTintList() {
602+
ColorStateList tint =
603+
new ColorStateList(
604+
new int[][] {new int[] {android.R.attr.state_pressed}, new int[0]},
605+
new int[] {Color.RED, Color.GREEN});
606+
drawable.setTintList(tint);
607+
drawable.setTintMode(Mode.ADD);
608+
ColorFilter colorFilter = new PorterDuffColorFilter(Color.BLUE, Mode.ADD);
609+
drawable.setColorFilter(colorFilter);
610+
verify(paint).setColorFilter(eq(colorFilter));
611+
when(paint.getColorFilter()).thenReturn(colorFilter);
612+
613+
drawable.draw(new Canvas());
614+
drawable.onStateChange(new int[] {android.R.attr.state_pressed});
615+
drawable.draw(new Canvas());
571616

572-
// Use ArgumentCaptor instead of eq() due to b/73121412 where ShadowPorterDuffColorFilter.equals
573-
// uses a method that can't be found (PorterDuffColorFilter.getColor).
574-
ArgumentCaptor<ColorFilter> captor = ArgumentCaptor.forClass(ColorFilter.class);
575-
verify(paint).setColorFilter(captor.capture());
576-
assertThat(captor.getValue()).isSameInstanceAs(colorFilter);
617+
// ColorFilter disables tint list, so draw() should not invoke setColorFilter() any more.
618+
verify(paint).setColorFilter(any());
577619
}
578620

579621
@Test

0 commit comments

Comments
 (0)