Color conversions - c++

My program reads from a png file many letters. The letters are black in white bg, as shown bellow.
I want my program given the colour values of those letters be able to print the letters in any text colour in any background colour. For example, I want to display 'a' using green text in blue background.
Using the following code I can change the text colour to any colour:
Color c;
c.r = original_black.r + ((255 - original_black.r) / 255.0) * desired_colour.r;
c.g = original_black.g + ((255 - original_black.g) / 255.0) * desired_colour.g;
c.b = original_black.b + ((255 - original_black.b) / 255.0) * desired_colour.b;
Result:
Question: How to update the above code to change the bg color to any color, as shown bellow:

Initial picture contains different colors (probably, like ClearType pixel rendering for LCD monitors). It is difficult to pick an acceptable way for colored edge pixels for all back-fore color combinations.
So I'd suggest to transform colors of initial picture to gray levels. One of the possible formulas:
Y = 0.299 R + 0.587 G + 0.114 B [0..255 range]
And use this value as alpha in alpha blending
c.r = (foreground.r * Y + (255 - Y) * background.r) / 255
and so on

Related

Algorithm For Bitmap [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
I'm creating a HSL gradient from raw pixel data, ignoring the hue how should I determine opacity based off of saturation & luminosity.
Also remember this is somewhat of an optical illusion, where the blue is in the image, it's actually becoming more transparent ( essentially there's a gradient with a blue rectangle below the gradient ).
double saturation = pixel.x / image.width,
luminosity = 1.0 - ( pixel.y / image.height );
double alpha_channel = 255.0 * ( 1.0 - ( ( luminosity ) * ( saturation ) ) );
// Luminosity Channel to fade from white to black.
double luminosity_amount = 255.0 * ( luminosity );
// Set the pixels Red / Green / Blue Alpha
pixel.color = color( luminosity_amount , alpha_channel );
Outcome Version:
Correct Version:
.
It becomes less saturated towards the right-middle. Meaning the bitmaps opacity is greater there.
If anyone could come up with an algorithm that would correctly fade the alpha channel from saturation & luminosity that'd be amazing.
Also reason why I'm doing this is so I don't need to create 360 textures for 360 degrees of hue. So I'd rather just create the gradient and layer the gradient over the hue.
If I understand you correctly, you want a grey scale bitmap with alpha that you can compose on top of any pure color.
Easily done. Imagine that the color underneath is red, for example. Then you can calculate the color you would want at every pixel. There are multiple ways to do that, but your 'correct bitmap' looks like this way:
TARGET = lum * ( WHITE * (1-sat) + RED * sat )
TARGET = WHITE * lum * (1-sat) + RED * lum * sat
You want the RED component to be provided by transparency, and the WHITE component to be provided by the bitmap gray:
TARGET = WHITE * gray_level/255 * alpha/255 + RED * (1-alpha/255)
Looks like you got the alpha part right:
1-alpha/255 = lum * sat
alpha = (1 - (lum * sat)) * 255
BUT, you got the gray level wrong:
gray_level/255 * alpha/255 = lum * (1-sat)
gray_level = 255 * (lum - (lum * sat)) / (1 - (lum * sat))
Be careful about the instability in the division when lum*sat is very close to 1.

Map angle to RGB color

This video shows what I think is a great visualization of gradient angle by mapping angle (in [-pi,pi]) to RGB color:
I would like to know if it is possible in OpenCV C++ to map a floating point value angle, whose range is -M_PI to M_PI, to an RGB value in some preset colorwheel. Thank you!
Look up hsv to rgb. H, or hue, is the angle you are looking for. You probably want full saturated values with maximum value, but if you turn s and v down a notch, the coding will look less artificial and computery.
Can you calculate this directly from the angle and the edge strength?
red = edgeStrength * sin(angle);
green = edgeStrength * sin(angle + 2*M_PI / 3.); // + 60°
blue = edgeStrength * sin(angle + 4*M_PI / 3.); // + 120°

Alpha Blending With Two Rendering Targets (DirectX or OpenGL)

