Jump to content
  • Advertisement
Sign in to follow this  
Austrian Coder

ARGB to YCrCb

This topic is 5398 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi guys, i have here a buffer (w*h) in which every byte contains a colorvalue in ARGB-Format. Now i need a way to convert a) the whole buffer to YCrCb b) one single ARGB value to YCrCb I hope somebody can help me. Thanks, Christian

Share this post


Link to post
Share on other sites
Advertisement
Some conversion code from my wavelet encoder:

/************************************************
* Color space conversion matrix coefficients *
************************************************/


/* all components are scaled up by 15 bits to *
* yield a minimum degree of precision while *
* still using pure integer math */


#define Yr (short)(0.257 * 32768 + 0.5)
#define Yg (short)(0.504 * 32768 + 0.5)
#define Yb (short)(0.098 * 32768 + 0.5)

#define Ur (short)(-0.148 * 32768 + 0.5)
#define Ug (short)(-0.291 * 32768 + 0.5)
#define Ub (short)(0.439 * 32768 + 0.5)

#define Vr (short)(0.439 * 32768 + 0.5)
#define Vg (short)(-0.368 * 32768 + 0.5)
#define Vb (short)(-0.071 * 32768 + 0.5)

static const short Y_COEFF[4] = {
Yb, Yg, Yr, 0x00
};

static const short U_COEFF[4] = {
Ub, Ug, Ur, 0x00
};

static const short V_COEFF[4] = {
Vb, Vg, Vr, 0x00
};

