MathGem:Color Operations

From GPWiki
Jump to: navigation, search

32 bit DWORD/Long to separate R,G,B,A channels

These functions will convert a 32bit DWORD/Long value to the R,G,B,A channels it's made of. This allows you to work on individual channels, instead of the entire color. This format is widely used in Microsoft Windows' GDI, where it is known as the COLORREF type. Keep in mind that most graphics libraries already provide functions that do this.

C/C++

void DWORD_To_RGBA(DWORD color, BYTE& r, BYTE& g, BYTE& b, BYTE& a) {

 r = (color & 0xFF000000) >> 24;
 g = (color & 0x00FF0000) >> 16;
 b = (color & 0x0000FF00) >> 8;
 a = (color & 0x000000FF);

}

Python

In Python we do the bitshift first (unlike C/C++) because in Python versions older than 2.4 hex values greater than 0x7FFFFFFF return negative values (unless suffixed with "L" as in 0xFFFFFFFFL).

def dword_to_rgba(color):

   r = (color >> 24) & 0xFF
   g = (color >> 16) & 0xFF
   b = (color >> 8) & 0xFF
   a = color & 0xFF
   return (r, g, b, a)

Visual Basic

In Visual Basic we use the AND (bitwise), Mod, and division to split a color. This goes like so:

Public Sub LongToRGB(Color As Long, Red As Long, Green As Long, Blue As Long)

   Dim lColor As Long
   lColor = Color
   Red = lColor Mod &H100
   lColor = lColor \ &H100
   Green = lColor Mod &H100
   lColor = lColor \ &H100
   Blue = lColor Mod &H100

End Sub

Or another, more standard way, bit mask and shift:

Private Sub LongToRGB2(lColor As Long, lR As Long, lG As Long, lB As Long) lR = lColor And &HFF& lG = (lColor And &HFF00&) \ &H100& lB = (lColor And &HFF0000) \ &H10000 End Sub

R,G,B,A channels to 32 bit DWORD/Long

This code will combine the given RGBA channels to one DWORD/Long. The returning value can be used in DirectX. Note that DirectX provides it's own functions for this, as well.

C/C++

void RGBA_To_DWORD(DWORD& color, BYTE r, BYTE g, BYTE b, BYTE a) {

 color = (r << 24) | (g << 16) | (b << 8) | a;

}

Python

def rgba_to_dword(r, g, b, a):

   color = (r << 24) | (g << 16) | (b << 8) | a
   return color

Gamma correction

Gamma correction is increasing or decreasing the brightness of a color. A higher gamma value will mean brighter colors, while a lower gamma value will result in darker colors. The code could be put in it's own macro or function.

The code below uses the color in a range of 0-255 per channel. Make sure that you actually clamp the value to 0-255 after changing the gamma value, or it could lead to undesirable results.

C/C++

You must include math.h (C) or cmath (C++) for pow().

 float fGammaCorrection = 2.5f;
 fR = pow( color.cR, fGammaCorrection );
 fG = pow( color.cG, fGammaCorrection );
 fB = pow( color.cB, fGammaCorrection );

Python

fGammaCorrection = 2.5 fR = cR ** fGammaCorrection fG = cG ** fGammaCorrection fB = cB ** fGammaCorrection

Visual Basic

Dim fGammaCorrection as Single fGammaCorrection = 2.5

fR = color.bR ^ fGammaCorrection fG = color.bG ^ fGammaCorrection fB = color.bB ^ fGammaCorrection

Interpolating colors

Interpolating between two colors, using a simple variable to control the interpolation amount. Note that DirectX8 and newer has their own D3DXColor* functions for all kinds of color operations.

Python

fR = Color1.r + Amount * (Color2.r - Color1.r) fG = Color1.g + Amount * (Color2.g - Color1.g) fB = Color1.b + Amount * (Color2.b - Color1.b) fA = Color1.a + Amount * (Color2.a - Color1.a)

Visual Basic

Dim fAmount as Single

