Linux Game Programming for PC & Embedded Systems using SDL
Presented by
Fore June
Author of Windows Fan, Linux Fan

Sample program for converting RGB to YCbCr to DCT coefficients and vice versa.

common.h
rgb_ybr.h
dct_video.h
encode.h
dctplayer.cpp
encode.cpp
dct_video.cpp
rgb_ybr.cpp
Makefile
sample_video.raw



../rgbybr/rgb_ybr.cpp:
/*
  rgb_ybr.cpp
  Containing functions for converting RGB to YCbCr and vice versa.
*/
#include <stdio.h>
#include <stdlib.h>
#include "common.h"
#include "rgb_ybr.h"


/*
  Convert from RGB to YCbCr using  ITU-R recommendation BT.601.
  Y = 0.299R + 0.587G + 0.114B
  Cb = 0.564(B-Y)
  Cr = 0.713(R-Y)

  Integer arithmetic is used to speed up calculations: 
	0.299 ~ 19595 / 2^16,
	0.587 ~ 38470 / 2^16
	.....
  Input: an RGB pixel
  Output: a YCbCr "pixel".
*/
void rgb2ycbcr( RGB &rgb, YCbCr &ycb )
{
  //coefs summed to 65536 ( 1 << 16 ), so Y is always within [0, 255]
  ycb.Y = (short)((19595 * rgb.R + 38470 * rgb.G + 7471 * rgb.B ) >> 16);
  ycb.Cb = (short)( ( 36962 * ( rgb.B - ycb.Y ) ) >> 16);
  ycb.Cr = (short)(( 46727 * ( rgb.R - ycb.Y ) ) >> 16);
}

//just convert an RGB pixel to Y component
void rgb2y( RGB &rgb, short &y )
{
  y = (short)((19595 * rgb.R + 38470 * rgb.G + 7471 * rgb.B ) >> 16);
}

//taking care of round off errors; ensure RGB values lie within [0, 255]
int chop_rgb ( int x )
{
  if ( x < 0 ) {
    x = 0;
  } else if ( x > 255 ) {
    x = 255;
  }
  return x;
}

/*
  Convert from YCbCr to RGB domain. Using ITU-R standard:
	R = Y + 1.402Cr
	G = Y - 0.344Cb - 0.714Cr
	R = B = Y + 1.772Cb
  Integer arithmetic is used to speed up calculations.
*/
void ycbcr2rgb( YCbCr &ycb, RGB &rgb )
{
  int Y = ( int ) ycb.Y << 16;	//same as multiply 65536

  rgb.R = chop_rgb ( ( Y             + (int)91881 * ycb.Cr ) >> 16 );
  rgb.G = chop_rgb ( ( Y  - (int)22544 * ycb.Cb - (int)46793 * ycb.Cr ) >> 16 );
  rgb.B = chop_rgb ( ( Y  + (int) 116129 * ycb.Cb ) >> 16 );
}

/*
  Convert an RGB macro block ( 16x16 ) to 4:2:0 YCbCr sample blocks ( six 8x8 blocks ).
*/
void macroblock2ycbcr ( RGB *macro16x16,  YCbCr_MACRO *ycbcr_macro )
{
  int i, j, k, r;
  short y;
  YCbCr ycb;

  r = k = 0;
  for ( i = 0; i < 16; ++i ) {
    for ( j = 0; j < 16; ++j ) {
      if ( !( i & 1 ) && !( j & 1 ) ) { 	//need one Cb, Cr for every 4 pixels
        rgb2ycbcr ( macro16x16[r], ycb );	//convert to Y and Cb, Cr values
        ycbcr_macro->Y[r] = ycb.Y;
        ycbcr_macro->Cb[k] = ycb.Cb;
        ycbcr_macro->Cr[k] = ycb.Cr;
        k++;
      } else {					//only need the Y component for other 3 pixels
        rgb2y ( macro16x16[r], ycbcr_macro->Y[r] );	
      }
      r++;					//convert every pixel for Y
    }
  }
}

/*
  Convert the 6 8x8 YCbCr sample blocks to RGB macroblock ( 16x16 ).
*/
void ycbcr2macroblock( YCbCr_MACRO *ycbcr_macro, RGB *macro16x16 )
{
  int i, j, k, r;
  short y;
  YCbCr ycb;

  r = k = 0;
  for ( i = 0; i < 16; ++i ) {
    for ( j = 0; j < 16; ++j ) {
      if ( !( i & 1 ) && !( j & 1 ) ) { //one Cb, Cr has been saved for every 4 pixels
        ycb.Y = ycbcr_macro->Y[r];
        ycb.Cb = ycbcr_macro->Cb[k];
        ycb.Cr = ycbcr_macro->Cr[k];
        ycbcr2rgb ( ycb, macro16x16[r]);
        k++;
      } else {
        ycb.Y = ycbcr_macro->Y[r];
        ycbcr2rgb( ycb, macro16x16[r] );
      }
      r++;
    }
  }
}

void print_ymacro ( YCbCr_MACRO ycbcr_macro )
{
  int i, j, k;
  k = 0;
  for ( i = 0; i < 16; ++i ) {
    printf("\n");
    for ( j = 0; j < 16; ++j ) {
      printf("%4d", ycbcr_macro.Y[k++] ); 
      if ( j % 4 == 3 ) printf ( " | " );      
    }
  }
  printf("\n---------------------------------------\n");
  k = 0;
  for ( i = 0; i < 8; ++i ) {
      printf("\n");
      for ( j = 0; j < 8; ++j ){
	printf("%4d", ycbcr_macro.Cb[k++] );
      }
  }
  printf("\n---------------------------------------\n");
  k = 0;
  for ( i = 0; i < 8; ++i ) {
      printf("\n");
      for ( j = 0; j < 8; ++j ){
	printf("%4d", ycbcr_macro.Cr[k++] );
      }
  }
}