/* SIMD (MMX™) version */
int swciRgbToYuv(swcImage* image) {

dword_ptr src;
byte_ptr y;
byte_ptr u;
byte_ptr v;

int w, h;

if (!image || !image->rgb) {
return SWC_ERR;
}

w = image->cols;
h = image->rows;
src = image->rgb;
y = image->y;
u = image->u;
v = image->v;

__asm {

/* push all double word registers and init FPU */
pushad
finit

/* setup pointers */
mov esi, src
mov edi, y
mov ecx, u
mov edx, v

mov ebx, h

row_loop0:

/* loop y */

push ebx
mov ebx, w

col_loop0:

/* loop x */

push ebx

/* ------------------------------------------------------- */
/* two pixels at once are processed by these routines */
/* ------------------------------------------------------- */

/* load two pixels */
movq mm0, [esi]

movq mm1, mm0
pxor mm2, mm2
/* mm0 = 0|0|0|R1|0|G1|0|B1 */
/* mm1 = 0|0|0|R2|0|G2|0|B2 */
punpcklbw mm0, mm2
punpckhbw mm1, mm2

/* save unpacked 16 rgb vectors */
movq mm3, mm0
movq mm4, mm1

/* compute dot product for two Y components */
movq mm5, [Y_COEFF]
pmaddwd mm0, mm5
pmaddwd mm1, mm5
/* shift to 15 bit precision */
psrad mm0, 15
psrad mm1, 15

/* tricky here - need to use mm6 as accumulator */
movq mm6, mm0
psrlq mm6, 32

/* add intermediate results for final Y calculation */
paddd mm0, mm6
movd eax, mm0
add eax, 16

/* add intermediate results for final Y calculation (second pixel) */
movq mm6, mm1
psrlq mm6, 32

paddd mm1, mm6
movd ebx, mm1
add ebx, 16

/* combine results from eax and ebx to one word */
shl ebx, 8
or eax, ebx

/* save two Y components */
mov [edi], ax

/* process U and v components - same as above */

/* restore unpacked 16 bit vectors */
movq mm0, mm3
movq mm1, mm4

movq mm5, [U_COEFF]
pmaddwd mm0, mm5
pmaddwd mm1, mm5
psrad mm0, 15
psrad mm1, 15

movq mm6, mm0
psrlq mm6, 32

paddd mm0, mm6
movd eax, mm0
add eax, 128

movq mm6, mm1
psrlq mm6, 32

paddd mm1, mm6
movd ebx, mm1
add ebx, 128

/* add the two components */
add eax, ebx

/* save intermediate result */
push eax

/* restore unpacked 16 bit vectors */
movq mm0, mm3
movq mm1, mm4

movq mm5, [V_COEFF]
pmaddwd mm0, mm5
pmaddwd mm1, mm5
psrad mm0, 15
psrad mm1, 15

movq mm6, mm0
psrlq mm6, 32

paddd mm0, mm6
movd eax, mm0
add eax, 128

movq mm6, mm1
psrlq mm6, 32

paddd mm1, mm6
movd ebx, mm1
add ebx, 128

/* add the two components */
add eax, ebx

/* save intermediate result */
push eax

mov ebx, w

/* load two pixels from second scanline */
movq mm0, [esi + ebx*4]
movq mm1, mm0
pxor mm2, mm2

/* mm0 = 0|0|0|R1|0|G1|0|B1 */
/* mm1 = 0|0|0|R2|0|G2|0|B2 */
punpcklbw mm0, mm2
punpckhbw mm1, mm2

/* save unpacked 16 rgb vectors */
movq mm3, mm0
movq mm4, mm1

/* compute dot product for second Y component */
movq mm5, [Y_COEFF]
pmaddwd mm0, mm5
pmaddwd mm1, mm5
psrad mm0, 15
psrad mm1, 15

movq mm6, mm0
psrlq mm6, 32

paddd mm0, mm6
movd eax, mm0
add eax, 16

movq mm6, mm1
psrlq mm6, 32

paddd mm1, mm6
movd ebx, mm1
add ebx, 16
shl ebx, 8
or eax, ebx

/* save Y component */
mov ebx, w
mov [edi + ebx], ax

/* restore unpacked 16 bit vectors */
movq mm0, mm3
movq mm1, mm4

/* IMPORTANT NOTE: need to process V first, since last value on stack */
/* is intermediate result from V components! */

movq mm5, [V_COEFF]
pmaddwd mm0, mm5
pmaddwd mm1, mm5
psrad mm0, 15
psrad mm1, 15

movq mm6, mm0
psrlq mm6, 32

paddd mm0, mm6
movd eax, mm0
add eax, 128

movq mm6, mm1
psrlq mm6, 32

paddd mm1, mm6
movd ebx, mm1
add ebx, 128

/* add the two components */
add eax, ebx

/* restore intermediate result from last scanline */
pop ebx
/* add current values and average them */
add eax, ebx
sar eax, 2

/* store V component */
mov [edx], al

/* restore unpacked 16 bit vectors */
movq mm0, mm3
movq mm1, mm4

movq mm5, [U_COEFF]
pmaddwd mm0, mm5
pmaddwd mm1, mm5
psrad mm0, 15
psrad mm1, 15

movq mm6, mm0
psrlq mm6, 32

paddd mm0, mm6
movd eax, mm0
add eax, 128

movq mm6, mm1
psrlq mm6, 32

paddd mm1, mm6
movd ebx, mm1
add ebx, 128

/* add the two components */
add eax, ebx

/* restore intermediate result from last scanline */
pop ebx
/* add current values and average them */
add eax, ebx
sar eax, 2

/* store U component */
mov [ecx], al

/* increment Y,U,V target pointers */
add edi, 2
inc ecx
inc edx

/* increment source pointer */
add esi, 8

/* endloop x */
pop ebx
sub ebx, 2
jnz col_loop0

/* skip to next scanline */
mov ebx, w
add edi, ebx
shl ebx, 2
add esi, ebx

/* endloop y */
pop ebx
sub ebx, 2
jnz row_loop0

emms
popad
}

return SWC_OK;
}


