Skip to content

Instantly share code, notes, and snippets.

@silvercircle
Created March 27, 2013 20:49
Show Gist options
  • Save silvercircle/5257898 to your computer and use it in GitHub Desktop.
Save silvercircle/5257898 to your computer and use it in GitHub Desktop.
colorizing bitmaps
/**
* colorize an image item (both standalone items with their own bitmap and glyph items).
*
* @param item image item to colorize
* @param clr color to use (note: BGRA format required, although, alpha is ignored)
* @param hue hue adjustment (in degrees, -180 .. +180
* @param saturation scalar value (0.0 ... 1.0)
* @param value scalar value (0.0 ... 1.0)
*
* note: this isn't performance critical as it only runs at skin loading time or when
* the user changes colorization options, never during rendering.
*
* if clr == 0, hsv transformation will be applied, otherwise it's rgb colorization.
*/
void Gfx::colorizeGlyph(TImageItem *item, const COLORREF clr, float hue, float saturation, float value)
{
LONG stride = 0, line, pixel;
HBITMAP hBitmap = 0;
LONG x, y, x1, y1;
BITMAP bmp = {0};
DWORD dwLen;
BYTE* p, *pOrig, *pLine, alpha;
float v_s_u, v_s_w, r, g, b;
if(0 == clr) { // do hsv transformation
v_s_u = value * saturation * cos(hue * M_PI/180);
v_s_w = value * saturation * sin(hue * M_PI/180);
}
else { // rgb colorization
BYTE rValue = GetRValue(clr);
BYTE gValue = GetGValue(clr);
BYTE bValue = GetBValue(clr);
r = (float)rValue / 2.55;
g = (float)gValue / 2.55;
b = (float)bValue / 2.55;
}
if(item) {
/*
* colorize a rectangular glyph
*/
if(item->dwFlags & IMAGE_GLYPH) {
hBitmap = Skin::glyphItem->hbm;
x = item->glyphMetrics[0];
y = item->glyphMetrics[1];
x1 = x + item->glyphMetrics[2] - 1;
y1 = y + item->glyphMetrics[3] - 1;
GetObject(hBitmap, sizeof(bmp), &bmp);
if (bmp.bmBitsPixel != 32)
return;
dwLen = bmp.bmWidth * bmp.bmHeight * 4;
p = (BYTE *)malloc(dwLen);
memset(p, 0, dwLen);
if (p == NULL)
return;
pOrig = p;
GetBitmapBits(hBitmap, dwLen, p);
stride = bmp.bmWidthBytes;
p += ((y * stride) + (4 * x));
for(line = y; line <= y1; line++) {
pLine = p;
for(pixel = x; pixel <= x1; pixel++) {
alpha = p[3];
if(alpha > 0) {
if(0 == clr)
hsvTransformPixel(p, value, v_s_u, v_s_w, alpha);
else
rgbTransformPixel(p, r, g, b, alpha);
}
p += 4;
}
p = pLine + stride;
}
SetBitmapBits(hBitmap, dwLen, pOrig);
free(pOrig);
}
else if (item->hbm) {
GetObject(item->hbm, sizeof(bmp), &bmp);
if (bmp.bmBitsPixel != 32)
return;
dwLen = bmp.bmWidth * bmp.bmHeight * 4;
p = (BYTE *)malloc(dwLen);
memset(p, 0, dwLen);
if (p == NULL)
return;
pOrig = p;
GetBitmapBits(item->hbm, dwLen, p);
for(pixel = 0; pixel < (bmp.bmWidth * bmp.bmHeight); pixel++) {
alpha = p[3];
if(alpha > 0) {
if(0 == clr)
hsvTransformPixel(p, value, v_s_u, v_s_w, alpha);
else
rgbTransformPixel(p, r, g, b, alpha);
}
p += 4;
}
SetBitmapBits(item->hbm, dwLen, pOrig);
free(pOrig);
}
}
}
/**
* transform a single pixel from hsv into rgb color space
*
* @param *p BYTE* - pointer to a single pixel in (BGRA pixel format!)
* @param value const float - the v component
* @param v_s_u const float - precalculated h/s factor
* @param v_s_w const float
* @param alpha const BYTE - alpha value for the pixel (0..255)
*/
inline void Gfx::hsvTransformPixel(BYTE *p, const float value, const float v_s_u, const float v_s_w, const BYTE alpha)
{
// ain't matrices beautiful? :)
float r = (.299 * value +.701 * v_s_u +.168 * v_s_w) * p[2] + (.587 * value -.587 * v_s_u +.330 * v_s_w) * p[1] + (.114 * value -.114 * v_s_u -.497 * v_s_w) * p[0];
float g = (.299 * value -.299 * v_s_u -.328 * v_s_w) * p[2] + (.587 * value +.413 * v_s_u +.035 * v_s_w) * p[1] + (.114 * value -.114 * v_s_u +.292 * v_s_w) * p[0];
float b = (.299 * value -.3 * v_s_u +1.25 * v_s_w) * p[2]+ (.587* value -.588 * v_s_u -1.05 * v_s_w) * p[1] + (.114 * value +.886 * v_s_u -.203 * v_s_w) * p[0];
/*
* premultiply
*/
p[0] = (int)b * alpha/255;
p[1] = (int)g * alpha/255;
p[2] = (int)r * alpha/255;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment