admin管理员组文章数量:1296311
I have a custom HsbColor
class instance. It's exactly what it sounds like, a class that describes a color in terms of hue, saturation, brightness. I also have access to the corresponding java.awt.Color
object.
We dynamically compute background color of table cells. That means we have to compute foreground colors as well.
How do we determine whether black or white would be a better match? I would prefer something without complex computations (if it's available through some library, it would be nice). We tried comparing the result of brightness * (saturation + 1)
with 0.5 (assuming brightness
and saturation
are floating-point figures), but it seems it doesn't work great with yellow (I get white which doesn't provide much contrast).
Java 8, Swing.
I have a custom HsbColor
class instance. It's exactly what it sounds like, a class that describes a color in terms of hue, saturation, brightness. I also have access to the corresponding java.awt.Color
object.
We dynamically compute background color of table cells. That means we have to compute foreground colors as well.
How do we determine whether black or white would be a better match? I would prefer something without complex computations (if it's available through some library, it would be nice). We tried comparing the result of brightness * (saturation + 1)
with 0.5 (assuming brightness
and saturation
are floating-point figures), but it seems it doesn't work great with yellow (I get white which doesn't provide much contrast).
Java 8, Swing.
Share edited Feb 12 at 9:48 Cagepi asked Feb 12 at 9:04 CagepiCagepi 3111 silver badge7 bronze badges 12 | Show 7 more comments3 Answers
Reset to default 1This was solved 30+ years ago by X/Motif (constants are in ColorP.h and Xm.h):
/* Contributions of each primary to overall luminosity, sum to 1.0 */
#define XmRED_LUMINOSITY 0.30
#define XmGREEN_LUMINOSITY 0.59
#define XmBLUE_LUMINOSITY 0.11
/* Percent effect of intensity, light, and luminosity & on brightness,
sum to 100 */
#define XmINTENSITY_FACTOR 75
#define XmLIGHT_FACTOR 0
#define XmLUMINOSITY_FACTOR 25
static int
Brightness(
XColor *color )
{
int brightness;
int intensity;
int light;
int luminosity, maxprimary, minprimary;
int red = color->red;
int green = color->green;
int blue = color->blue;
intensity = (red + green + blue) / 3;
/*
* The casting nonsense below is to try to control the point at
* the truncation occurs.
*/
luminosity = (int) ((XmRED_LUMINOSITY * (float) red)
+ (XmGREEN_LUMINOSITY * (float) green)
+ (XmBLUE_LUMINOSITY * (float) blue));
maxprimary = ( (red > green) ?
( (red > blue) ? red : blue ) :
( (green > blue) ? green : blue ) );
minprimary = ( (red < green) ?
( (red < blue) ? red : blue ) :
( (green < blue) ? green : blue ) );
light = (minprimary + maxprimary) / 2;
brightness = ( (intensity * XmINTENSITY_FACTOR) +
(light * XmLIGHT_FACTOR) +
(luminosity * XmLUMINOSITY_FACTOR) ) / 100;
return(brightness);
}
And it gets used thus:
#define XmMAX_SHORT 65535
#define XmCOLOR_PERCENTILE (XmMAX_SHORT / 100)
#define XmDEFAULT_FOREGROUND_THRESHOLD 70
static int XmFOREGROUND_THRESHOLD;
/* ... */
int default_foreground_threshold_spec;
default_foreground_threshold_spec = screen.foregroundThreshold;
if ((default_foreground_threshold_spec <= 0) ||
(default_foreground_threshold_spec > 100))
default_foreground_threshold_spec = XmDEFAULT_FOREGROUND_THRESHOLD;
XmFOREGROUND_THRESHOLD = default_foreground_threshold_spec * XmCOLOR_PERCENTILE;
/* ... */
int brightness = Brightness(bg_color);
if (brightness > XmFOREGROUND_THRESHOLD)
{
fg_color->red = 0;
fg_color->green = 0;
fg_color->blue = 0;
}
else
{
fg_color->red = XmMAX_SHORT;
fg_color->green = XmMAX_SHORT;
fg_color->blue = XmMAX_SHORT;
}
As far as I know, XmDEFAULT_FOREGROUND_THRESHOLD
is always used, so default_foreground_threshold_spec
is always 70.
Translating that into Java, we get:
/* Contributions of each primary to overall luminosity, sum to 1.0 */
private static final double RED_LUMINOSITY = 0.30;
private static final double GREEN_LUMINOSITY = 0.59;
private static final double BLUE_LUMINOSITY = 0.11;
/* Percent effect of intensity, light, and luminosity & on brightness,
sum to 100 */
private static final int INTENSITY_FACTOR = 75;
private static final int LIGHT_FACTOR = 0;
private static final int LUMINOSITY_FACTOR = 25;
private static int brightness(Color color) {
int red = color.getRed();
int green = color.getGreen();
int blue = color.getBlue();
int intensity = (red + green + blue) / 3;
int luminosity = (int) ((RED_LUMINOSITY * red)
+ (GREEN_LUMINOSITY * green)
+ (BLUE_LUMINOSITY * blue));
int maxprimary = IntStream.of(red, green, blue).max().getAsInt();
int minprimary = IntStream.of(red, green, blue).min().getAsInt();
int light = (minprimary + maxprimary) / 2;
int brightness = ((intensity * INTENSITY_FACTOR) +
(light * LIGHT_FACTOR) +
(luminosity * LUMINOSITY_FACTOR)) / 100;
return brightness;
}
private static final int DEFAULT_FOREGROUND_THRESHOLD = 70;
public static Color foregroundColorFor(Color bgColor) {
int foregroundThreshold = DEFAULT_FOREGROUND_THRESHOLD * 255 / 100;
int brightness = brightness(bgColor);
if (brightness > foregroundThreshold) {
return Color.BLACK;
} else {
return Color.WHITE;
}
}
(There are some small differences, because in X, a color component is 0–65535, unlike in Java where a component is either 0–255 or a float.)
We dynamically compute background color of table cells.
That means we have to compute foreground colors as well.
How do we determine whether black or white text would be a better match?
I would prefer something without complex computations.
A simple logic to decide "black or white" without complex computations is to just isolate the colour wheel into light and dark areas, and then for contrast simply use the appropriate "opposite" luminosity.
If you cover one eye then relax (or "blur") your vision while looking at the colour wheel below, it will become obvious which hues are in the darks and which are in the lights. No math needed, just natural biology.
The Java code to achieve same result could be something like this:
String color_hint = "";
int hue = 60; //# test degree: Yellow
if( (hue <= 45 || hue >= 205) ) { color_hint = "needs white text "; }
else { color_hint = "needs black text"; }
System.out.println("cell BG hue: " + hue + " degrees");
System.out.println("colour hint: " + color_hint);
You could also try using Luminosity.
Note that the highest "darks" is Magenta @ lum: 105.315
, the lowest "lights" is Green @ lum: 149.65
. There is a distance of 44 units between them, this is orange (high) and teal (low). They might become akward for you since these ones look okay with either black or white text. You will have to test and decide if, for your app's logic, it should be having a black or white foreground (eg: white foreground text).
A starting point in code:
public class Main
{
public static double lum = 0;
public static int hue = 0;
public static int red = 0, grn = 0, blu = 0;
public static String color_hint = "";
public static void main( String[] args )
{
//### Option 1: Checking via degree of Hue (H)...
hue = 60; //# testing hue of 60 degrees (yellow)
if( (hue <= 45 || hue >= 205) ) { color_hint = "Hue needs white text "; }
else { color_hint = "Hue needs black text"; }
//### Option 2: Checking via R+G+B Luminosity (Y)...
red = 0; grn = 255; blu = 0;
lum = get_luma( red , grn , blu );
//lum = ( (lum + 2.2) + 16 ); //# not needed here, but it might help your decision
System.out.println("cell BG hue: " + hue + " degrees");
System.out.println("colour hint: " + color_hint);
System.out.println("cell BG RGB: " + red +","+ grn +","+ blu);
System.out.println("luminosity backgrnd: " + lum);
System.out.println("======================================");
}
public static double get_luma( int in_red, int in_grn , int in_blu )
{
return ((in_red * 0.299) + (in_grn * 0.587) + (in_blu * 0.114));
}
}
Is there a reason you can't use the WCAG contrast ratio definition?
L1 = lighter colour relative luminance
L2 = darker colour relative luminance
contrast ratio = (L1 + 0.05) / (L2 + 0.05)
Assuming your colours are within sRGB, you can calculate the relative luminance from RGB values.
relative luminance = 0.2126 * Linear(R) + 0.7152 * Linear(G) + 0.0722 * Linear(B)
Linear(value) => value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ^ 2.4
You could check which foreground colour has a greater contrast with the background colour, and use that.
(It's the approach I use for the RGB text at the bottom of my colour picker)
Note that HSB's Brightness and HSL's Lightness don't represent what the eye perceives, they are just a reshaping of RGB.
本文标签: javaGet better contrast Color dynamicallyStack Overflow
版权声明:本文标题:java - Get better contrast Color dynamically - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741613373a2388386.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
L
value to decide if any cell colour (via HSL) with anL < 0.5
gets white text or vice versa). Another solution is to just calculate theL
manually from the RGB values extracted from the HSB. – VC.One Commented Feb 12 at 10:34L
can be used to calculate contrast). – VC.One Commented Feb 12 at 10:44Lum = ( Red * 0.299 ) + ( Grn * 0.587 ) + ( Blu * 0.114 )
. That output number between 0 - 255 is your colour's luminosity. Maybe your custom HSB class could also calculateL
at same time and then update some global variables (eg: set anL
for each current colour)? – VC.One Commented Feb 12 at 14:58