shithub: treason

ref: 2a330793160ce56ce985e1723725188b235c26c5
dir: treason/yuv.c

View raw version
#include <u.h>

/* yuv→rgb by Adrien Descamps */

#define clamp(v) ((v)<0?0 : ((v)>255?255:v))
#define FIXED_POINT_VALUE(value, precision) ((int)(((value)*(1<<precision))+0.5))

typedef struct
{
	u8int cb_factor;   // [(255*CbNorm)/CbRange]
	u8int cr_factor;   // [(255*CrNorm)/CrRange]
	u8int g_cb_factor; // [Bf/Gf*(255*CbNorm)/CbRange]
	u8int g_cr_factor; // [Rf/Gf*(255*CrNorm)/CrRange]
	u8int y_factor;    // [(YMax-YMin)/255]
	u8int y_offset;    // YMin
} YUV2RGBParam;

#define YUV2RGB_PARAM(Rf, Bf, YMin, YMax, CbCrRange) \
	{.cb_factor=FIXED_POINT_VALUE(255.0*(2.0*(1-Bf))/CbCrRange, 6), \
	.cr_factor=FIXED_POINT_VALUE(255.0*(2.0*(1-Rf))/CbCrRange, 6), \
	.g_cb_factor=FIXED_POINT_VALUE(Bf/(1.0-Bf-Rf)*255.0*(2.0*(1-Bf))/CbCrRange, 7), \
	.g_cr_factor=FIXED_POINT_VALUE(Rf/(1.0-Bf-Rf)*255.0*(2.0*(1-Rf))/CbCrRange, 7), \
	.y_factor=FIXED_POINT_VALUE(255.0/(YMax-YMin), 7), \
	.y_offset=YMin}

static const YUV2RGBParam YUV2RGB[3] = {
	// ITU-T T.871 (JPEG)
	YUV2RGB_PARAM(0.299, 0.114, 0.0, 255.0, 255.0),
	// ITU-R BT.601-7
	YUV2RGB_PARAM(0.299, 0.114, 16.0, 235.0, 224.0),
	// ITU-R BT.709-6
	YUV2RGB_PARAM(0.2126, 0.0722, 16.0, 235.0, 224.0)
};

void yuv420_rgb24(
	u32int width, u32int height,
	const u8int *Y, const u8int *U, const u8int *V, u32int Y_stride, u32int UV_stride,
	u8int *RGB, u32int RGB_stride)
{
	const YUV2RGBParam *const param = &(YUV2RGB[0]);
	u32int x, y;
	for(y=0; y<(height-1); y+=2)
	{
		const u8int *y_ptr1=Y+y*Y_stride,
			*y_ptr2=Y+(y+1)*Y_stride,
			*u_ptr=U+(y/2)*UV_stride,
			*v_ptr=V+(y/2)*UV_stride;

		u8int *rgb_ptr1=RGB+y*RGB_stride,
			*rgb_ptr2=RGB+(y+1)*RGB_stride;

		for(x=0; x<(width-1); x+=2)
		{
			s8int u_tmp, v_tmp;
			u_tmp = u_ptr[0]-128;
			v_tmp = v_ptr[0]-128;

			//compute Cb Cr color offsets, common to four pixels
			s16int b_cb_offset, r_cr_offset, g_cbcr_offset;
			b_cb_offset = (param->cb_factor*u_tmp)>>6;
			r_cr_offset = (param->cr_factor*v_tmp)>>6;
			g_cbcr_offset = (param->g_cb_factor*u_tmp + param->g_cr_factor*v_tmp)>>7;

			s16int y_tmp;
			y_tmp = (param->y_factor*(y_ptr1[0]-param->y_offset))>>7;
			rgb_ptr1[2] = clamp(y_tmp + r_cr_offset);
			rgb_ptr1[1] = clamp(y_tmp - g_cbcr_offset);
			rgb_ptr1[0] = clamp(y_tmp + b_cb_offset);

			y_tmp = (param->y_factor*(y_ptr1[1]-param->y_offset))>>7;
			rgb_ptr1[5] = clamp(y_tmp + r_cr_offset);
			rgb_ptr1[4] = clamp(y_tmp - g_cbcr_offset);
			rgb_ptr1[3] = clamp(y_tmp + b_cb_offset);

			y_tmp = (param->y_factor*(y_ptr2[0]-param->y_offset))>>7;
			rgb_ptr2[2] = clamp(y_tmp + r_cr_offset);
			rgb_ptr2[1] = clamp(y_tmp - g_cbcr_offset);
			rgb_ptr2[0] = clamp(y_tmp + b_cb_offset);

			y_tmp = (param->y_factor*(y_ptr2[1]-param->y_offset))>>7;
			rgb_ptr2[5] = clamp(y_tmp + r_cr_offset);
			rgb_ptr2[4] = clamp(y_tmp - g_cbcr_offset);
			rgb_ptr2[3] = clamp(y_tmp + b_cb_offset);

			rgb_ptr1 += 6;
			rgb_ptr2 += 6;
			y_ptr1 += 2;
			y_ptr2 += 2;
			u_ptr += 1;
			v_ptr += 1;
		}
	}
}