I am trying to do the following:
Create a transparent texture called T
Render a textured quad called Q to T
Render T to the screen
Note that the alpha component of T will be zero and the alpha component of Q could be less than one.
I need two alpha blending equations so that if I render multiple instances of Q to T (1st blending equations used) and then render T (2nd blending euqations used), it would be the same as rendering multiple instances of Q directly to the screen.
I use this blending equation
color = src * srcAlpha + dst * (1 - srcAlpha)
alpha = 1 * srcAlpha + 0 * destAlpha
for the case when I render Qs directly to the screen, but cannot define two blending equations that achieve the same thing when I render to T first.
Note that T's pixels are initially fully transparent (alpha = 0) as I don't want it to overwrite the screen if it's not been draw to. Q may have any transparency level in each of its pixels.
What you are asking is mathematically impossible using linear interpolation.
Let's say you have two transparent objects, A and B. You have your texture T, and the screen S. And Blend is the blend equation.
What you are doing with rendering to T is:
T = Blend(B, Blend(A, T))
S = Blend(T, S)
//Therefore
S = Blend(Blend(B, Blend(A, T)), S)
See how there are 3 separate blending operations here? You cannot do that with two blending operations. Which is all you would have if you render A and B.
Linear interpolation is neither commutative nor associative. The order of blending operations matters. And the effect you achieve by writing to a temporary intermediate and blending that to the final buffer cannot be achieved without that intermediary.
There are blending operations that are associative and commutative. For example, additive. But that's almost certainly not going to achieve the visual effect you want, since it adds colors together. That may be something you could use, if you're doing HDR lighting. But there are many cases where it would be inappropriate.
Additive blending would involve a blend equation of the form:
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
With the source alpha determining what percentage of the incoming color to add.
I think this works when rendering Q into T:
color = src * srcAlpha + dst * (1 - srcAlpha)
alpha = (1 - destAlpha) * srcAlpha + 1 * destAlpha
Edit: Thinking about it, my answer won't result in the screen's alpha channel matching the alpha channel you describe when you're rendering direct into the screen. Your alpha blend mode when rendering direct to screen is very unusual. I'm not sure whether you really want the screen's alpha to represent the alpha of the last texture you rendered, or whether you don't actually care what the screen's alpha channel looks like. In most rendering situations people don't care about the state of the back buffer's alpha channel, hopefully that's the case here and my answer works for you.
I have figured this out, and given it's not something I've seen elsewhere on the internet, I hope this will help someone else...
For the first blend equation, for quads Q rendering to T:
Q -> T
color = src * srcAlpha + dst * (1 - srcAlpha)
alpha = 1 * srcAlpha + (1 - srcAlpha) * destAlpha
For the second blend equation, T going to the screen:
T -> S
color = src * 1 + dst * (1 - srcAlpha)
alpha = doesn't matter as screen alpha isn't used
Explanation
For Q -> T, the equation for the color components is as per the normal alpha blending equation:
color = src * srcAlpha + dst * (1 - srcAlpha)
however we need to make sure the alpha channel is set up for the second part. Because we are effectively pre-multiplying the alphas of Q when rendered to T using this blending equation, we only need the alpha in the second stage to determine how much to dim the color of the screen color by when rendering T, hence the alpha in the first equation is:
alpha = 1 * srcAlpha + (1 - srcAlpha) * destAlpha
but we only use it to fade the destination color behind the color of T in the second equation (we use 1 for src color as that was pre-multiplied by equation 1):
color = src * 1 + dst * (1 - srcAlpha)
In DirectX 11 this gives the same results whether equation 1 is used to render directly to the screen or both equations are used to render to T then the screen.
Code for DirectX 11 (for people who prefer real code):
1st equation:
D3D11_BLEND_DESC blend{};
blend.AlphaToCoverageEnable = FALSE;
auto& target = blend.RenderTarget[0];
target.BlendEnable = TRUE;
target.RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
target.SrcBlend = D3D11_BLEND_SRC_ALPHA;
target.DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
target.BlendOp = D3D11_BLEND_OP_ADD;
target.SrcBlendAlpha = D3D11_BLEND_ONE;
target.DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
target.BlendOpAlpha = D3D11_BLEND_OP_ADD;
2nd equation:
D3D11_BLEND_DESC blend{};
blend.AlphaToCoverageEnable = FALSE;
auto& target = blend.RenderTarget[0];
target.BlendEnable = TRUE;
target.RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
target.SrcBlend = D3D11_BLEND_ONE;
target.DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
target.BlendOp = D3D11_BLEND_OP_ADD;
target.SrcBlendAlpha = D3D11_BLEND_ZERO;
target.DestBlendAlpha = D3D11_BLEND_ONE;
target.BlendOpAlpha = D3D11_BLEND_OP_ADD;
T is initially cleared to {0, 0, 0, 0}.

