I'm trying emulate the Tint Effect of Open XML. What it does is change the hue of pixels in an image by shifting the hue. It takes 2 parameters: 1) the hue
(in degrees) and 2) the amt
(the amount, a percentage). It is #2 that I'm having issues with. The spec states:
Tint: Shifts effect color values either towards or away from hue by the specified amount.
- amt (Amount) - Specifies by how much the color value is shifted.
- hue (Hue) - Specifies the hue towards which to tint.
Never minding the XML construction, I can emulate values that have an amt
of 100%. So for example, if I want Blue (hue: 240°), I can create this (the Tinted one). Here's an example:
Original and Tinted (hue = 240, Amount = 100%).
This is achieved simply by setting the hue to 240, keeping saturation and luminance the same and converting to RGB and writing each pixel.
Here's what I can't achieve though:
Hue=240 (blue), Amount = 30%, 50% and 80%, respectively
Again, the spec for Amount
says Specifies by how much the color value is shifted. I've tried all sorts of ways here to get this to work, but can't seem to (hue=hue*amount
, originalhue * amount + hue
, etc.)
More examples: Hue=120 (green), Amount = 30%, 50%, 80% and 100%, respectively. The 100% one I can get.
Here are some value lists of a single pixel in the pictures above:
Hue Amount R G B | H S L Original 244 196 10 | 48 0.92 0.5 Blue 240 30% 237 30 45 | 356 0.85 0.52 Blue 240 50% 245 9 156 | 323 0.93 0.5 Blue 240 80% 140 12 244 | 273 0.91 0.5 Blue 240 100% 12 12 244 | 240 0.91 0.5
Hue Amount R G B | H S L Original 244 196 10 | 48 0.92 0.5 Green 120 30% 211 237 30 | 68 0.85 0.52 Green 120 50% 159 237 30 | 83 0.85 0.52 Green 120 80% 81 237 29 | 105 0.85 0.52 Green 120 100% 29 237 29 | 120 0.85 0.52
So, the question is: Does anyone know how this should work?
Note: This is not a duplicate of:
- Changing the tint of a bitmap while preserving the overall brightness
- Rotate Hue using ImageAttributes in C#
- ...or anything else I could find on SO
Hue is determined by the dominant wavelength of the visible spectrum. It is the attribute that permits colors to be classified as red, yellow, green, blue, or an intermediate color. Saturation pertains the amount of white light mixed with a hue.
Brightness is the relative lightness or darkness of a particular color, from black (no brightness) to white (full brightness). Brightness is also called Lightness in some contexts, in particular in SQL queries.
HSV Color Scale: The HSV (which stands for Hue Saturation Value) scale provides a numerical readout of your image that corresponds to the color names contained therein. Hue is measured in degrees from 0 to 360. For instance, cyan falls between 181–240 degrees, and magenta falls between 301–360 degrees.
I am quite certain that your problem results from the way you are interpolating angles. Here's an interpolation function (written in python) that should do the trick. It is based on a suggestion from the xna forums thread Shortest 2D Angle Interpolation.
def wrap(value, lower, upper):
distance = upper - lower
return value - ((value-lower)//distance)*distance
def shortestangle(a,b):
angle = wrap(b-a, 0, 360)
if angle>=180: angle -= 360
return angle
def interpolate(a,b,amount):
return (a+shortestangle(a,b)*amount)%360
Now, interpolate(originalHue,hue,amount)
should produce the desired result.
Edit: It is my understanding that your goal is to rotate the original hue towards a certain target hue by some given amount. I'm sure you're already familiar with this, but for the sake of illustration, here's a color wheel.
(source: sapdesignguild.org)
The problem is that mixing (or interpolating) two angles is not trivial, so code like hue = ((hue - originalHue) * amount) + originalHue
will not work. There is an infinite number of ways you can go from one angle to another because of the wraparound at 360°. To get from 0° to 60° you could rotate 60° counter-clockwise, 420° counter-clockwise, 300° clockwise etc. Usually the shortest angle is the desired one.
For example, lets consider the penguin necks: if your original hue is 30° (orange), your target is 240° (blue) and the amount is 50%, you would get the following results:
//Linear Interpolation
(30° + (240° - 30°)*0.5) = 135° (green)
//"Shortest 2D Angle Interpolation"
(30° + shortestangle(30°,240°)*0.5) % 360 = (30° + (-150°)*0.5) % 360 = 315° (magenta)
My guess is that the second result is the one you are looking for, but I may be wrong and the error could be somewhere else entirely...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With