From 601c1d9489933fc8a524b65c179bfcac88d1ee84 Mon Sep 17 00:00:00 2001 From: Karim Abou Zeid Date: Wed, 23 Sep 2015 16:39:27 +0200 Subject: [PATCH] Replaced the algorithm for determining whether two colors are distinguishable. Merged some duplicated code into its own methods. --- .../base/AbsSlidingMusicPanelActivity.java | 43 +++++---- .../kabouzeid/gramophone/util/ColorUtil.java | 91 +++++++++++++++++-- app/src/main/res/values/colors.xml | 2 +- app/src/main/res/values/styles_parents.xml | 2 +- 4 files changed, 109 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsSlidingMusicPanelActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsSlidingMusicPanelActivity.java index 99d26cf4..f859c998 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsSlidingMusicPanelActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsSlidingMusicPanelActivity.java @@ -722,16 +722,12 @@ public abstract class AbsSlidingMusicPanelActivity extends AbsMusicServiceActivi private void updateShuffleState() { switch (MusicPlayerRemote.getShuffleMode()) { case MusicService.SHUFFLE_MODE_SHUFFLE: - int activatedColor = colorPlaybackControls - ? getFixedShuffleRepeatButtonColor(lastPlaybackControlsColor) - : ThemeSingleton.get().positiveColor.getDefaultColor(); shuffleButton.setImageDrawable(Util.getTintedDrawable(this, R.drawable.ic_shuffle_white_36dp, - activatedColor)); + getActivatedIconColor())); break; default: - int deactivatedColor = ColorUtil.resolveColor(this, android.R.attr.textColorSecondary); shuffleButton.setImageDrawable(Util.getTintedDrawable(this, R.drawable.ic_shuffle_white_36dp, - deactivatedColor)); + getDeactivatedIconColor())); break; } } @@ -747,34 +743,43 @@ public abstract class AbsSlidingMusicPanelActivity extends AbsMusicServiceActivi } private void updateRepeatState() { - int activatedColor = colorPlaybackControls - ? getFixedShuffleRepeatButtonColor(lastPlaybackControlsColor) - : ThemeSingleton.get().positiveColor.getDefaultColor(); switch (MusicPlayerRemote.getRepeatMode()) { case MusicService.REPEAT_MODE_ALL: repeatButton.setImageDrawable(Util.getTintedDrawable(this, R.drawable.ic_repeat_white_36dp, - activatedColor)); + getActivatedIconColor())); break; case MusicService.REPEAT_MODE_THIS: repeatButton.setImageDrawable(Util.getTintedDrawable(this, R.drawable.ic_repeat_one_white_36dp, - activatedColor)); + getActivatedIconColor())); break; default: - int deactivatedColor = ColorUtil.resolveColor(this, android.R.attr.textColorSecondary); repeatButton.setImageDrawable(Util.getTintedDrawable(this, R.drawable.ic_repeat_white_36dp, - deactivatedColor)); + getDeactivatedIconColor())); break; } } + private int getActivatedIconColor() { + if (colorPlaybackControls) { + return ensureActivatedColorVisibleIfNecessary(lastPlaybackControlsColor); + } else { + return ThemeSingleton.get().positiveColor.getDefaultColor(); + } + } + + private int getDeactivatedIconColor() { + return ColorUtil.resolveColor(this, android.R.attr.textColorSecondary); + } + /** - * Checks whether the default color and the activated color are similar. If true, returns a darker - * activated color. Else, returns the given color as-is. + * @return If the activated color wont have enough difference to the deactivated color Color.WHITE / Color.BLACK (depending on the theme), + * else the unmodified accentColor. */ - private int getFixedShuffleRepeatButtonColor(int activatedColor) { - if (ColorUtil.calculateColorDistance(activatedColor, - ColorUtil.resolveColor(this, android.R.attr.textColorSecondary))) { - return ColorUtil.shiftColor(activatedColor, 0.6f); + private int ensureActivatedColorVisibleIfNecessary(int activatedColor) { + // Not optimal, but much easier then computing the opaque deactivated color on the background color every time. + int preBlendedDeactivatedIconColor = ThemeSingleton.get().darkTheme ? Color.argb(255, 188, 188, 188) : Color.argb(255, 115, 115, 115); + if (ColorUtil.getColorDifference(activatedColor, preBlendedDeactivatedIconColor) <= 30d) { + return ThemeSingleton.get().darkTheme ? Color.WHITE : Color.BLACK; } return activatedColor; } diff --git a/app/src/main/java/com/kabouzeid/gramophone/util/ColorUtil.java b/app/src/main/java/com/kabouzeid/gramophone/util/ColorUtil.java index 0945c2fe..dfa1e703 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/util/ColorUtil.java +++ b/app/src/main/java/com/kabouzeid/gramophone/util/ColorUtil.java @@ -138,16 +138,91 @@ public class ColorUtil { return backgroundColor; } + public static int[] rgb2lab(int R, int G, int B) { + //http://www.brucelindbloom.com + + float r, g, b, X, Y, Z, fx, fy, fz, xr, yr, zr; + float Ls, as, bs; + float eps = 216.f / 24389.f; + float k = 24389.f / 27.f; + + float Xr = 0.964221f; // reference white D50 + float Yr = 1.0f; + float Zr = 0.825211f; + + // RGB to XYZ + r = R / 255.f; //R 0..1 + g = G / 255.f; //G 0..1 + b = B / 255.f; //B 0..1 + + // assuming sRGB (D65) + if (r <= 0.04045) + r = r / 12; + else + r = (float) Math.pow((r + 0.055) / 1.055, 2.4); + + if (g <= 0.04045) + g = g / 12; + else + g = (float) Math.pow((g + 0.055) / 1.055, 2.4); + + if (b <= 0.04045) + b = b / 12; + else + b = (float) Math.pow((b + 0.055) / 1.055, 2.4); + + + X = 0.436052025f * r + 0.385081593f * g + 0.143087414f * b; + Y = 0.222491598f * r + 0.71688606f * g + 0.060621486f * b; + Z = 0.013929122f * r + 0.097097002f * g + 0.71418547f * b; + + // XYZ to Lab + xr = X / Xr; + yr = Y / Yr; + zr = Z / Zr; + + if (xr > eps) + fx = (float) Math.pow(xr, 1 / 3.); + else + fx = (float) ((k * xr + 16.) / 116.); + + if (yr > eps) + fy = (float) Math.pow(yr, 1 / 3.); + else + fy = (float) ((k * yr + 16.) / 116.); + + if (zr > eps) + fz = (float) Math.pow(zr, 1 / 3.); + else + fz = (float) ((k * zr + 16.) / 116); + + Ls = (116 * fy) - 16; + as = 500 * (fx - fy); + bs = 200 * (fy - fz); + + int[] lab = new int[3]; + lab[0] = (int) (2.55 * Ls + .5); + lab[1] = (int) (as + .5); + lab[2] = (int) (bs + .5); + return lab; + } + /** - * Calculates the distance of two colors in 3D Space. + * Computes the difference between two RGB colors by converting them to the L*a*b scale and + * comparing them using the CIE76 algorithm { http://en.wikipedia.org/wiki/Color_difference#CIE76} * - * Taken from http://stackoverflow.com/a/1725535/984061 + * @return > 23 corresponds to a JND (just noticeable difference) */ - public static boolean calculateColorDistance(int color1, int color2) { - double red = Math.pow(Math.abs(Color.red(color2) - Color.red(color1)), 2); - double green = Math.pow(Math.abs(Color.green(color2) - Color.green(color1)), 2); - double blue = Math.pow(Math.abs(Color.blue(color2) - Color.blue(color1)), 2); - double distance = red + green + blue; - return distance < 55000; + public static double getColorDifference(int a, int b) { + int r1, g1, b1, r2, g2, b2; + r1 = Color.red(a); + g1 = Color.green(a); + b1 = Color.blue(a); + r2 = Color.red(b); + g2 = Color.green(b); + b2 = Color.blue(b); + int[] lab1 = rgb2lab(r1, g1, b1); + int[] lab2 = rgb2lab(r2, g2, b2); + return Math.sqrt(Math.pow(lab2[0] - lab1[0], 2) + Math.pow(lab2[1] - lab1[1], 2) + Math.pow(lab2[2] - lab1[2], 2)); } } diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index cb438b07..8ae95aff 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -4,7 +4,7 @@ #ff4081 #3f51b5 - #424242 + #616161 #212121 \ No newline at end of file diff --git a/app/src/main/res/values/styles_parents.xml b/app/src/main/res/values/styles_parents.xml index f3082c16..af03a3be 100644 --- a/app/src/main/res/values/styles_parents.xml +++ b/app/src/main/res/values/styles_parents.xml @@ -43,7 +43,7 @@ @color/cardview_light_background - @color/grey_800 + @color/grey_700 @color/md_divider_black @color/secondary_text_default_material_light