// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.util;
import android.content.res.Resources;
import android.graphics.Color;
import org.chromium.base.ApiCompatibilityUtils;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ntp.NewTabPage;
import org.chromium.chrome.browser.ntp.NtpStyleUtils;
import org.chromium.chrome.browser.tab.Tab;
/**
* Helper functions for working with colors.
*/
public class ColorUtils {
private static final float CONTRAST_LIGHT_ITEM_THRESHOLD = 3f;
private static final float LIGHTNESS_OPAQUE_BOX_THRESHOLD = 0.82f;
private static final float LOCATION_BAR_TRANSPARENT_BACKGROUND_ALPHA = 0.2f;
private static final float MAX_LUMINANCE_FOR_VALID_THEME_COLOR = 0.94f;
private static final float THEMED_FOREGROUND_BLACK_FRACTION = 0.64f;
/** Percentage to darken a color by when setting the status bar color. */
private static final float DARKEN_COLOR_FRACTION = 0.6f;
/**
* Computes the lightness value in HSL standard for the given color.
*/
public static float getLightnessForColor(int color) {
int red = Color.red(color);
int green = Color.green(color);
int blue = Color.blue(color);
int largest = Math.max(red, Math.max(green, blue));
int smallest = Math.min(red, Math.min(green, blue));
int average = (largest + smallest) / 2;
return average / 255.0f;
}
/** Calculates the contrast between the given color and white, using the algorithm provided by
* the WCAG v2 in http://www.w3.org/TR/WCAG20/#contrast-ratiodef.
*/
private static float getContrastForColor(int color) {
float bgR = Color.red(color) / 255f;
float bgG = Color.green(color) / 255f;
float bgB = Color.blue(color) / 255f;
bgR = (bgR < 0.03928f) ? bgR / 12.92f : (float) Math.pow((bgR + 0.055f) / 1.055f, 2.4f);
bgG = (bgG < 0.03928f) ? bgG / 12.92f : (float) Math.pow((bgG + 0.055f) / 1.055f, 2.4f);
bgB = (bgB < 0.03928f) ? bgB / 12.92f : (float) Math.pow((bgB + 0.055f) / 1.055f, 2.4f);
float bgL = 0.2126f * bgR + 0.7152f * bgG + 0.0722f * bgB;
return Math.abs((1.05f) / (bgL + 0.05f));
}
/**
* @return The base color for the textbox given a toolbar background color.
*/
public static int getTextBoxColorForToolbarBackground(Resources res, Tab tab, int color) {
if (shouldUseOpaqueTextboxBackground(color)) {
// NTP should have no visible textbox in the toolbar, so just return the toolbar's
// background color.
if (tab.getNativePage() instanceof NewTabPage) {
return NtpStyleUtils.getToolbarBackgroundColorResource(res);
}
return Color.WHITE;
}
return getColorWithOverlay(color, Color.WHITE, LOCATION_BAR_TRANSPARENT_BACKGROUND_ALPHA);
}
/**
* @return Alpha for the textbox given a Tab.
*/
public static float getTextBoxAlphaForToolbarBackground(Tab tab) {
int color = tab.getThemeColor();
if (tab.getNativePage() instanceof NewTabPage) {
if (((NewTabPage) tab.getNativePage()).isLocationBarShownInNTP()) return 0f;
}
return shouldUseOpaqueTextboxBackground(color)
? 1f : LOCATION_BAR_TRANSPARENT_BACKGROUND_ALPHA;
}
/**
* Get a color when overlayed with a different color.
* @param baseColor The base Android color.
* @param overlayColor The overlay Android color.
* @param overlayAlpha The alpha |overlayColor| should have on the base color.
*/
public static int getColorWithOverlay(int baseColor, int overlayColor, float overlayAlpha) {
return Color.rgb(
(int) MathUtils.interpolate(Color.red(baseColor), Color.red(overlayColor),
overlayAlpha),
(int) MathUtils.interpolate(Color.green(baseColor), Color.green(overlayColor),
overlayAlpha),
(int) MathUtils.interpolate(Color.blue(baseColor), Color.blue(overlayColor),
overlayAlpha));
}
/**
* Darkens the given color to use on the status bar.
* @param color Color which should be darkened.
* @return Color that should be used for Android status bar.
*/
public static int getDarkenedColorForStatusBar(int color) {
return getDarkenedColor(color, DARKEN_COLOR_FRACTION);
}
/**
* Darken a color to a fraction of its current brightness.
* @param color The input color.
* @param darkenFraction The fraction of the current brightness the color should be.
* @return The new darkened color.
*/
public static int getDarkenedColor(int color, float darkenFraction) {
float[] hsv = new float[3];
Color.colorToHSV(color, hsv);
hsv[2] *= darkenFraction;
return Color.HSVToColor(hsv);
}
/**
* Check whether lighter or darker foreground elements (i.e. text, drawables etc.)
* should be used depending on the given background color.
* @param backgroundColor The background color value which is being queried.
* @return Whether light colored elements should be used.
*/
public static boolean shouldUseLightForegroundOnBackground(int backgroundColor) {
return getContrastForColor(backgroundColor) >= CONTRAST_LIGHT_ITEM_THRESHOLD;
}
/**
* Check which version of the textbox background should be used depending on the given
* color.
* @param color The color value we are querying for.
* @return Whether the transparent version of the background should be used.
*/
public static boolean shouldUseOpaqueTextboxBackground(int color) {
return getLightnessForColor(color) > LIGHTNESS_OPAQUE_BOX_THRESHOLD;
}
/**
* Returns an opaque version of the given color.
* @param color Color for which an opaque version should be returned.
* @return Opaque version of the given color.
*/
public static int getOpaqueColor(int color) {
return Color.rgb(Color.red(color), Color.green(color), Color.blue(color));
}
/**
* Test if the toolbar is using the default color.
* @param resources The resources to get the toolbar primary color.
* @param color The color that the toolbar is using.
* @return If the color is the default toolbar color.
*/
public static boolean isUsingDefaultToolbarColor(Resources resources, int color) {
return color == ApiCompatibilityUtils.getColor(resources, R.color.default_primary_color);
}
/**
* Determine if a theme color is valid. A theme color is invalid if its luminance is > 0.94.
* @param color The color to test.
* @return True if the theme color is valid.
*/
public static boolean isValidThemeColor(int color) {
return ColorUtils.getLightnessForColor(color) <= MAX_LUMINANCE_FOR_VALID_THEME_COLOR;
}
/**
* Compute a color to use for assets that sit on top of a themed background.
* @param themeColor The base theme color.
* @return A color to use for elements in the foreground (on top of the base theme color).
*/
public static int getThemedAssetColor(int themeColor, boolean isIncognito) {
if (ColorUtils.shouldUseLightForegroundOnBackground(themeColor) || isIncognito) {
// Dark theme.
return Color.WHITE;
} else {
// Light theme.
return ColorUtils.getColorWithOverlay(themeColor, Color.BLACK,
THEMED_FOREGROUND_BLACK_FRACTION);
}
}
}