How do you calculate a "highlight color"? [duplicate]

Given a system (a website for instance) that lets a user customize the background color for some section but not the font color (to keep number of options to a minimum), is there a way to programmatically determine if a "light" or "dark" font color is necessary?
I'm sure there is some algorithm, but I don't know enough about colors, luminosity, etc to figure it out on my own.
I encountered similar problem. I had to find a good method of selecting contrastive font color to display text labels on colorscales/heatmaps. It had to be universal method and generated color had to be "good looking", which means that simple generating complementary color was not good solution - sometimes it generated strange, very intensive colors that were hard to watch and read.
After long hours of testing and trying to solve this problem, I found out that the best solution is to select white font for "dark" colors, and black font for "bright" colors.
Here's an example of function I am using in C#:
Color ContrastColor(Color color)
{
int d = 0;
// Counting the perceptive luminance - human eye favors green color...
double luminance = (0.299 * color.R + 0.587 * color.G + 0.114 * color.B)/255;
if (luminance > 0.5)
d = 0; // bright colors - black font
else
d = 255; // dark colors - white font
return Color.FromArgb(d, d, d);
}
This was tested for many various colorscales (rainbow, grayscale, heat, ice, and many others) and is the only "universal" method I found out.
Edit
Changed the formula of counting a to "perceptive luminance" - it really looks better! Already implemented it in my software, looks great.
Edit 2
#WebSeed provided a great working example of this algorithm: http://codepen.io/WebSeed/full/pvgqEq/
Based on Gacek's answer but directly returning color constants (additional modifications see below):
public Color ContrastColor(Color iColor)
{
// Calculate the perceptive luminance (aka luma) - human eye favors green color...
double luma = ((0.299 * iColor.R) + (0.587 * iColor.G) + (0.114 * iColor.B)) / 255;
// Return black for bright colors, white for dark colors
return luma > 0.5 ? Color.Black : Color.White;
}
Note: I removed the inversion of the luma value to make bright colors have a higher value, what seems more natural to me and is also the 'default' calculation method.
(Edit: This has since been adopted in the original answer, too)
I used the same constants as Gacek from here since they worked great for me.
You can also implement this as an Extension Method using the following signature:
public static Color ContrastColor(this Color iColor)
You can then easily call it via
foregroundColor = backgroundColor.ContrastColor().
Thank you #Gacek. Here's a version for Android:
#ColorInt
public static int getContrastColor(#ColorInt int color) {
// Counting the perceptive luminance - human eye favors green color...
double a = 1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255;
int d;
if (a < 0.5) {
d = 0; // bright colors - black font
} else {
d = 255; // dark colors - white font
}
return Color.rgb(d, d, d);
}
And an improved (shorter) version:
#ColorInt
public static int getContrastColor(#ColorInt int color) {
// Counting the perceptive luminance - human eye favors green color...
double a = 1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255;
return a < 0.5 ? Color.BLACK : Color.WHITE;
}
My Swift implementation of Gacek's answer:
func contrastColor(color: UIColor) -> UIColor {
var d = CGFloat(0)
var r = CGFloat(0)
var g = CGFloat(0)
var b = CGFloat(0)
var a = CGFloat(0)
color.getRed(&r, green: &g, blue: &b, alpha: &a)
// Counting the perceptive luminance - human eye favors green color...
let luminance = 1 - ((0.299 * r) + (0.587 * g) + (0.114 * b))
if luminance < 0.5 {
d = CGFloat(0) // bright colors - black font
} else {
d = CGFloat(1) // dark colors - white font
}
return UIColor( red: d, green: d, blue: d, alpha: a)
}
Javascript [ES2015]
const hexToLuma = (colour) => {
const hex = colour.replace(/#/, '');
const r = parseInt(hex.substr(0, 2), 16);
const g = parseInt(hex.substr(2, 2), 16);
const b = parseInt(hex.substr(4, 2), 16);
return [
0.299 * r,
0.587 * g,
0.114 * b
].reduce((a, b) => a + b) / 255;
};
Ugly Python if you don't feel like writing it :)
'''
Input a string without hash sign of RGB hex digits to compute
complementary contrasting color such as for fonts
'''
def contrasting_text_color(hex_str):
(r, g, b) = (hex_str[:2], hex_str[2:4], hex_str[4:])
return '000' if 1 - (int(r, 16) * 0.299 + int(g, 16) * 0.587 + int(b, 16) * 0.114) / 255 < 0.5 else 'fff'
Thanks for this post.
For whoever might be interested, here's an example of that function in Delphi:
function GetContrastColor(ABGColor: TColor): TColor;
var
ADouble: Double;
R, G, B: Byte;
begin
if ABGColor <= 0 then
begin
Result := clWhite;
Exit; // *** EXIT RIGHT HERE ***
end;
if ABGColor = clWhite then
begin
Result := clBlack;
Exit; // *** EXIT RIGHT HERE ***
end;
// Get RGB from Color
R := GetRValue(ABGColor);
G := GetGValue(ABGColor);
B := GetBValue(ABGColor);
// Counting the perceptive luminance - human eye favors green color...
ADouble := 1 - (0.299 * R + 0.587 * G + 0.114 * B) / 255;
if (ADouble < 0.5) then
Result := clBlack // bright colors - black font
else
Result := clWhite; // dark colors - white font
end;
This is such a helpful answer. Thanks for it!
I'd like to share an SCSS version:
#function is-color-light( $color ) {
// Get the components of the specified color
$red: red( $color );
$green: green( $color );
$blue: blue( $color );
// Compute the perceptive luminance, keeping
// in mind that the human eye favors green.
$l: 1 - ( 0.299 * $red + 0.587 * $green + 0.114 * $blue ) / 255;
#return ( $l < 0.5 );
}
Now figuring out how to use the algorithm to auto-create hover colors for menu links. Light headers get a darker hover, and vice-versa.
Short Answer:
Calculate the luminance (Y) of the given color, and flip the text either black or white based on a pre-determined middle contrast figure. For a typical sRGB display, flip to white when Y < 0.4 (i.e. 40%)
Longer Answer
Not surprisingly, nearly every answer here presents some misunderstanding, and/or is quoting incorrect coefficients. The only answer that is actually close is that of Seirios, though it relies on WCAG 2 contrast which is known to be incorrect itself.
If I say "not surprisingly", it is due in part to the massive amount of misinformation on the internet on this particular subject. The fact this field is still a subject of active research and unsettled science adds to the fun. I come to this conclusion as the result of the last few years of research into a new contrast prediction method for readability.
The field of visual perception is dense and abstract, as well as developing, so it is common for misunderstandings to exist. For instance, HSV and HSL are not even close to perceptually accurate. For that you need a perceptually uniform model such as CIELAB or CIELUV or CIECAM02 etc.
Some misunderstandings have even made their way into standards, such as the contrast part of WCAG 2 (1.4.3), which has been demonstrated as incorrect over much of its range.
First Fix:
The coefficients shown in many answers here are (.299, .587, .114) and are wrong, as they pertain to a long obsolete system known as NTSC YIQ, the analog broadcast system in North America some decades ago. While they may still be used in some YCC encoding specs for backwards compatibility, they should not be used in an sRGB context.
The coefficients for sRGB and Rec.709 (HDTV) are:
Red: 0.2126
Green: 0.7152
Blue: 0.0722
Other color spaces like Rec2020 or AdobeRGB use different coefficients, and it is important to use the correct coefficients for a given color space.
The coefficients can not be applied directly to 8 bit sRGB encoded image or color data. The encoded data must first be linearized, then the coefficients applied to find the luminance (light value) of the given pixel or color.
For sRGB there is a piecewise transform, but as we are only interested in the perceived lightness contrast to find the point to "flip" the text from black to white, we can take a shortcut via the simple gamma method.
Andy's Shortcut to Luminance & Lightness
Divide each sRGB color by 255.0, then raise to the power of 2.2, then multiply by the coefficients and sum them to find estimated luminance.
let Ys = Math.pow(sR/255.0,2.2) * 0.2126 +
Math.pow(sG/255.0,2.2) * 0.7152 +
Math.pow(sB/255.0,2.2) * 0.0722; // Andy's Easy Luminance for sRGB. For Rec709 HDTV change the 2.2 to 2.4
Here, Y is the relative luminance from an sRGB monitor, on a 0.0 to 1.0 scale. This is not relative to perception though, and we need further transforms to fit our human visual perception of the relative lightness, and also of the perceived contrast.
The 40% Flip
But before we get there, if you are only looking for a basic point to flip the text from black to white or vice versa, the cheat is to use the Y we just derived, and make the flip point about Y = 0.40;. so for colors higher than 0.4 Y, make the text black #000 and for colors darker than 0.4 Y, make the text white #fff.
let textColor = (Ys < 0.4) ? "#fff" : "#000"; // Low budget down and dirty text flipper.
Why 40% and not 50%? Our human perception of lightness/darkness and of contrast is not linear. For a self illuminated display, it so happens that 0.4 Y is about middle contrast under most typical conditions.
Yes it varies, and yes this is an over simplification. But if you are flipping text black or white, the simple answer is a useful one.
Perceptual Bonus Round
Predicting the perception of a given color and lightness is still a subject of active research, and not entirely settled science. The L* (Lstar) of CIELAB or LUV has been used to predict perceptual lightness, and even to predict perceived contrast. However, L* works well for surface colors in a very defined/controlled environment, and does not work as well for self illuminated displays.
While this varies depending on not only the display type and calibration, but also your environment and the overall page content, if you take the Y from above, and raise it by around ^0.685 to ^0.75, you'll find that 0.5 is typically the middle point to flip the text from white to black.
let textColor = (Math.pow(Ys,0.75) < 0.5) ? "#fff" : "#000"; // perceptually based text flipper.
Using the exponent 0.685 will make the text color swap on a darker color, and using 0.8 will make the text swap on a lighter color.
Spatial Frequency Double Bonus Round
It is useful to note that contrast is NOT just the distance between two colors. Spatial frequency, in other words font weight and size, are also CRITICAL factors that cannot be ignored.
That said, you may find that when colors are in the midrange, that you'd want to increase the size and or weight of the font.
let textSize = "16px";
let textWeight = "normal";
let Ls = Math.pow(Ys,0.7);
if (Ls > 0.33 && Ls < 0.66) {
textSize = "18px";
textWeight = "bold";
} // scale up fonts for the lower contrast mid luminances.
Hue R U
It's outside the scope of this post to delve deeply, but above we are ignoring hue and chroma. Hue and chroma do have an effect, such as Helmholtz Kohlrausch, and the simpler luminance calculations above do not always predict intensity due to saturated hues.
To predict these more subtle aspects of perception, a complete appearance model is needed. R. Hunt, M. Fairshild, E. Burns are a few authors worth looking into if you want to plummet down the rabbit hole of human visual perception...
For this narrow purpose, we could re-weight the coefficients slightly, knowing that green makes up the majority of of luminance, and pure blue and pure red should always be the darkest of two colors. What tends to happen using the standard coefficients, is middle colors with a lot of blue or red may flip to black at a lower than ideal luminance, and colors with a high green component may do the opposite.
That said, I find this is best addressed by increasing font size and weight in the middle colors.
Putting it all together
So we'll assume you'll send this function a hex string, and it will return a style string that can be sent to a particular HTML element.
Check out the CODEPEN, inspired by the one Seirios did:
CodePen: Fancy Font Flipping
One of the things the Codepen code does is increase the text size for the lower contrast midrange. Here's a sample:
And if you want to play around with some of these concepts, see the SAPC development site at https://www.myndex.com/SAPC/ clicking on "research mode" provides interactive experiments to demonstrate these concepts.
Terms of enlightenment
Luminance: Y (relative) or L (absolute cd/m2) a spectrally weighted but otherwise linear measure of light. Not to be confused with "Luminosity".
Luminosity: light over time, useful in astronomy.
Lightness: L* (Lstar) perceptual lightness as defined by the CIE. Some models have a related lightness J*.
I had the same problem but i had to develop it in PHP. I used #Garek's solution and i also used this answer:
Convert hex color to RGB values in PHP to convert HEX color code to RGB.
So i'm sharing it.
I wanted to use this function with given Background HEX color, but not always starting from '#'.
//So it can be used like this way:
$color = calculateColor('#804040');
echo $color;
//or even this way:
$color = calculateColor('D79C44');
echo '<br/>'.$color;
function calculateColor($bgColor){
//ensure that the color code will not have # in the beginning
$bgColor = str_replace('#','',$bgColor);
//now just add it
$hex = '#'.$bgColor;
list($r, $g, $b) = sscanf($hex, "#%02x%02x%02x");
$color = 1 - ( 0.299 * $r + 0.587 * $g + 0.114 * $b)/255;
if ($color < 0.5)
$color = '#000000'; // bright colors - black font
else
$color = '#ffffff'; // dark colors - white font
return $color;
}
Flutter implementation
Color contrastColor(Color color) {
if (color == Colors.transparent || color.alpha < 50) {
return Colors.black;
}
double luminance = (0.299 * color.red + 0.587 * color.green + 0.114 * color.blue) / 255;
return luminance > 0.5 ? Colors.black : Colors.white;
}
Based on Gacek's answer, and after analyzing #WebSeed's example with the WAVE browser extension, I've come up with the following version that chooses black or white text based on contrast ratio (as defined in W3C's Web Content Accessibility Guidelines (WCAG) 2.1), instead of luminance.
This is the code (in javascript):
// As defined in WCAG 2.1
var relativeLuminance = function (R8bit, G8bit, B8bit) {
var RsRGB = R8bit / 255.0;
var GsRGB = G8bit / 255.0;
var BsRGB = B8bit / 255.0;
var R = (RsRGB <= 0.03928) ? RsRGB / 12.92 : Math.pow((RsRGB + 0.055) / 1.055, 2.4);
var G = (GsRGB <= 0.03928) ? GsRGB / 12.92 : Math.pow((GsRGB + 0.055) / 1.055, 2.4);
var B = (BsRGB <= 0.03928) ? BsRGB / 12.92 : Math.pow((BsRGB + 0.055) / 1.055, 2.4);
return 0.2126 * R + 0.7152 * G + 0.0722 * B;
};
var blackContrast = function(r, g, b) {
var L = relativeLuminance(r, g, b);
return (L + 0.05) / 0.05;
};
var whiteContrast = function(r, g, b) {
var L = relativeLuminance(r, g, b);
return 1.05 / (L + 0.05);
};
// If both options satisfy AAA criterion (at least 7:1 contrast), use preference
// else, use higher contrast (white breaks tie)
var chooseFGcolor = function(r, g, b, prefer = 'white') {
var Cb = blackContrast(r, g, b);
var Cw = whiteContrast(r, g, b);
if(Cb >= 7.0 && Cw >= 7.0) return prefer;
else return (Cb > Cw) ? 'black' : 'white';
};
A working example may be found in my fork of #WebSeed's codepen, which produces zero low contrast errors in WAVE.
As Kotlin / Android extension:
fun Int.getContrastColor(): Int {
// Counting the perceptive luminance - human eye favors green color...
val a = 1 - (0.299 * Color.red(this) + 0.587 * Color.green(this) + 0.114 * Color.blue(this)) / 255
return if (a < 0.5) Color.BLACK else Color.WHITE
}
An implementation for objective-c
+ (UIColor*) getContrastColor:(UIColor*) color {
CGFloat red, green, blue, alpha;
[color getRed:&red green:&green blue:&blue alpha:&alpha];
double a = ( 0.299 * red + 0.587 * green + 0.114 * blue);
return (a > 0.5) ? [[UIColor alloc]initWithRed:0 green:0 blue:0 alpha:1] : [[UIColor alloc]initWithRed:255 green:255 blue:255 alpha:1];
}
iOS Swift 3.0 (UIColor extension):
func isLight() -> Bool
{
if let components = self.cgColor.components, let firstComponentValue = components[0], let secondComponentValue = components[1], let thirdComponentValue = components[2] {
let firstComponent = (firstComponentValue * 299)
let secondComponent = (secondComponentValue * 587)
let thirdComponent = (thirdComponentValue * 114)
let brightness = (firstComponent + secondComponent + thirdComponent) / 1000
if brightness < 0.5
{
return false
}else{
return true
}
}
print("Unable to grab components and determine brightness")
return nil
}
Swift 4 Example:
extension UIColor {
var isLight: Bool {
let components = cgColor.components
let firstComponent = ((components?[0]) ?? 0) * 299
let secondComponent = ((components?[1]) ?? 0) * 587
let thirdComponent = ((components?[2]) ?? 0) * 114
let brightness = (firstComponent + secondComponent + thirdComponent) / 1000
return !(brightness < 0.6)
}
}
UPDATE - Found that 0.6 was a better test bed for the query
Note there is an algorithm for this in the google closure library that references a w3c recommendation: http://www.w3.org/TR/AERT#color-contrast. However, in this API you provide a list of suggested colors as a starting point.
/**
* Find the "best" (highest-contrast) of the suggested colors for the prime
* color. Uses W3C formula for judging readability and visual accessibility:
* http://www.w3.org/TR/AERT#color-contrast
* #param {goog.color.Rgb} prime Color represented as a rgb array.
* #param {Array<goog.color.Rgb>} suggestions Array of colors,
* each representing a rgb array.
* #return {!goog.color.Rgb} Highest-contrast color represented by an array.
*/
goog.color.highContrast = function(prime, suggestions) {
var suggestionsWithDiff = [];
for (var i = 0; i < suggestions.length; i++) {
suggestionsWithDiff.push({
color: suggestions[i],
diff: goog.color.yiqBrightnessDiff_(suggestions[i], prime) +
goog.color.colorDiff_(suggestions[i], prime)
});
}
suggestionsWithDiff.sort(function(a, b) { return b.diff - a.diff; });
return suggestionsWithDiff[0].color;
};
/**
* Calculate brightness of a color according to YIQ formula (brightness is Y).
* More info on YIQ here: http://en.wikipedia.org/wiki/YIQ. Helper method for
* goog.color.highContrast()
* #param {goog.color.Rgb} rgb Color represented by a rgb array.
* #return {number} brightness (Y).
* #private
*/
goog.color.yiqBrightness_ = function(rgb) {
return Math.round((rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000);
};
/**
* Calculate difference in brightness of two colors. Helper method for
* goog.color.highContrast()
* #param {goog.color.Rgb} rgb1 Color represented by a rgb array.
* #param {goog.color.Rgb} rgb2 Color represented by a rgb array.
* #return {number} Brightness difference.
* #private
*/
goog.color.yiqBrightnessDiff_ = function(rgb1, rgb2) {
return Math.abs(
goog.color.yiqBrightness_(rgb1) - goog.color.yiqBrightness_(rgb2));
};
/**
* Calculate color difference between two colors. Helper method for
* goog.color.highContrast()
* #param {goog.color.Rgb} rgb1 Color represented by a rgb array.
* #param {goog.color.Rgb} rgb2 Color represented by a rgb array.
* #return {number} Color difference.
* #private
*/
goog.color.colorDiff_ = function(rgb1, rgb2) {
return Math.abs(rgb1[0] - rgb2[0]) + Math.abs(rgb1[1] - rgb2[1]) +
Math.abs(rgb1[2] - rgb2[2]);
};
base R version of #Gacek's answer to get luminance (you can apply your own threshold easily)
# vectorized
luminance = function(col) c(c(.299, .587, .114) %*% col2rgb(col)/255)
Usage:
luminance(c('black', 'white', '#236FAB', 'darkred', '#01F11F'))
# [1] 0.0000000 1.0000000 0.3730039 0.1629843 0.5698039
If you're manipulating color spaces for visual effect it's generally easier to work in HSL (Hue, Saturation and Lightness) than RGB. Moving colours in RGB to give naturally pleasing effects tends to be quite conceptually difficult, whereas converting into HSL, manipulating there, then converting back out again is more intuitive in concept and invariably gives better looking results.
Wikipedia has a good introduction to HSL and the closely related HSV. And there's free code around the net to do the conversion (for example here is a javascript implementation)
What precise transformation you use is a matter of taste, but personally I'd have thought reversing the Hue and Lightness components would be certain to generate a good high contrast colour as a first approximation, but you can easily go for more subtle effects.
You can have any hue text on any hue background and ensure that it is legible. I do it all the time. There's a formula for this in Javascript on Readable Text in Colour – STW*
As it says on that link, the formula is a variation on the inverse-gamma adjustment calculation, though a bit more manageable IMHO.
The menus on the right-hand side of that link and its associated pages use randomly-generated colours for text and background, always legible. So yes, clearly it can be done, no problem.
An Android variation that captures the alpha as well.
(thanks #thomas-vos)
/**
* Returns a colour best suited to contrast with the input colour.
*
* #param colour
* #return
*/
#ColorInt
public static int contrastingColour(#ColorInt int colour) {
// XXX https://stackoverflow.com/questions/1855884/determine-font-color-based-on-background-color
// Counting the perceptive luminance - human eye favors green color...
double a = 1 - (0.299 * Color.red(colour) + 0.587 * Color.green(colour) + 0.114 * Color.blue(colour)) / 255;
int alpha = Color.alpha(colour);
int d = 0; // bright colours - black font;
if (a >= 0.5) {
d = 255; // dark colours - white font
}
return Color.argb(alpha, d, d, d);
}
I would have commented on the answer by #MichaelChirico but I don't have enough reputation. So, here's an example in R with returning the colours:
get_text_colour <- function(
background_colour,
light_text_colour = 'white',
dark_text_colour = 'black',
threshold = 0.5
) {
background_luminance <- c(
c( .299, .587, .114 ) %*% col2rgb( background_colour ) / 255
)
return(
ifelse(
background_luminance < threshold,
light_text_colour,
dark_text_colour
)
)
}
> get_text_colour( background_colour = 'blue' )
[1] "white"
> get_text_colour( background_colour = c( 'blue', 'yellow', 'pink' ) )
[1] "white" "black" "black"
> get_text_colour( background_colour = c('black', 'white', '#236FAB', 'darkred', '#01F11F') )
[1] "white" "black" "white" "white" "black"

How would I get this to UV map correctly?

Alright so I have my code to draw out a big landscape using C++ and DirectX. I had it textured with one texture and then needed to add more. I saw people doing it where they had 1 texture image and the image contained 2 textures. Thats what I made, it's a 256x128 image. My problem now is that since my terrain automatically generated the coordinates to UV map 1 texture now it is displaying both textures. I need to make it so when the height of the world is high enough it is 1 texture and everything under is another texture. My code for the UV coordinates,
Vertices[y * WIDTH * x].U = x / 1.28;
Vertices[y * WIDTH * x].V = y / 1.28;
those are my mapping coordinates, X is the current X value of the vertice it is drawing and the Y value is its current y position. The heightmap is 128x128 so I divided by 1.28 to make it so that each polygon had the texture UV mapped on it. The height is calculated as well since I am loading a heightmap and im trying to get it so when it is high enough it UV maps 1 half of the image and if it is the other it UV maps the other side of the image. Someone please help!
bool topTexture = height[x][y] > threshold;
float u = x / 1.28;
float v = y / 1.28;
Vertices[y * WIDTH * x].U = (u - (int)u) / 2 + (topTexture ? 0.5 : 0);
Vertices[y * WIDTH * x].V = (v - (int)v) / 2 + (topTexture ? 0.5 : 0);
You may want to blend the two textures at the threshold level. Then, you have to do it in the PixelShader.