fR = Color1.r + fAmount * (Color2.r - Color1.r) fG = Color1.g + fAmount * (Color2.g - Color1.g) fB = Color1.b + fAmount * (Color2.b - Color1.b) fA = Color1.a + fAmount * (Color2.a - Color1.a)

C/C++

float fAmount;

fR = Color1.r + fAmount * (Color2.r - Color1.r); fG = Color1.g + fAmount * (Color2.g - Color1.g); fB = Color1.b + fAmount * (Color2.b - Color1.b); fA = Color1.a + fAmount * (Color2.a - Color1.a);

RGB and BGR conversions

C/C++

DWORD rgb_to_bgr(DWORD rgb) {

 int b, g, r;
 b = rgb & 0xFF;
 g = (rgb >> 8) & 0xFF;
 r = rgb >> 16;
 return (b << 16) | (g << 8) | r;

}

DWORD bgr_to_rgb(bgr) {

   int r, g, b;
   r = bgr & 0xFF;
   g = (bgr >> 8) & 0xFF;
   b = bgr >> 16;
   return (r << 16) | (g << 8) | b;

}

Python

def rgb_to_bgr(rgb):

   b = rgb & 0xFF
   g = (rgb >> 8) & 0xFF
   r = rgb >> 16
   return (b << 16) | (g << 8) | r

def bgr_to_rgb(bgr):

   r = bgr & 0xFF
   g = (bgr >> 8) & 0xFF
   b = bgr >> 16
   return (r << 16) | (g << 8) | b

Working with floating point "scalar" colors

Given that the color elements are often represented by a single byte, it is sometimes more useful to work with color as a value range of 0 to 1. Thus, a variable representing a color range is divided by the maximum value of a byte (255 or 0xFF for you hex lovers). You'll find this mathematical representation most often in fx, vertex, or pixel shader scripts. In order to use it in your own code you'll need to make some modifications to the standard operations.

Rules

Since a monitor can't represent colors brighter than white or darker than black or any elemental combination of brightness thereof, we have to clearly define what our results should be: struct color {

    double red,green,blue;

}; // . . . inline void clipColorElements(const color _in, color &_out) { // Pardon the arcane c++. _out.red=(_in.red > 1.0) ? 1.0 :

    (_in.red < 0.0) ? 0.0 :
    _in.red;

_out.green=(_in.green > 1.0) ? 1.0 :

    (_in.green < 0.0) ? 0.0 :
    _in.green;

_out.blue=(_in.blue > 1.0) ? 1.0 :

    (_in.blue < 0.0) ? 0.0 :
    _in.blue;

} // . . .

Conversion

Next we need to convert the standard format colors to scalars.

void rgbToScalar(const BYTE red, const BYTE green, const BYTE blue, color &scalar_out) {

    scalar_out.red=red/255;
    scalar_out.green=green/255;
    scalar_out.blue=blue/255;

}

We also need a way to go back, of course.

void scalarToRgb(const color scalar_in, BYTE &red, BYTE &green, BYTE &blue) {

    red=scalar_in.red*255;
    green=scalar_in.green*255;
    blue=scalar_in.blue*255;

}

Operations

Now we can throw the new operators into our color structure. Multiplication is the easiest, so we'll start with that.

struct color { // . . .

    color operation * (const color rh) {
         red*=rh.red;
         green*=rh.green;
         blue*=rh.blue;
    }

// . . . };

Addition is a little more difficult.

struct color { // . . .

    color operation + (const color rh) {
         red+=rh.red;
         green+=rh.green;
         blue+=rh.blue;
         clipColorElements(this, this);
    }

// . . . };

Subtraction is nearly identical

struct color { // . . .

    color operation - (const color rh) {
         red-=rh.red;
         green-=rh.green;
         blue-=rh.blue;
         clipColorElements(this, this);
    }

// . . . };

Not hard, is it? I don't recommend using this code from anything that will be running several times a second, but it's fine for calculating various effects and rendering fractals. With some modification it can be applied to sound waves as well.

Note: I wrote these code samples from memory, so pardon me if it doesn't work "out of the box."