int swciYuvToRgb(swcImage* image) {

dword_ptr tmp;
int w, h;
byte_ptr y, u, v;

if (!image || !image->rgb) {
/* invalid params */
return SWC_ERR;
}

/* copy image properties to local variables to make them accessable from
* within our inline assembly core
*/


tmp = image->rgb;
w = image->cols;
h = image->rows;
y = image->y;
u = image->u;
v = image->v;

__asm {
pushad /* save all dword registers */
finit /* initialize FPU */

/* actual MMX implementation follows here */

xor eax, eax /* clear eax to zero */

mov ebx, h /* store image height in ebx */
mov ecx, w /* store image width in ecx */
mov edx, y
mov edi, u
mov esi, v

row_loop:

push ebx /* save image height - we use ebx for many things */
mov ebx, ecx /* setup col count */

col_loop:

push ebx /* save col count - we mess with ebx later */
xor ebx, ebx /* clear all 32 bits */

/* mm0 = Bu | Gu + Gv | Rv | 0000 */
mov al, [edi]
mov bl, [esi]
movq mm0, [CoefRgbU + 8 * eax]
paddw mm0, [CoefRgbV + 8 * ebx]

/* for each U,V pair there are four Y components to process (YUV420!) */

/* first scanline */
/* mm1 = By0 | Gy0 | Ry0 | 0000, mm1 = By1 | Gy1 | Ry1 | 0000 */
mov al, [edx]
mov bl, [edx+1]
movq mm1, [CoefRgbY + 8 * eax]
movq mm2, [CoefRgbY + 8 * ebx]

/* next scanline */
/* mm3 = By2 | Gy2 | Ry2 | 0000, mm4 = By3 | Gy3 | Ry3 | 0000 */
mov al, [edx + ecx]
mov bl, [edx + ecx + 1]
movq mm3, [CoefRgbY + 8 * eax]
movq mm4, [CoefRgbY + 8 * ebx]

/* process all four pixels at one iteration */
paddw mm1, mm0
paddw mm2, mm0
paddw mm3, mm0
paddw mm4, mm0

/* divide by 64 to scale to byte values */
psraw mm1, 6
psraw mm2, 6
psraw mm3, 6
psraw mm4, 6

/* now we need to clamp values - packuswb comes in handy here:
* values below zero are clamped to zero, values above 255 are set to 255
*/

packuswb mm1, mm2
packuswb mm3, mm4

/* now store the for pixels from mm1 and mm3 into target */

mov ebx, tmp
movntq [ebx], mm1
movq [ebx + 4 * ecx], mm3

/* proceed to next pixel pair */
add tmp, 8
add edx, 2
add edi, 1
add esi, 1

/* loop */
pop ebx
sub ebx, 2
jnz col_loop

/* skip to next row */
mov ebx, tmp
lea ebx, [ebx + 4*ecx]
add edx, ecx
mov tmp, ebx

pop ebx
sub ebx, 2
jnz row_loop

emms /* clear FPU state */
popad /* pop all dword registers back from stack :) */
};

return SWC_OK;
}




Hope it's useful,
Pat.

[edit]
You cannot convert alpha! Only color components can be transformed to YCrCb.
General conversion works by multiplying the rgb color vector with the conversion matrix as provided in the code sample.
Just invert the matrix to get the inverse transform. You can also google for these values as I used a standard PAL matrix. You might want to use other coefficients for NTSC or SECAM.
[/edit]

Share this post


Link to post
Share on other sites
Opps, brain-fart. Ignore the code I posted. It transforms to YUV422
, which is not what you want.

At the heart of the color-space conversion is the matrix-multiply calculation. The multiplier coefficients depend on two variables: the range of the Y/CbCr output and the range of the RGB input. (Note: the offsets of 64 or 512 indicated in these equations are accomplished in a separate ‘bias adder’ function near the output)

The RGB to YcbCr matrix-multiply and offset equations are as follows:
Y = (Y Range/RGB Range)(0.299R + 0.587G + 0.114B) + 64
Cb = (Cb Range/RGB Range)(-0.1687R - 0.3313G + 0.5B) + 512
Cr = (Cr Range/RGB Range)(0.5R - 0.4187G - 0.0813B) + 512

You may look at the jpeglib source code, too. They have implemented this conversion (also using MMX, SSE and 3DNow! assembly).

Hope this helps,
Pat.

Share this post


Link to post
Share on other sites
after a quick shifty at your posting history i'm guessing you are using OpenGL, if so you might want to look at using the Color Matrix to do your color space conversion.
The bonus is the color space conversion is basically free as it is ALWAYS performed and cant be disabled [smile].

Share this post


Link to post
Share on other sites
just a word of warning about the OpenGL color matrix: I've had some different OpenGL drivers (different chip manufacturers, not just card models or driver versions) that completely ignored my color matrix stuff with no warnings (I was trying to do some grey-scale effects). I'm not sure how to test for a working implementation. And this was a while back...so maybe everything is fixed by now. Sorry if that didn't make any sense, I'm beat.

Share this post


Link to post
Share on other sites
Quote:
Original post by _the_phantom_
after a quick shifty at your posting history i'm guessing you are using OpenGL, if so you might want to look at using the Color Matrix to do your color space conversion.
The bonus is the color space conversion is basically free as it is ALWAYS performed and cant be disabled [smile].

Just because it's conceptually always enabled doesn't mean it's for free. A driver is free to internally skip the color matrix operation when the identity matrix is loaded, and just transfer the pixels as they are (assuming there are no other source to destination format conversions needed). So saying it's for free is a bit much.

Share this post


Link to post
Share on other sites
In addition to that, a driver is free to not support the color matrix at all, since it is part of the optional ARB_imaging subset of GL. And it only applies to pixel transfer operation IIRC.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!