/*
 *			GPAC - Multimedia Framework C SDK
 *
 *			Authors: Jean Le Feuvre
 *			Copyright (c) Telecom ParisTech 2000-2023
 *					All rights reserved
 *
 *  This file is part of GPAC / common tools sub-project
 *
 *  GPAC is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  GPAC is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */


#include <gpac/tools.h>
#include <gpac/constants.h>
#include <gpac/color.h>

#ifndef GPAC_DISABLE_COMPOSITOR

static GF_Err color_write_nv12_10_to_yuv(GF_VideoSurface *vs_dst, GF_VideoSurface *vs_src, GF_Window *_src_wnd, Bool swap_up);
static GF_Err color_write_yv12_10_to_yuv(GF_VideoSurface *vs_dst, GF_VideoSurface *vs_src, const GF_Window *_src_wnd, Bool swap_up);
static GF_Err color_write_yuv422_10_to_yuv422(GF_VideoSurface *vs_dst, GF_VideoSurface *vs_src, GF_Window *_src_wnd, Bool swap_up);
static GF_Err color_write_yuv422_10_to_yuv(GF_VideoSurface *vs_dst, GF_VideoSurface *vs_src, GF_Window *_src_wnd, Bool swap_up);
static GF_Err color_write_yuv444_10_to_yuv444(GF_VideoSurface *vs_dst, GF_VideoSurface *vs_src, GF_Window *_src_wnd, Bool swap_up);
static GF_Err color_write_yuv444_10_to_yuv(GF_VideoSurface *vs_dst, GF_VideoSurface *vs_src, GF_Window *_src_wnd, Bool swap_up);
static GF_Err color_write_yuv420_to_yuv(GF_VideoSurface *vs_dst, GF_VideoSurface *vs_src, GF_Window *_src_wnd, Bool swap_uv);
static GF_Err color_write_yuv422_to_yuv(GF_VideoSurface *vs_dst, GF_VideoSurface *vs_src, GF_Window *_src_wnd, Bool swap_uv);
static GF_Err color_write_yuv444_to_yuv(GF_VideoSurface *vs_dst, GF_VideoSurface *vs_src, GF_Window *_src_wnd, Bool swap_uv);
static GF_Err color_write_yvyu_to_yuv(GF_VideoSurface *vs_dst, GF_VideoSurface *vs_src, GF_Window *_src_wnd, Bool swap_uv);
static GF_Err color_write_rgb_to_24(GF_VideoSurface *vs_dst, GF_VideoSurface *vs_src, GF_Window *_src_wnd);
static GF_Err color_write_rgb_to_32(GF_VideoSurface *vs_dst, GF_VideoSurface *vs_src, GF_Window *_src_wnd);


static GFINLINE u8 colmask(s32 a, s32 n)
{
	s32 mask = (1 << n) - 1;
	return (u8) (a & (0xff & ~mask)) | ((-((a >> n) & 1)) & mask);
}


/* YUV -> RGB conversion loading two lines at each call */

#define col_clip(a) MAX(0, MIN(255, a))
#define SCALEBITS_OUT	13
#define FIX_OUT(x)		((unsigned short) ((x) * (1L<<SCALEBITS_OUT) + 0.5))

static s32 RGB_Y[256];
static s32 B_U[256];
static s32 G_U[256];
static s32 G_V[256];
static s32 R_V[256];


static s32 yuv2rgb_is_init = 0;
static void yuv2rgb_init(void)
{
	s32 i;
	if (yuv2rgb_is_init) return;
	yuv2rgb_is_init = 1;

	for(i = 0; i < 256; i++) {
		RGB_Y[i] = FIX_OUT(1.164) * (i - 16);
		B_U[i] = FIX_OUT(2.018) * (i - 128);
		G_U[i] = FIX_OUT(0.391) * (i - 128);
		G_V[i] = FIX_OUT(0.813) * (i - 128);
		R_V[i] = FIX_OUT(1.596) * (i - 128);
	}
}

static void yuv_load_lines_planar(unsigned char *dst, s32 dststride, unsigned char *y_src, unsigned char *u_src, unsigned char * v_src, s32 y_stride, s32 uv_stride, s32 width, Bool dst_yuv)
{
	u32 hw, x;
	unsigned char *dst2 = (unsigned char *) dst + dststride;
	unsigned char *y_src2 = (unsigned char *) y_src + y_stride;

	hw = width / 2;
	if (dst_yuv) {
		for (x = 0; x < hw; x++) {
			dst[0] = dst[4] = dst2[0] = dst2[4] = v_src[x];
			dst[1] = dst[5] = dst2[1] = dst2[5] = u_src[x];

			dst[2] = *y_src;
			dst[3] = 0xFF;
			y_src++;

			dst[6] = *y_src;
			dst[7] = 0xFF;
			y_src++;

			dst2[2] = *y_src2;
			dst2[3] = 0xFF;
			y_src2++;

			dst2[6] = *y_src2;
			dst2[7] = 0xFF;
			y_src2++;

			dst += 8;
			dst2 += 8;
		}
		return;
	}
	for (x = 0; x < hw; x++) {
		s32 u, v;
		s32 b_u, g_uv, r_v, rgb_y;

		u = u_src[x];
		v = v_src[x];

		b_u = B_U[u];
		g_uv = G_U[u] + G_V[v];
		r_v = R_V[v];

		rgb_y = RGB_Y[*y_src];
		dst[0] = col_clip( (rgb_y + r_v) >> SCALEBITS_OUT);
		dst[1] = col_clip( (rgb_y - g_uv) >> SCALEBITS_OUT);
		dst[2] = col_clip( (rgb_y + b_u) >> SCALEBITS_OUT);
		dst[3] = 0xFF;
		y_src++;

		rgb_y = RGB_Y[*y_src];
		dst[4] = col_clip( (rgb_y + r_v) >> SCALEBITS_OUT);
		dst[5] = col_clip( (rgb_y - g_uv) >> SCALEBITS_OUT);
		dst[6] = col_clip( (rgb_y + b_u) >> SCALEBITS_OUT);
		dst[7] = 0xFF;
		y_src++;

		rgb_y = RGB_Y[*y_src2];
		dst2[0] = col_clip( (rgb_y + r_v) >> SCALEBITS_OUT);
		dst2[1] = col_clip( (rgb_y - g_uv) >> SCALEBITS_OUT);
		dst2[2] = col_clip( (rgb_y + b_u) >> SCALEBITS_OUT);
		dst2[3] = 0xFF;
		y_src2++;

		rgb_y = RGB_Y[*y_src2];
		dst2[4] = col_clip( (rgb_y + r_v) >> SCALEBITS_OUT);
		dst2[5] = col_clip( (rgb_y - g_uv) >> SCALEBITS_OUT);
		dst2[6] = col_clip( (rgb_y + b_u) >> SCALEBITS_OUT);
		dst2[7] = 0xFF;
		y_src2++;

		dst += 8;
		dst2 += 8;
	}
}
static void yuv422_load_lines_planar(unsigned char *dst, s32 dststride, unsigned char *y_src, unsigned char *u_src, unsigned char * v_src, s32 y_stride, s32 uv_stride, s32 width, Bool dst_yuv)
{
	u32 hw, x;
	unsigned char *dst2 = (unsigned char *)dst + dststride;
	unsigned char *y_src2 = (unsigned char *)y_src + y_stride;
	unsigned char *u_src2 = (unsigned char *)u_src + uv_stride;
	unsigned char *v_src2 = (unsigned char *)v_src + uv_stride;

	hw = width / 2;
	if (dst_yuv) {
		for (x = 0; x < hw; x++) {
			dst[0] = dst[4] = *v_src;
			dst[1] = dst[5] = *u_src;
			dst[2] = *y_src;
			y_src++;
			dst[3] = 0xFF;
			dst[6] = *y_src;
			y_src++;
			dst[7] = 0xFF;

			u_src++;
			v_src++;

			dst2[0] = dst2[4] = *v_src2;
			dst2[1] = dst2[5] = *u_src2;
			dst2[2] = *y_src;
			y_src2++;
			dst2[3] = 0xFF;
			dst2[6] = *y_src;
			y_src2++;
			dst2[7] = 0xFF;

			u_src2++;
			v_src2++;

			dst += 8;
			dst2 += 8;
		}
		return;
	}

	for (x = 0; x < hw; x++) {
		s32 b_u, g_uv, r_v, rgb_y;

		b_u = B_U[*u_src];
		g_uv = G_U[*u_src] + G_V[*v_src];
		r_v = R_V[*v_src];
		rgb_y = RGB_Y[*y_src];
		dst[0] = col_clip((rgb_y + r_v) >> SCALEBITS_OUT);
		dst[1] = col_clip((rgb_y - g_uv) >> SCALEBITS_OUT);
		dst[2] = col_clip((rgb_y + b_u) >> SCALEBITS_OUT);
		dst[3] = 0xFF;
		y_src++;

		rgb_y = RGB_Y[*y_src];
		dst[4] = col_clip((rgb_y + r_v) >> SCALEBITS_OUT);
		dst[5] = col_clip((rgb_y - g_uv) >> SCALEBITS_OUT);
		dst[6] = col_clip((rgb_y + b_u) >> SCALEBITS_OUT);
		dst[7] = 0xFF;
		y_src++;
		u_src++;
		v_src++;

		b_u = B_U[*u_src2];
		g_uv = G_U[*u_src2] + G_V[*v_src2];
		r_v = R_V[*v_src2];
		rgb_y = RGB_Y[*y_src2];
		dst2[0] = col_clip((rgb_y + r_v) >> SCALEBITS_OUT);
		dst2[1] = col_clip((rgb_y - g_uv) >> SCALEBITS_OUT);
		dst2[2] = col_clip((rgb_y + b_u) >> SCALEBITS_OUT);
		dst2[3] = 0xFF;
		y_src2++;

		rgb_y = RGB_Y[*y_src2];
		dst2[4] = col_clip((rgb_y + r_v) >> SCALEBITS_OUT);
		dst2[5] = col_clip((rgb_y - g_uv) >> SCALEBITS_OUT);
		dst2[6] = col_clip((rgb_y + b_u) >> SCALEBITS_OUT);
		dst2[7] = 0xFF;
		y_src2++;
		u_src2++;
		v_src2++;

		dst += 8;
		dst2 += 8;
	}
}
static void yuv444_load_lines_planar(unsigned char *dst, s32 dststride, unsigned char *y_src, unsigned char *u_src, unsigned char * v_src, s32 y_stride, s32 uv_stride, s32 width, Bool dst_yuv)
{
	u32 hw, x;
	unsigned char *dst2 = (unsigned char *)dst + dststride;
	unsigned char *y_src2 = (unsigned char *)y_src + y_stride;
	unsigned char *u_src2 = (unsigned char *)u_src + uv_stride;
	unsigned char *v_src2 = (unsigned char *)v_src + uv_stride;

	hw = width / 2;

	if (dst_yuv) {
		for (x = 0; x < hw; x++) {
			dst[0] = *v_src;
			dst[1] = *u_src;
			dst[2] = *y_src;
			dst[3] = 0xFF;

			y_src++;
			u_src++;
			v_src++;

			dst[4] = *v_src;
			dst[5] = *u_src;
			dst[6] = *y_src;
			dst[7] = 0xFF;

			y_src++;
			u_src++;
			v_src++;

			dst2[0] = *v_src2;
			dst2[1] = *u_src2;
			dst2[2] = *y_src2;
			dst2[3] = 0xFF;

			y_src2++;
			u_src2++;
			v_src2++;

			dst2[4] = *v_src2;
			dst2[5] = *u_src2;
			dst2[6] = *y_src2;
			dst2[7] = 0xFF;

			y_src2++;
			u_src2++;
			v_src2++;

			dst += 8;
			dst2 += 8;
		}
		return;
	}

	for (x = 0; x < hw; x++) {
		s32 b_u, g_uv, r_v, rgb_y;


		b_u = B_U[*u_src];
		g_uv = G_U[*u_src] + G_V[*v_src];
		r_v = R_V[*v_src];
		rgb_y = RGB_Y[*y_src];
		dst[0] = col_clip((rgb_y + r_v) >> SCALEBITS_OUT);
		dst[1] = col_clip((rgb_y - g_uv) >> SCALEBITS_OUT);
		dst[2] = col_clip((rgb_y + b_u) >> SCALEBITS_OUT);
		dst[3] = 0xFF;
		y_src++;
		u_src++;
		v_src++;


		b_u = B_U[*u_src];
		g_uv = G_U[*u_src] + G_V[*v_src];
		r_v = R_V[*v_src];
		rgb_y = RGB_Y[*y_src];
		dst[4] = col_clip((rgb_y + r_v) >> SCALEBITS_OUT);
		dst[5] = col_clip((rgb_y - g_uv) >> SCALEBITS_OUT);
		dst[6] = col_clip((rgb_y + b_u) >> SCALEBITS_OUT);
		dst[7] = 0xFF;
		y_src++;
		u_src++;
		v_src++;


		b_u = B_U[*u_src2];
		g_uv = G_U[*u_src2] + G_V[*v_src2];
		r_v = R_V[*v_src2];
		rgb_y = RGB_Y[*y_src2];
		dst2[0] = col_clip((rgb_y + r_v) >> SCALEBITS_OUT);
		dst2[1] = col_clip((rgb_y - g_uv) >> SCALEBITS_OUT);
		dst2[2] = col_clip((rgb_y + b_u) >> SCALEBITS_OUT);
		dst2[3] = 0xFF;
		y_src2++;
		u_src2++;
		v_src2++;


		b_u = B_U[*u_src2];
		g_uv = G_U[*u_src2] + G_V[*v_src2];
		r_v = R_V[*v_src2];
		rgb_y = RGB_Y[*y_src2];

		dst2[4] = col_clip((rgb_y + r_v) >> SCALEBITS_OUT);
		dst2[5] = col_clip((rgb_y - g_uv) >> SCALEBITS_OUT);
		dst2[6] = col_clip((rgb_y + b_u) >> SCALEBITS_OUT);
		dst2[7] = 0xFF;
		y_src2++;
		u_src2++;
		v_src2++;

		dst += 8;
		dst2 += 8;
	}
}

static void yuv_10_load_lines_planar(unsigned char *dst, s32 dststride, unsigned char *_y_src, unsigned char *_u_src, unsigned char *_v_src, s32 y_stride, s32 uv_stride, s32 width, Bool dst_yuv)
{
	u32 hw, x;
	unsigned char *dst2 = (unsigned char *) dst + dststride;
	unsigned short *y_src2 = (unsigned short *) (_y_src + y_stride);
	unsigned short *y_src = (unsigned short *)_y_src;
	unsigned short *u_src = (unsigned short *)_u_src;
	unsigned short *v_src = (unsigned short *)_v_src;


	hw = width / 2;
	if (dst_yuv) {
		for (x = 0; x < hw; x++) {
			dst[0] = dst[4] = dst2[0] = dst2[4] = v_src[x] >> 2;
			dst[1] = dst[5] = dst2[1] = dst2[5] = u_src[x] >> 2;
			dst[2] = *y_src >> 2;
			y_src++;
			dst[3] = 0xFF;

			dst[6] = *y_src >> 2;
			y_src++;
			dst[7] = 0xFF;

			dst2[2] = *y_src2 >> 2;
			y_src2++;
			dst2[3] = 0xFF;

			dst2[6] = *y_src2 >> 2;
			y_src2++;
			dst2[7] = 0xFF;

			dst += 8;
			dst2 += 8;
		}
		return;
	}
	for (x = 0; x < hw; x++) {
		s32 u, v;
		s32 b_u, g_uv, r_v, rgb_y;

		u = u_src[x] >> 2;
		v = v_src[x] >> 2;

		b_u = B_U[u];
		g_uv = G_U[u] + G_V[v];
		r_v = R_V[v];

		rgb_y = RGB_Y[*y_src >> 2];
		dst[0] = col_clip( (rgb_y + r_v) >> SCALEBITS_OUT);
		dst[1] = col_clip( (rgb_y - g_uv) >> SCALEBITS_OUT);
		dst[2] = col_clip( (rgb_y + b_u) >> SCALEBITS_OUT);
		dst[3] = 0xFF;
		y_src++;

		rgb_y = RGB_Y[*y_src >> 2];
		dst[4] = col_clip( (rgb_y + r_v) >> SCALEBITS_OUT);
		dst[5] = col_clip( (rgb_y - g_uv) >> SCALEBITS_OUT);
		dst[6] = col_clip( (rgb_y + b_u) >> SCALEBITS_OUT);
		dst[7] = 0xFF;
		y_src++;

		rgb_y = RGB_Y[*y_src2 >> 2];
		dst2[0] = col_clip( (rgb_y + r_v) >> SCALEBITS_OUT);
		dst2[1] = col_clip( (rgb_y - g_uv) >> SCALEBITS_OUT);
		dst2[2] = col_clip( (rgb_y + b_u) >> SCALEBITS_OUT);
		dst2[3] = 0xFF;
		y_src2++;

		rgb_y = RGB_Y[*y_src2 >> 2];
		dst2[4] = col_clip( (rgb_y + r_v) >> SCALEBITS_OUT);
		dst2[5] = col_clip( (rgb_y - g_uv) >> SCALEBITS_OUT);
		dst2[6] = col_clip( (rgb_y + b_u) >> SCALEBITS_OUT);
		dst2[7] = 0xFF;
		y_src2++;

		dst += 8;
		dst2 += 8;
	}
}
static void yuv422_10_load_lines_planar(unsigned char *dst, s32 dststride, unsigned char *_y_src, unsigned char *_u_src, unsigned char *_v_src, s32 y_stride, s32 uv_stride, s32 width, Bool dst_yuv)
{
	u32 hw, x;
	unsigned char *dst2 = (unsigned char *)dst + dststride;
	unsigned short *y_src2 = (unsigned short *)(_y_src + y_stride);
	unsigned short *u_src2 = (unsigned short *)(_u_src + uv_stride);
	unsigned short *v_src2 = (unsigned short *)(_v_src + uv_stride);
	unsigned short *y_src = (unsigned short *)_y_src;
	unsigned short *u_src = (unsigned short *)_u_src;
	unsigned short *v_src = (unsigned short *)_v_src;

	hw = width / 2;

	if (dst_yuv) {
		for (x = 0; x < hw; x++) {
			dst[0] = dst[4] = *v_src >> 2;
			dst[1] = dst[5] = *u_src >> 2;
			dst[2] = *y_src >> 2;
			y_src++;
			dst[3] = 0xFF;

			dst[6] = *y_src >> 2;
			y_src++;
			dst[7] = 0xFF;

			dst2[0] = dst2[4] = *v_src2 >> 2;
			dst2[1] = dst2[5] = *u_src2 >> 2;
			dst2[2] = *y_src2 >> 2;
			y_src2++;
			dst2[3] = 0xFF;

			dst2[6] = *y_src2 >> 2;
			y_src2++;
			dst2[7] = 0xFF;

			y_src2++;
			u_src2++;
			v_src2++;

			dst += 8;
			dst2 += 8;
		}
		return;
	}
	for (x = 0; x < hw; x++) {
		s32 b_u, g_uv, r_v, rgb_y;

		b_u = B_U[*u_src >> 2];
		g_uv = G_U[*u_src >> 2] + G_V[*v_src >> 2];
		r_v = R_V[*v_src >> 2];
		rgb_y = RGB_Y[*y_src >> 2];
		dst[0] = col_clip((rgb_y + r_v) >> SCALEBITS_OUT);
		dst[1] = col_clip((rgb_y - g_uv) >> SCALEBITS_OUT);
		dst[2] = col_clip((rgb_y + b_u) >> SCALEBITS_OUT);
		dst[3] = 0xFF;
		y_src++;




		rgb_y = RGB_Y[*y_src >> 2];
		dst[4] = col_clip((rgb_y + r_v) >> SCALEBITS_OUT);
		dst[5] = col_clip((rgb_y - g_uv) >> SCALEBITS_OUT);
		dst[6] = col_clip((rgb_y + b_u) >> SCALEBITS_OUT);
		dst[7] = 0xFF;
		y_src++;
		u_src++;
		v_src++;


		b_u = B_U[*u_src2 >> 2];
		g_uv = G_U[*u_src2 >> 2] + G_V[*v_src2 >> 2];
		r_v = R_V[*v_src2 >> 2];
		rgb_y = RGB_Y[*y_src2 >> 2];
		dst2[0] = col_clip((rgb_y + r_v) >> SCALEBITS_OUT);
		dst2[1] = col_clip((rgb_y - g_uv) >> SCALEBITS_OUT);
		dst2[2] = col_clip((rgb_y + b_u) >> SCALEBITS_OUT);
		dst2[3] = 0xFF;
		y_src2++;

		rgb_y = RGB_Y[*y_src2 >> 2];
		dst2[4] = col_clip((rgb_y + r_v) >> SCALEBITS_OUT);
		dst2[5] = col_clip((rgb_y - g_uv) >> SCALEBITS_OUT);
		dst2[6] = col_clip((rgb_y + b_u) >> SCALEBITS_OUT);
		dst2[7] = 0xFF;
		y_src2++;
		u_src2++;
		v_src2++;

		dst += 8;
		dst2 += 8;
	}
}
static void yuv444_10_load_lines_planar(unsigned char *dst, s32 dststride, unsigned char *_y_src, unsigned char *_u_src, unsigned char *_v_src, s32 y_stride, s32 uv_stride, s32 width, Bool dst_yuv)
{
	u32 hw, x;
	unsigned char *dst2 = (unsigned char *)dst + dststride;
	unsigned short * y_src2 = (unsigned short *)(_y_src + y_stride);
	unsigned short * u_src2 = (unsigned short *)(_u_src + uv_stride);
	unsigned short * v_src2 = (unsigned short *)(_v_src + uv_stride);
	unsigned short * y_src = (unsigned short *)_y_src;
	unsigned short * u_src = (unsigned short *)_u_src;
	unsigned short * v_src = (unsigned short *)_v_src;

	hw = width / 2;
	if (dst_yuv) {
		for (x = 0; x < hw; x++) {
			dst[0] = *v_src >> 2;
			dst[1] = *u_src >> 2;
			dst[2] = *y_src >> 2;
			dst[3] = 0xFF;
			y_src++;
			u_src++;
			v_src++;

			dst[4] = *v_src >> 2;
			dst[5] = *u_src >> 2;
			dst[6] = *y_src >> 2;
			dst[7] = 0xFF;
			y_src++;
			u_src++;
			v_src++;

			dst2[0] = *v_src2 >> 2;
			dst2[1] = *u_src2 >> 2;
			dst2[2] = *y_src2 >> 2;
			dst2[3] = 0xFF;
			y_src2++;
			u_src2++;
			v_src2++;

			dst2[4] = *v_src2 >> 2;
			dst2[5] = *u_src2 >> 2;
			dst2[6] = *y_src2 >> 2;
			dst2[7] = 0xFF;
			y_src2++;
			u_src2++;
			v_src2++;

			dst += 8;
			dst2 += 8;
		}
		return;
	}
	for (x = 0; x < hw; x++) {
		s32 b_u, g_uv, r_v, rgb_y;


		b_u = B_U[*u_src >> 2];
		g_uv = G_U[*u_src >> 2] + G_V[*v_src >> 2];
		r_v = R_V[*v_src >> 2];
		rgb_y = RGB_Y[*y_src >> 2];
		dst[0] = col_clip((rgb_y + r_v) >> SCALEBITS_OUT);
		dst[1] = col_clip((rgb_y - g_uv) >> SCALEBITS_OUT);
		dst[2] = col_clip((rgb_y + b_u) >> SCALEBITS_OUT);
		dst[3] = 0xFF;
		y_src++;
		u_src++;
		v_src++;


		b_u = B_U[*u_src >> 2];
		g_uv = G_U[*u_src >> 2] + G_V[*v_src >> 2];
		r_v = R_V[*v_src >> 2];
		rgb_y = RGB_Y[*y_src >> 2];
		dst[4] = col_clip((rgb_y + r_v) >> SCALEBITS_OUT);
		dst[5] = col_clip((rgb_y - g_uv) >> SCALEBITS_OUT);
		dst[6] = col_clip((rgb_y + b_u) >> SCALEBITS_OUT);
		dst[7] = 0xFF;
		y_src++;
		u_src++;
		v_src++;


		b_u = B_U[*u_src2 >> 2];
		g_uv = G_U[*u_src2 >> 2] + G_V[*v_src2 >> 2];
		r_v = R_V[*v_src2 >> 2];
		rgb_y = RGB_Y[*y_src2 >> 2];
		dst2[0] = col_clip((rgb_y + r_v) >> SCALEBITS_OUT);
		dst2[1] = col_clip((rgb_y - g_uv) >> SCALEBITS_OUT);
		dst2[2] = col_clip((rgb_y + b_u) >> SCALEBITS_OUT);
		dst2[3] = 0xFF;
		y_src2++;
		u_src2++;
		v_src2++;


		b_u = B_U[*u_src2 >> 2];
		g_uv = G_U[*u_src2 >> 2] + G_V[*v_src2 >> 2];
		r_v = R_V[*v_src2 >> 2];
		rgb_y = RGB_Y[*y_src2 >> 2];

		dst2[4] = col_clip((rgb_y + r_v) >> SCALEBITS_OUT);
		dst2[5] = col_clip((rgb_y - g_uv) >> SCALEBITS_OUT);
		dst2[6] = col_clip((rgb_y + b_u) >> SCALEBITS_OUT);
		dst2[7] = 0xFF;
		y_src2++;
		u_src2++;
		v_src2++;

		dst += 8;
		dst2 += 8;
	}
}

static void yuv_load_lines_packed(unsigned char *dst, s32 dststride, unsigned char *y_src, unsigned char *u_src, unsigned char * v_src, s32 width, Bool dst_yuv)
{
	u32 hw;

	hw = width / 2;
	if (dst_yuv) {
		while (hw) {
			hw--;

			dst[0] = dst[4] = *u_src;
			dst[1] = dst[5] = *v_src;
			dst[2] = *y_src;
			dst[3] = 0xFF;
			dst[6] = *(y_src+2);
			dst[7] = 0xFF;

			dst += 8;
			y_src += 4;
			u_src += 4;
			v_src += 4;
		}
		return;
	}
	while (hw) {
		s32 b_u, g_uv, r_v, rgb_y;
		hw--;

		b_u = B_U[*u_src];
		g_uv = G_U[*u_src] + G_V[*v_src];
		r_v = R_V[*v_src];

		rgb_y = RGB_Y[*y_src];
		dst[0] = col_clip( (rgb_y + r_v) >> SCALEBITS_OUT);
		dst[1] = col_clip( (rgb_y - g_uv) >> SCALEBITS_OUT);
		dst[2] = col_clip( (rgb_y + b_u) >> SCALEBITS_OUT);
		dst[3] = 0xFF;

		rgb_y = RGB_Y[*(y_src+2)];
		dst[4] = col_clip( (rgb_y + r_v) >> SCALEBITS_OUT);
		dst[5] = col_clip( (rgb_y - g_uv) >> SCALEBITS_OUT);
		dst[6] = col_clip( (rgb_y + b_u) >> SCALEBITS_OUT);
		dst[7] = 0xFF;

		dst += 8;
		y_src += 4;
		u_src += 4;
		v_src += 4;
	}
}


static void yuva_load_lines(unsigned char *dst, s32 dststride, unsigned char *y_src, unsigned char *u_src, unsigned char *v_src, unsigned char *a_src,
                               s32 y_stride, s32 uv_stride, s32 width, Bool dst_yuv)
{
	u32 hw, x;
	unsigned char *dst2 = dst + dststride;
	unsigned char *y_src2 = y_src + y_stride;
	unsigned char *a_src2 = a_src + y_stride;

	yuv2rgb_init();

	hw = width / 2;
	if (dst_yuv) {
		for (x = 0; x < hw; x++) {

			dst[0] = dst[4] = dst2[0] = dst2[4] = v_src[x];
			dst[1] = dst[5] = dst2[1] = dst2[5] = u_src[x];

			dst[2] = *y_src;
			dst[3] = *a_src;
			y_src++;
			a_src++;

			dst[6] = *y_src;
			dst[7] = *a_src;
			y_src++;
			a_src++;

			dst2[2] = *y_src2;
			dst2[3] = *a_src2;
			y_src2++;
			a_src2++;

			dst2[6] = *y_src2;
			dst2[7] = *a_src2;
			y_src2++;
			a_src2++;

			dst += 8;
			dst2 += 8;
		}
		return;
	}
	for (x = 0; x < hw; x++) {
		s32 u, v;
		s32 b_u, g_uv, r_v, rgb_y;

		u = u_src[x];
		v = v_src[x];

		b_u = B_U[u];
		g_uv = G_U[u] + G_V[v];
		r_v = R_V[v];

		rgb_y = RGB_Y[*y_src];
		dst[0] = col_clip ( (rgb_y + r_v) >> SCALEBITS_OUT );
		dst[1] = col_clip ( (rgb_y - g_uv) >> SCALEBITS_OUT );
		dst[2] = col_clip ( (rgb_y + b_u) >> SCALEBITS_OUT );
		dst[3] = *a_src;
		y_src++;
		a_src++;

		rgb_y = RGB_Y[*y_src];
		dst[4] = col_clip ( (rgb_y + r_v) >> SCALEBITS_OUT );
		dst[5] = col_clip ( (rgb_y - g_uv) >> SCALEBITS_OUT );
		dst[6] = col_clip ( (rgb_y + b_u) >> SCALEBITS_OUT );
		dst[7] = *a_src;
		y_src++;
		a_src++;

		rgb_y = RGB_Y[*y_src2];
		dst2[0] = col_clip ( (rgb_y + r_v) >> SCALEBITS_OUT );
		dst2[1] = col_clip ( (rgb_y - g_uv) >> SCALEBITS_OUT );
		dst2[2] = col_clip ( (rgb_y + b_u) >> SCALEBITS_OUT );
		dst2[3] = *a_src2;
		y_src2++;
		a_src2++;

		rgb_y = RGB_Y[*y_src2];
		dst2[4] = col_clip ( (rgb_y + r_v) >> SCALEBITS_OUT );
		dst2[5] = col_clip ( (rgb_y - g_uv) >> SCALEBITS_OUT );
		dst2[6] = col_clip ( (rgb_y + b_u) >> SCALEBITS_OUT );
		dst2[7] = *a_src2;
		y_src2++;
		a_src2++;

		dst += 8;
		dst2 += 8;
	}
}

static s32 mul255(s32 a, s32 b)
{
	return ((a+1) * b) >> 8;
}

typedef void (*copy_row_proto)(u8 *src, u32 src_w, u8 *_dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha, u32 dst_pitch, u32 dst_height);
typedef void (*load_line_proto)(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 src_width, u32 src_height, u8 *dst_bits, Bool dst_yuv);

static void copy_row_rgb_555(u8 *src, u32 src_w, u8 *_dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha, u32 dst_pitch, u32 dst_height)
{
	s32 pos;
	u16 *dst = (u16 *)_dst;
	u8 a=0, r=0, g=0, b=0;
	x_pitch /= 2;
	pos = 0x10000;
	while (dst_w) {
		while ( pos >= 0x10000L ) {
			r = *src++;
			g = *src++;
			b = *src++;
			a = *src++;
			pos -= 0x10000L;
		}
		if (a) *dst = GF_COL_555(r, g, b);
		dst += x_pitch;
		pos += h_inc;
		dst_w--;
	}
}

static void copy_row_rgb_565(u8 *src, u32 src_w, u8 *_dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha, u32 dst_pitch, u32 dst_height)
{
	s32 pos;
	u16 *dst = (u16 *)_dst;
	u8 a=0, r=0, g=0, b=0;
	x_pitch /= 2;
	pos = 0x10000;
	while (dst_w) {
		while ( pos >= 0x10000L ) {
			r = *src++;
			g = *src++;
			b = *src++;
			a = *src++;
			pos -= 0x10000L;
		}
		if (a) *dst = GF_COL_565(r, g, b);
		dst += x_pitch;
		pos += h_inc;
		dst_w--;
	}
}


static void copy_row_rgb_24(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha, u32 dst_pitch, u32 dst_height)
{
	s32 pos;
	u8 a=0, r=0, g=0, b=0;

	pos = 0x10000;
	while (dst_w) {
		while ( pos >= 0x10000L ) {
			r = *src++;
			g = *src++;
			b = *src++;
			a = *src++;
			pos -= 0x10000L;
		}
		if (a) {
			dst[0] = r;
			dst[1] = g;
			dst[2] = b;
		}
		dst += x_pitch;
		pos += h_inc;
		dst_w--;
	}
}

static void copy_row_bgr_24(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha, u32 dst_pitch, u32 dst_height)
{
	s32 pos;
	u8 a=0, r=0, g=0, b=0;

	pos = 0x10000;
	while (dst_w) {
		while ( pos >= 0x10000L ) {
			r = *src++;
			g = *src++;
			b = *src++;
			a = *src++;
			pos -= 0x10000L;
		}
		if (a) {
			dst[0] = b;
			dst[1] = g;
			dst[2] = r;
		}
		dst += x_pitch;
		pos += h_inc;
		dst_w--;
	}
}

static void copy_row_bgrx(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha, u32 dst_pitch, u32 dst_height)
{
	u8 a=0, r=0, g=0, b=0;
	s32 pos = 0x10000L;

	while (dst_w) {
		while ( pos >= 0x10000L ) {
			r = *src++;
			g = *src++;
			b = *src++;
			a = *src++;
			pos -= 0x10000L;
		}
		if (a) {
			dst[0] = b;
			dst[1] = g;
			dst[2] = r;
			dst[3] = 0xFF;
		}
		dst += x_pitch;
		pos += h_inc;
		dst_w--;
	}
}


static void copy_row_argb(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha, u32 dst_pitch, u32 dst_height)
{
	u8 a=0, r=0, g=0, b=0;
	s32 pos = 0x10000L;

	while (dst_w) {
		while ( pos >= 0x10000L ) {
			r = *src++;
			g = *src++;
			b = *src++;
			a = *src++;
			pos -= 0x10000L;
		}
		if (a) {
			dst[0] = 0xFF;
			dst[1] = r;
			dst[2] = g;
			dst[3] = b;
		}
		dst += x_pitch;
		pos += h_inc;
		dst_w--;
	}
}

static void copy_row_rgbx(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha, u32 dst_pitch, u32 dst_height)
{
	u8 a=0, r=0, g=0, b=0;
	s32 pos = 0x10000L;

	while ( dst_w) {
		while ( pos >= 0x10000L ) {
			r = *src++;
			g = *src++;
			b = *src++;
			a = *src++;
			pos -= 0x10000L;
		}
		if (a) {
			dst[0] = r;
			dst[1] = g;
			dst[2] = b;
			dst[3] = 0xFF;
		}
		dst+=x_pitch;
		pos += h_inc;
		dst_w--;
	}
}

static void copy_row_rgbd(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha, u32 dst_pitch, u32 dst_height)
{
	u8 a=0, r=0, g=0, b=0;
	s32 pos = 0x10000L;

	while ( dst_w) {
		while ( pos >= 0x10000L ) {
			r = *src++;
			g = *src++;
			b = *src++;
			a = *src++;
			pos -= 0x10000L;
		}
		dst[0] = r;
		dst[1] = g;
		dst[2] = b;
		dst[3] = a;

		dst+=x_pitch;
		pos += h_inc;
		dst_w--;
	}
}
#if 0
static void copy_row_yuv444(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha, u32 dst_pitch, u32 dst_height)
{
	s32 pos;
	u8 *dY, *dU, *dV;
	u8 a=0, y=0, u=0, v=0;

	dY = dst;
	dU = dst + dst_height * dst_pitch;
	dV = dU + dst_height * dst_pitch;

	pos = 0x10000;
	while (dst_w) {
		while ( pos >= 0x10000L ) {
			v = *src++;
			u = *src++;
			y = *src++;
			a = *src++;
			pos -= 0x10000L;
		}
		if (a) {
			*dV = v;
			*dU = u;
			*dY = y;
		}
		dY++;
		dU++;
		dV++;

		pos += h_inc;
		dst_w--;
	}
}
#endif

static void merge_row_rgb_555(u8 *src, u32 src_w, u8 *_dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha, u32 dst_pitch, u32 dst_height)
{
	u32 _r, _g, _b, a=0, r=0, g=0, b=0;
	s32 pos;
	u16 col, *dst = (u16 *)_dst;
	x_pitch /= 2;
	pos = 0x10000;
	while (dst_w) {
		while ( pos >= 0x10000L ) {
			r = *src++;
			g = *src++;
			b = *src++;
			a = *src++;
			pos -= 0x10000L;
			a = mul255(a, alpha);
		}
		if (a && alpha) {
			col = *dst;
			_r = colmask(col >> (10 - 3), 3);
			_g = colmask(col >> (5 - 3), 3);
			_b = colmask(col << 3, 3);

			_r = mul255(a, r - _r) + _r;
			_g = mul255(a, g - _g) + _g;
			_b = mul255(a, b - _b) + _b;
			*dst = GF_COL_555(_r, _g, _b);
		}
		dst += x_pitch;
		pos += h_inc;
		dst_w--;
	}
}

static void merge_row_rgb_565(u8 *src, u32 src_w, u8 *_dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha, u32 dst_pitch, u32 dst_height)
{
	u32 _r, _g, _b, a=0, r=0, g=0, b=0;
	s32 pos;
	u16 col, *dst = (u16 *)_dst;
	x_pitch /= 2;
	pos = 0x10000;
	while (dst_w) {
		while ( pos >= 0x10000L ) {
			r = *src++;
			g = *src++;
			b = *src++;
			a = *src++;
			pos -= 0x10000L;
			a = mul255(a, alpha);
		}
		if (a) {
			col = *dst;
			_r = (col >> 8) & 0xf8;
			_g = (col >> 3) & 0xfc;
			_b = (col << 3) & 0xf8;
			_r = mul255(a, r - _r) + _r;
			_g = mul255(a, g - _g) + _g;
			_b = mul255(a, b - _b) + _b;
			*dst = GF_COL_565(_r, _g, _b);
		}
		dst += x_pitch;
		pos += h_inc;
		dst_w--;
	}
}


static void merge_row_rgb_24(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha, u32 dst_pitch, u32 dst_height)
{
	u32 _r, _g, _b, a=0, r=0, g=0, b=0;
	s32 pos;

	pos = 0x10000;
	while (dst_w) {
		while ( pos >= 0x10000L ) {
			r = *src++;
			g = *src++;
			b = *src++;
			a = *src++;
			pos -= 0x10000L;
			a = mul255(a, alpha);
		}
		if (a) {
			_r = dst[0];
			_g = dst[0];
			_b = dst[0];
			dst[0] = mul255(a, r - _r) + _r;
			dst[1] = mul255(a, g - _g) + _g;
			dst[2] = mul255(a, b - _b) + _b;
		}
		dst += x_pitch;
		pos += h_inc;
		dst_w--;
	}
}


#if 0
static void merge_row_yuv444(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha, u32 dst_pitch, u32 dst_height)
{
	u32 _r, _g, _b, a=0, r=0, g=0, b=0;
	s32 pos;
	u8 *dY, *dU, *dV;

	dY = dst;
	dU = dst + dst_height * dst_pitch;
	dV = dU + dst_height * dst_pitch;

	pos = 0x10000;
	while (dst_w) {
		while ( pos >= 0x10000L ) {
			r = *src++;
			g = *src++;
			b = *src++;
			a = *src++;
			pos -= 0x10000L;
			a = mul255(a, alpha);
		}
		if (a) {
			_r = dY[0];
			_g = dU[0];
			_b = dV[0];
			dY[0] = mul255(a, r - _r) + _r;
			dU[1] = mul255(a, g - _g) + _g;
			dV[2] = mul255(a, b - _b) + _b;
		}
		dY++;
		dU++;
		dV++;

		pos += h_inc;
		dst_w--;
	}
}
#endif

static void merge_row_bgr_24(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha, u32 dst_pitch, u32 dst_height)
{
	u32 _r, _g, _b, a=0, r=0, g=0, b=0;
	s32 pos;

	pos = 0x10000;
	while (dst_w) {
		while ( pos >= 0x10000L ) {
			r = *src++;
			g = *src++;
			b = *src++;
			a = *src++;
			pos -= 0x10000L;
		}

		if (a && alpha) {
			_b = dst[0];
			_g = dst[1];
			_r = dst[2];
			a = mul255(a, alpha);
			dst[0] = mul255(a, b - _b) + _b;
			dst[1] = mul255(a, g - _g) + _g;
			dst[2] = mul255(a, r - _r) + _r;
		}
		dst += x_pitch;
		pos += h_inc;
		dst_w--;
	}
}


static void merge_row_bgrx(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha, u32 dst_pitch, u32 dst_height)
{
	u32 _r, _g, _b, a=0, r=0, g=0, b=0;
	s32 pos;

	pos = 0x10000;
	while (dst_w) {
		while ( pos >= 0x10000L ) {
			r = *src++;
			g = *src++;
			b = *src++;
			a = *src++;
			a = mul255(a, alpha);
			pos -= 0x10000L;
		}

		if (a) {
			_b = dst[0];
			_g = dst[1];
			_r = dst[2];

			_r = mul255(a, r - _r) + _r;
			_g = mul255(a, g - _g) + _g;
			_b = mul255(a, b - _b) + _b;

			dst[0] = _b;
			dst[1] = _g;
			dst[2] = _r;
			dst[3] = 0xFF;
		}
		dst += x_pitch;
		pos += h_inc;
		dst_w--;
	}
}

static void merge_row_rgbx(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha, u32 dst_pitch, u32 dst_height)
{
	u32 _r, _g, _b, a=0, r=0, g=0, b=0;
	s32 pos;

	pos = 0x10000;
	while (dst_w) {
		while ( pos >= 0x10000L ) {
			r = *src++;
			g = *src++;
			b = *src++;
			a = *src++;
			a = mul255(a, alpha);
			pos -= 0x10000L;
		}

		if (a) {
			_r = dst[0];
			_g = dst[1];
			_b = dst[2];
			_r = mul255(a, r - _r) + _r;
			_g = mul255(a, g - _g) + _g;
			_b = mul255(a, b - _b) + _b;
			dst[0] = _r;
			dst[1] = _g;
			dst[2] = _b;
			dst[3] = 0xFF;
		}
		dst += x_pitch;
		pos += h_inc;
		dst_w--;
	}
}


static void merge_row_bgra(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha, u32 dst_pitch, u32 dst_height)
{
	u32 _a, _r, _g, _b, a=0, r=0, g=0, b=0;
	s32 pos;

	pos = 0x10000;
	while (dst_w) {
		while ( pos >= 0x10000L ) {
			r = *src++;
			g = *src++;
			b = *src++;
			a = *src++;
			pos -= 0x10000L;
			a = mul255(a, alpha);
		}

		if (a) {
			_b = dst[0];
			_g = dst[1];
			_r = dst[2];
			if (dst[3]) {
				_a = mul255(a, a) + mul255(0xFF-a, 0xFF);
				_r = mul255(a, r - _r) + _r;
				_g = mul255(a, g - _g) + _g;
				_b = mul255(a, b - _b) + _b;
				dst[0] = _b;
				dst[1] = _g;
				dst[2] = _r;
				dst[3] = _a;
			} else {
				dst[0] = b;
				dst[1] = g;
				dst[2] = r;
				dst[3] = a;
			}
		}
		dst += x_pitch;
		pos += h_inc;
		dst_w--;
	}
}


static void merge_row_argb(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha, u32 dst_pitch, u32 dst_height)
{
	u32 _a, _r, _g, _b, a=0, r=0, g=0, b=0;
	s32 pos;

	pos = 0x10000;
	while (dst_w) {
		while ( pos >= 0x10000L ) {
			r = *src++;
			g = *src++;
			b = *src++;
			a = *src++;
			pos -= 0x10000L;
			a = mul255(a, alpha);
		}

		if (a) {
			_r = dst[1];
			_g = dst[2];
			_b = dst[3];
			if (dst[0]) {
				_a = mul255(a, a) + mul255(0xFF-a, 0xFF);
				_r = mul255(a, r - _r) + _r;
				_g = mul255(a, g - _g) + _g;
				_b = mul255(a, b - _b) + _b;
				dst[0] = _a;
				dst[1] = _r;
				dst[2] = _g;
				dst[3] = _b;
			} else {
				dst[0] = a;
				dst[1] = b;
				dst[2] = g;
				dst[3] = r;
			}
		}
		dst += x_pitch;
		pos += h_inc;
		dst_w--;
	}
}

static void merge_row_rgba(u8 *src, u32 src_w, u8 *dst, u32 dst_w, s32 h_inc, s32 x_pitch, u8 alpha, u32 dst_pitch, u32 dst_height)
{
	u32 _a, _r, _g, _b, a=0, r=0, g=0, b=0;
	s32 pos;
	pos = 0x10000;
	while (dst_w) {
		while ( pos >= 0x10000L ) {
			r = *src++;
			g = *src++;
			b = *src++;
			a = *src++;
			pos -= 0x10000L;
			a = mul255(a, alpha);
		}

		if (a) {
			_r = dst[0];
			_g = dst[1];
			_b = dst[2];
			if (dst[3]) {
				_a = mul255(a, a) + mul255(0xFF-a, 0xFF);
				_r = mul255(a, r - _r) + _r;
				_g = mul255(a, g - _g) + _g;
				_b = mul255(a, b - _b) + _b;
				dst[0] = _r;
				dst[1] = _g;
				dst[2] = _b;
				dst[3] = _a;
			} else {
				dst[0] = r;
				dst[1] = g;
				dst[2] = b;
				dst[3] = a;
			}
		}
		dst += x_pitch;
		pos += h_inc;
		dst_w--;
	}
}


static void load_line_grey(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits, Bool dst_yuv)
{
	u32 i;
	src_bits += x_offset + y_offset*y_pitch;
	for (i=0; i<width; i++) {
		dst_bits[0] = dst_bits[1] = dst_bits[2] = *src_bits++;
		dst_bits[3] = 0xFF;
		dst_bits+=4;
	}
}

static void load_line_alpha_grey(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits, Bool dst_yuv)
{
	u32 i;
	src_bits += x_offset*2 + y_offset*y_pitch;
	for (i=0; i<width; i++) {
		dst_bits[0] = dst_bits[1] = dst_bits[2] = *src_bits++;
		dst_bits[3] = *src_bits++;
		dst_bits+=4;
	}
}

static void load_line_grey_alpha(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits, Bool dst_yuv)
{
	u32 i;
	src_bits += x_offset*2 + y_offset*y_pitch;
	for (i=0; i<width; i++) {
		dst_bits[3] = *src_bits++;
		dst_bits[0] = dst_bits[1] = dst_bits[2] = *src_bits++;
		dst_bits+=4;
	}
}

static void load_line_rgb_555(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits, Bool dst_yuv)
{
	u32 i;
	src_bits += x_offset*3 + y_offset*y_pitch;
	for (i=0; i<width; i++) {
		u16 c = *((u16*)src_bits + i);
		dst_bits[0] = colmask(c >> (10 - 3), 3);
		dst_bits[1] = colmask(c >> (5 - 3), 3);
		dst_bits[2] = colmask(c << 3, 3);
		dst_bits[3] = 0xFF;
		dst_bits+=4;
	}
}

static void load_line_rgb_565(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits, Bool dst_yuv)
{
	u32 i;
	src_bits += x_offset*3 + y_offset*y_pitch;
	for (i=0; i<width; i++) {
		u16 c = *((u16*)src_bits + i);
		dst_bits[0] = colmask(c >> (11 - 3), 3);
		dst_bits[1] = colmask(c >> (5 - 2), 2);
		dst_bits[2] = colmask(c << 3, 3);
		dst_bits[3] = 0xFF;
		dst_bits+=4;
	}
}

static void load_line_rgb_24(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits, Bool dst_yuv)
{
	u32 i;
	src_bits += x_offset*3 + y_offset*y_pitch;
	for (i=0; i<width; i++) {
		dst_bits[0] = *src_bits++;
		dst_bits[1] = *src_bits++;
		dst_bits[2] = *src_bits++;
		dst_bits[3] = 0xFF;
		dst_bits+=4;
	}
}

static void load_line_bgr_24(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits, Bool dst_yuv)
{
	u32 i;
	src_bits += x_offset*3 + y_offset*y_pitch;
	for (i=0; i<width; i++) {
		dst_bits[2] = *src_bits++;
		dst_bits[1] = *src_bits++;
		dst_bits[0] = *src_bits++;
		dst_bits[3] = 0xFF;
		dst_bits+=4;
	}
}

static void load_line_rgb_32(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits, Bool dst_yuv)
{
	u32 i;
	src_bits += x_offset*4 + y_offset*y_pitch;
	for (i=0; i<width; i++) {
		dst_bits[0] = *src_bits++;
		dst_bits[1] = *src_bits++;
		dst_bits[2] = *src_bits++;
		dst_bits[3] = *src_bits++;
		dst_bits += 4;
	}
}
static void load_line_xrgb(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits, Bool dst_yuv)
{
	u32 i;
	src_bits += x_offset*4 + y_offset*y_pitch;
	for (i=0; i<width; i++) {
		src_bits++;
		dst_bits[0] = *src_bits++;
		dst_bits[1] = *src_bits++;
		dst_bits[2] = *src_bits++;
		dst_bits[3] = 0xFF;
		dst_bits += 4;
	}
}
static void load_line_bgrx(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits, Bool dst_yuv)
{
	u32 i;
	src_bits += x_offset*4 + y_offset*y_pitch;
	for (i=0; i<width; i++) {
		dst_bits[2] = *src_bits++;
		dst_bits[1] = *src_bits++;
		dst_bits[0] = *src_bits++;
		dst_bits[3] = 0xFF;
		src_bits++;
		dst_bits += 4;
	}
}

static void load_line_rgbd(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits, Bool dst_yuv)
{
	u32 i;
	src_bits += x_offset*4 + y_offset*y_pitch;
	for (i=0; i<width; i++) {
		dst_bits[0] = *src_bits++;
		dst_bits[1] = *src_bits++;
		dst_bits[2] = *src_bits++;
		dst_bits[3] = 0xFF;
		src_bits++;
		dst_bits += 4;
	}
}

static void load_line_rgbds(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits, Bool dst_yuv)
{
	u32 i;
	src_bits += x_offset*4 + y_offset*y_pitch;
	for (i=0; i<width; i++) {
		dst_bits[0] = *src_bits++;
		dst_bits[1] = *src_bits++;
		dst_bits[2] = *src_bits++;
		dst_bits[3] = (( *src_bits++) & 0x80) ? 255 : 0;
		dst_bits += 4;
	}
}

static void load_line_bgra(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits, Bool dst_yuv)
{
	u32 i;
	src_bits += x_offset*4 + y_offset*y_pitch;
	for (i=0; i<width; i++) {
		dst_bits[2] = *src_bits++;
		dst_bits[1] = *src_bits++;
		dst_bits[0] = *src_bits++;
		dst_bits[3] = *src_bits++;
		dst_bits += 4;
	}
}

static void load_line_argb(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits, Bool dst_yuv)
{
	u32 i;
	src_bits += x_offset*4 + y_offset*y_pitch;
	for (i=0; i<width; i++) {
		dst_bits[3] = *src_bits++;
		dst_bits[0] = *src_bits++;
		dst_bits[1] = *src_bits++;
		dst_bits[2] = *src_bits++;
		dst_bits += 4;
	}
}
static void load_line_yv12(char *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits, u8 *pU, u8 *pV, Bool dst_yuv)
{
	u8 *pY;
	pY = (u8 *)src_bits;
	if (!pU) {
		pU = (u8 *)src_bits + y_pitch*height;
		pV = (u8 *)src_bits + 5*y_pitch*height/4;
	}

	pY += x_offset + y_offset*y_pitch;
	pU += x_offset/2 + y_offset*y_pitch/4;
	pV += x_offset/2 + y_offset*y_pitch/4;
	yuv_load_lines_planar((unsigned char*)dst_bits, 4*width, pY, pU, pV, y_pitch, y_pitch/2, width, dst_yuv);
}
static void load_line_yuv422(char *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits, u8 *pU, u8 *pV, Bool dst_yuv)
{
	u8 *pY;
	pY = (u8 *)src_bits;
	if (!pU) {
		pU = (u8 *)src_bits + y_pitch*height;
		pV = (u8 *)src_bits + 3 * y_pitch*height / 2;
	}
	
	pY += x_offset + y_offset*y_pitch;
	pU += x_offset / 2 + y_offset*y_pitch / 2;
	pV += x_offset / 2 + y_offset*y_pitch / 2;
	yuv422_load_lines_planar((unsigned char*)dst_bits, 4 * width, pY, pU, pV, y_pitch, y_pitch / 2, width, dst_yuv);
}
static void load_line_yuv444(char *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits, u8 *pU, u8 *pV, Bool dst_yuv)
{
	u8 *pY;
	pY = (u8 *)src_bits;
	if (!pU) {
		pU = (u8 *)src_bits + y_pitch*height;
		pV = (u8 *)src_bits + 2 * y_pitch*height;
	}

	
	pY += x_offset + y_offset*y_pitch;
	pU += x_offset + y_offset*y_pitch;
	pV += x_offset + y_offset*y_pitch;
	yuv444_load_lines_planar((unsigned char*)dst_bits, 4 * width, pY, pU, pV, y_pitch, y_pitch, width, dst_yuv);
}
static void load_line_yv12_10(char *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits, u8 *pU, u8 *pV, Bool dst_yuv)
{
	u8 *pY;
	pY = (u8 *)src_bits;
	if (!pU) {
		pU = (u8 *)src_bits + y_pitch*height;
		pV = (u8 *)src_bits + 5*y_pitch*height/4;
	}

	pY += x_offset + y_offset*y_pitch;
	pU += x_offset/2 + y_offset*y_pitch/4;
	pV += x_offset/2 + y_offset*y_pitch/4;
	yuv_10_load_lines_planar((unsigned char*)dst_bits, 4*width, pY, pU, pV, y_pitch, y_pitch/2, width, dst_yuv);
}
static void load_line_yuv422_10(char *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits, u8 *pU, u8 *pV, Bool dst_yuv)
{
	u8 *pY;
	u16  *src_y, *src_u, *src_v;
	pY = (u8 *)src_bits;
	if (!pU) {
		pU = (u8 *)src_bits + y_pitch*height;
		pV = (u8 *)src_bits + 3 * y_pitch*height / 2;
	}
	src_y = (u16 *)pY + x_offset;
	src_u = (u16 *)pU + x_offset / 2;
	src_v = (u16 *)pV + x_offset / 2;
	

	pY = (u8 *)src_y + y_offset*y_pitch;
	pU = (u8 *)src_u + y_offset*y_pitch / 2;
	pV = (u8 *)src_v + y_offset*y_pitch / 2;
	yuv422_10_load_lines_planar((unsigned char*)dst_bits, 4 * width, pY, pU, pV, y_pitch, y_pitch / 2, width, dst_yuv);
}
static void load_line_yuv444_10(char *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits, u8 *pU, u8 *pV, Bool dst_yuv)
{
	u8 *pY;
	u16  *src_y, *src_u, *src_v;
	pY = (u8 *)src_bits;
	if (!pU) {
		pU = (u8 *)src_bits + y_pitch*height;
		pV = (u8 *)src_bits + 2 * y_pitch*height;
	}
	 src_y = (u16 *)pY + x_offset;
	 src_u = (u16 *)pU + x_offset;
	 src_v = (u16 *)pV + x_offset;
	

	pY = (u8 *)src_y + y_offset*y_pitch;
	pU = (u8 *)src_u + y_offset*y_pitch;
	pV = (u8 *)src_v + y_offset*y_pitch;
	yuv444_10_load_lines_planar((unsigned char*)dst_bits, 4 * width, pY, pU, pV, y_pitch, y_pitch, width, dst_yuv);
}
static void load_line_yuva(char *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits, u8 *pU, u8 *pV, u8 *pA, Bool dst_yuv)
{
	u8 *pY;
	pY = (u8*)src_bits;
	if (!pU) {
		pU = (u8*)src_bits + y_pitch*height;
		pV = (u8*)src_bits + 5*y_pitch*height/4;
		pA = (u8*)src_bits + 3*y_pitch*height/2;
	}

	pY += x_offset + y_offset*y_pitch;
	pU += x_offset/2 + y_offset*y_pitch/4;
	pV += x_offset/2 + y_offset*y_pitch/4;
	pA += x_offset + y_offset*y_pitch;
	yuva_load_lines(dst_bits, 4*width, pY, pU, pV, pA, y_pitch, y_pitch/2, width, dst_yuv);
}

static void load_line_yuyv(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits, Bool dst_yuv)
{
	u8 *pY, *pU, *pV;
	pY = (u8 *)src_bits + x_offset + y_offset*y_pitch;
	pU = (u8 *)pY + 1;
	pV = (u8 *)pY + 3;
	yuv_load_lines_packed((unsigned char*)dst_bits, 4*width, pY, pU, pV, width, dst_yuv);
}
static void load_line_uyvy(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits, Bool dst_yuv)
{
	u8 *pY, *pU, *pV;
	pU = (u8 *)src_bits + x_offset + y_offset*y_pitch;
	pY = (u8 *)pU + 1;
	pV = (u8 *)pU + 2;
	yuv_load_lines_packed((unsigned char*)dst_bits, 4*width, pY, pU, pV, width, dst_yuv);
}
static void load_line_yvyu(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits, Bool dst_yuv)
{
	u8 *pY, *pU, *pV;
	pY = (u8 *)src_bits + x_offset + y_offset*y_pitch;
	pV = (u8 *)pY + 1;
	pU = (u8 *)pY + 3;
	yuv_load_lines_packed((unsigned char*)dst_bits, 4*width, pY, pU, pV, width, dst_yuv);
}
static void load_line_vyuy(u8 *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits, Bool dst_yuv)
{
	u8 *pY, *pU, *pV;
	pV = (u8 *)src_bits + x_offset + y_offset*y_pitch;
	pY = (u8 *)pV + 1;
	pU = (u8 *)pV + 2;
	yuv_load_lines_packed((unsigned char*)dst_bits, 4*width, pY, pU, pV, width, dst_yuv);
}


static void gf_yuv_load_lines_nv12_nv21(unsigned char *dst, s32 dststride, unsigned char *y_src, unsigned char *u_src, unsigned char *v_src, s32 y_stride, s32 width, Bool dst_yuv)
{
	u32 hw, x;
	unsigned char *dst2 = (unsigned char *) dst + dststride;
	unsigned char *y_src2 = (unsigned char *) y_src + y_stride;

	hw = width / 2;
	if (dst_yuv) {
		for (x = 0; x < hw; x++) {

			dst[0] = dst[4] = dst2[0] = dst2[4] = v_src[2*x];
			dst[1] = dst[5] = dst2[1] = dst2[5] = u_src[2*x];
			dst[2] = *y_src;
			y_src++;
			dst[3] = 0xFF;

			dst[6] = *y_src;
			y_src++;
			dst[7] = 0xFF;

			dst2[2] = *y_src2;
			y_src2++;
			dst2[3] = 0xFF;

			dst2[6] = *y_src2;
			y_src2++;
			dst2[7] = 0xFF;

			dst += 8;
			dst2 += 8;
		}
		return;
	}
	for (x = 0; x < hw; x++) {
		s32 u, v;
		s32 b_u, g_uv, r_v, rgb_y;

		u = u_src[2*x];
		v = v_src[2*x];

		b_u = B_U[u];
		g_uv = G_U[u] + G_V[v];
		r_v = R_V[v];

		rgb_y = RGB_Y[*y_src];
		dst[0] = col_clip( (rgb_y + r_v) >> SCALEBITS_OUT);
		dst[1] = col_clip( (rgb_y - g_uv) >> SCALEBITS_OUT);
		dst[2] = col_clip( (rgb_y + b_u) >> SCALEBITS_OUT);
		dst[3] = 0xFF;
		y_src++;

		rgb_y = RGB_Y[*y_src];
		dst[4] = col_clip( (rgb_y + r_v) >> SCALEBITS_OUT);
		dst[5] = col_clip( (rgb_y - g_uv) >> SCALEBITS_OUT);
		dst[6] = col_clip( (rgb_y + b_u) >> SCALEBITS_OUT);
		dst[7] = 0xFF;
		y_src++;

		rgb_y = RGB_Y[*y_src2];
		dst2[0] = col_clip( (rgb_y + r_v) >> SCALEBITS_OUT);
		dst2[1] = col_clip( (rgb_y - g_uv) >> SCALEBITS_OUT);
		dst2[2] = col_clip( (rgb_y + b_u) >> SCALEBITS_OUT);
		dst2[3] = 0xFF;
		y_src2++;

		rgb_y = RGB_Y[*y_src2];
		dst2[4] = col_clip( (rgb_y + r_v) >> SCALEBITS_OUT);
		dst2[5] = col_clip( (rgb_y - g_uv) >> SCALEBITS_OUT);
		dst2[6] = col_clip( (rgb_y + b_u) >> SCALEBITS_OUT);
		dst2[7] = 0xFF;
		y_src2++;

		dst += 8;
		dst2 += 8;
	}
}

static void load_line_nv12(char *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits, u8 *pU, Bool dst_yuv)
{
	u8 *pY = (u8*)src_bits;
	if (!pU) {
		pU = (u8*)src_bits + y_pitch*height;
	}

	pY += x_offset + y_offset*y_pitch;
	pU += x_offset + y_offset*y_pitch/2; //half vertical sampling
	gf_yuv_load_lines_nv12_nv21(dst_bits, 4*width, pY, pU, pU + 1, y_pitch, width, dst_yuv);
}
static void load_line_nv21(char *src_bits, u32 x_offset, u32 y_offset, u32 y_pitch, u32 width, u32 height, u8 *dst_bits, u8 *pU, Bool dst_yuv)
{
	u8 *pY = (u8*)src_bits;
	if (!pU) {
		pU = (u8*)src_bits + y_pitch*height;
	}

	pY += x_offset + y_offset*y_pitch;
	pU += x_offset + y_offset*y_pitch/2; //half vertical sampling
	gf_yuv_load_lines_nv12_nv21(dst_bits, 4*width, pY, pU+1, pU, y_pitch, width, dst_yuv);
}

//#define COLORKEY_MPEG4_STRICT

static Bool format_is_yuv(u32 in_pf)
{
	switch (in_pf) {
	case GF_PIXEL_YUYV:
	case GF_PIXEL_YVYU:
	case GF_PIXEL_UYVY:
	case GF_PIXEL_VYUY:
	case GF_PIXEL_YUV:
	case GF_PIXEL_YVU:
	case GF_PIXEL_YUV_10:
	case GF_PIXEL_YUV422:
	case GF_PIXEL_YUV422_10:
	case GF_PIXEL_YUV444:
	case GF_PIXEL_YUV444_10:
		return GF_TRUE;
		/*not supported yet*/
	case GF_PIXEL_YUVA:
	default:
		return GF_FALSE;
	}
}

GF_EXPORT
GF_Err gf_stretch_bits(GF_VideoSurface *dst, GF_VideoSurface *src, GF_Window *dst_wnd, GF_Window *src_wnd, u8 alpha, Bool flip, GF_ColorKey *key, GF_ColorMatrix *cmat)
{
	u8 *tmp, *rows;
	u8 ka=0, kr=0, kg=0, kb=0, kl=0, kh=0;
	s32 src_row;
	u32 i, yuv_planar_type = 0;
	Bool no_memcpy, dst_yuv = GF_FALSE;
	Bool force_load_odd_yuv_lines = GF_FALSE;
	Bool yuv_init = GF_FALSE;
	Bool has_alpha = (alpha!=0xFF) ? GF_TRUE : GF_FALSE;
	u32 dst_bpp, dst_w_size;
	s32 pos_y, inc_y, inc_x, prev_row, x_off;
	u32 src_w, src_h, dst_w, dst_h;
	u8 *dst_bits = NULL, *dst_bits_prev = NULL, *dst_temp_bits = NULL;
	s32 dst_x_pitch = dst->pitch_x;
	copy_row_proto copy_row = NULL;
	load_line_proto load_line = NULL;

	if (cmat && (cmat->m[15] || cmat->m[16] || cmat->m[17] || (cmat->m[18]!=FIX_ONE) || cmat->m[19] )) has_alpha = GF_TRUE;
	else if (key && (key->alpha<0xFF)) has_alpha = GF_TRUE;

	//check if we have a dedicated copy/conv when no stretch nor blending (avoids line load while copying)
	if ((alpha==0xFF) && !flip && !key && !cmat) {
		Bool no_stretch = GF_FALSE;
		Bool output_yuv = format_is_yuv(dst->pixel_format);
		GF_Err e = GF_NOT_SUPPORTED;

		if (!dst_wnd) no_stretch = GF_TRUE;
		else if (src_wnd) {
			if (!dst_wnd->x && !dst_wnd->y && (dst_wnd->w==src_wnd->w) && (dst_wnd->h==src_wnd->h))
				no_stretch = GF_TRUE;
		} else {
			if ((dst_wnd->w==src->width) && (dst_wnd->h==src->height))
				no_stretch = GF_TRUE;
		}
		if (no_stretch && output_yuv) {
			//check YUV10->8, YUV->YUV
			switch (src->pixel_format) {
			case GF_PIXEL_NV12_10:
				e = color_write_nv12_10_to_yuv(dst, src, src_wnd, GF_FALSE);
				break;
			case GF_PIXEL_NV21_10:
				e = color_write_nv12_10_to_yuv(dst, src, src_wnd, GF_TRUE);
				break;
			case GF_PIXEL_YUV_10:
				e = color_write_yv12_10_to_yuv(dst, src, src_wnd, GF_FALSE);
				break;
			case GF_PIXEL_YUV422_10:
				if (dst->pixel_format == GF_PIXEL_YUV422)
					e = color_write_yuv422_10_to_yuv422(dst, src, src_wnd, GF_FALSE);
				else if (dst->pixel_format == GF_PIXEL_YUV)
					e = color_write_yuv422_10_to_yuv(dst, src, src_wnd, GF_FALSE);
				else if (dst->pixel_format == GF_PIXEL_YVU)
					e = color_write_yuv422_10_to_yuv(dst, src, src_wnd, GF_TRUE);
				break;
			case GF_PIXEL_YUV444_10:
				if (dst->pixel_format == GF_PIXEL_YUV444)
					e = color_write_yuv444_10_to_yuv444(dst, src, src_wnd, GF_FALSE);
				else if (dst->pixel_format == GF_PIXEL_YUV)
					e = color_write_yuv444_10_to_yuv(dst, src, src_wnd, GF_FALSE);
				else if (dst->pixel_format == GF_PIXEL_YVU)
					e = color_write_yuv444_10_to_yuv(dst, src, src_wnd, GF_TRUE);
				break;
			case GF_PIXEL_YUV:
				e = color_write_yuv420_to_yuv(dst, src, src_wnd, GF_FALSE);
				break;
			case GF_PIXEL_YVU:
				e = color_write_yuv420_to_yuv(dst, src, src_wnd, GF_TRUE);
				break;
			case GF_PIXEL_YUV422:
				e = color_write_yuv422_to_yuv(dst, src, src_wnd, GF_FALSE);
				break;
			case GF_PIXEL_YUV444:
				e = color_write_yuv444_to_yuv(dst, src, src_wnd, GF_FALSE);
				break;
			case GF_PIXEL_YUYV:
			case GF_PIXEL_YVYU:
			case GF_PIXEL_UYVY:
			case GF_PIXEL_VYUY:
				e = color_write_yvyu_to_yuv(dst, src, src_wnd, GF_FALSE);
				break;
			}
		}
		else if (no_stretch && !output_yuv) {
			//check rgb->rgb copy
			switch (dst->pixel_format) {
			case GF_PIXEL_RGB:
			case GF_PIXEL_BGR:
				e = color_write_rgb_to_24(dst, src, src_wnd);
				break;
			case GF_PIXEL_RGBX:
			case GF_PIXEL_XRGB:
			case GF_PIXEL_RGBD:
			case GF_PIXEL_RGBDS:
			case GF_PIXEL_BGRX:
			case GF_PIXEL_XBGR:
				e = color_write_rgb_to_32(dst, src, src_wnd);
				break;
			default:
				break;
			}
		}
		if (e == GF_OK) return GF_OK;
	}
	
	
	switch (src->pixel_format) {
	case GF_PIXEL_GREYSCALE:
		load_line = load_line_grey;
		break;
	case GF_PIXEL_ALPHAGREY:
		load_line = load_line_alpha_grey;
		has_alpha = GF_TRUE;
		break;
	case GF_PIXEL_GREYALPHA:
		load_line = load_line_grey_alpha;
		has_alpha = GF_TRUE;
		break;
	case GF_PIXEL_RGB_555:
		load_line = load_line_rgb_555;
		break;
	case GF_PIXEL_RGB_565:
		load_line = load_line_rgb_565;
		break;
	case GF_PIXEL_RGB:
		load_line = load_line_rgb_24;
		break;
	case GF_PIXEL_BGR:
		load_line = load_line_bgr_24;
		break;
	case GF_PIXEL_ARGB:
		has_alpha = GF_TRUE;
		load_line = load_line_argb;
		break;
	case GF_PIXEL_BGRA:
		has_alpha = GF_TRUE;
		load_line = load_line_bgra;
		break;
	case GF_PIXEL_RGBA:
		has_alpha = GF_TRUE;
	case GF_PIXEL_RGBX:
		load_line = load_line_rgb_32;
		break;
	case GF_PIXEL_XRGB:
		load_line = load_line_xrgb;
		break;
	case GF_PIXEL_BGRX:
		load_line = load_line_bgrx;
		break;
	case GF_PIXEL_RGBDS:
		load_line = load_line_rgbds;
		has_alpha = GF_TRUE;
		break;
	case GF_PIXEL_RGBD:
		load_line = load_line_rgbd;
		break;
	case GF_PIXEL_YUV:
	case GF_PIXEL_YVU:
		yuv2rgb_init();
		yuv_planar_type = 1;
		break;
	case GF_PIXEL_YUV422:
		yuv2rgb_init();
		yuv_planar_type = 4;
		break;
	case GF_PIXEL_YUV444:
		yuv2rgb_init();
		yuv_planar_type = 5;
		break;

	case GF_PIXEL_YUV_10:
		yuv2rgb_init();
		yuv_planar_type = 3;
		break;
	case GF_PIXEL_YUV422_10:
		yuv2rgb_init();
		yuv_planar_type = 6;
		break;
	case GF_PIXEL_YUV444_10:
		yuv2rgb_init();
		yuv_planar_type = 7;
		break;
	case GF_PIXEL_NV21:
		yuv2rgb_init();
		yuv_planar_type = 8;
		break;
	case GF_PIXEL_NV12:
		yuv2rgb_init();
		yuv_planar_type = 9;
		break;
	case GF_PIXEL_YUVA:
		has_alpha = GF_TRUE;
	case GF_PIXEL_YUVD:
		yuv_planar_type = 2;
		yuv2rgb_init();
		break;
	case GF_PIXEL_YUYV:
		yuv_planar_type = 0;
		yuv2rgb_init();
		load_line = load_line_yuyv;
		break;
	case GF_PIXEL_UYVY:
		yuv_planar_type = 0;
		yuv2rgb_init();
		load_line = load_line_uyvy;
		break;
	case GF_PIXEL_YVYU:
		yuv_planar_type = 0;
		yuv2rgb_init();
		load_line = load_line_yvyu;
		break;
	case GF_PIXEL_VYUY:
		yuv_planar_type = 0;
		yuv2rgb_init();
		load_line = load_line_vyuy;
		break;
	default:
		GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("Source pixel format %s not supported by gf_stretch_bits\n", gf_pixel_fmt_name(src->pixel_format) ));
		return GF_NOT_SUPPORTED;
	}

	/*only RGB output supported*/
	switch (dst->pixel_format) {
	case GF_PIXEL_RGB_555:
		dst_bpp = sizeof(unsigned char)*2;
		copy_row = has_alpha ? merge_row_rgb_555 : copy_row_rgb_555;
		break;
	case GF_PIXEL_RGB_565:
		dst_bpp = sizeof(unsigned char)*2;
		copy_row = has_alpha ? merge_row_rgb_565 : copy_row_rgb_565;
		break;
	case GF_PIXEL_RGB:
		dst_bpp = sizeof(unsigned char)*3;
		copy_row = has_alpha ? merge_row_rgb_24 : copy_row_rgb_24;
		break;
	case GF_PIXEL_BGR:
		dst_bpp = sizeof(unsigned char)*3;
		copy_row = has_alpha ? merge_row_bgr_24 : copy_row_bgr_24;
		break;
	case GF_PIXEL_RGBX:
		dst_bpp = sizeof(unsigned char)*4;
		copy_row = has_alpha ? merge_row_bgrx : copy_row_bgrx;
		break;
	case GF_PIXEL_ARGB:
		dst_bpp = sizeof(unsigned char)*4;
		copy_row = has_alpha ? merge_row_argb : copy_row_argb;
		break;
	case GF_PIXEL_BGRA:
		dst_bpp = sizeof(unsigned char)*4;
		copy_row = has_alpha ? merge_row_bgra : copy_row_bgrx;
		break;
	case GF_PIXEL_RGBD:
		dst_bpp = sizeof(unsigned char)*4;
		copy_row = has_alpha ? merge_row_bgrx : copy_row_rgbd;
		break;
	case GF_PIXEL_RGBA:
		dst_bpp = sizeof(unsigned char)*4;
		copy_row = has_alpha ? merge_row_rgba : copy_row_rgbx;
		break;
	case GF_PIXEL_BGRX:
		dst_bpp = sizeof(unsigned char)*4;
		copy_row = has_alpha ? merge_row_rgbx : copy_row_rgbx;
		break;
#if 0
	//yuv dest not yet supported
	case GF_PIXEL_YUV444:
		dst_bpp = sizeof(unsigned char)*3;
		copy_row = has_alpha ? merge_row_yuv444 : copy_row_yuv444;
		dst_yuv = GF_TRUE;
		break;
#endif
	default:
		GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("Destination pixel format %s not supported by gf_stretch_bits, patch welcome\n", gf_pixel_fmt_name(dst->pixel_format) ));
		return GF_NOT_SUPPORTED;
	}
	/*x_pitch 0 means linear framebuffer*/
	if (!dst_x_pitch) dst_x_pitch = dst_bpp;


	src_w = src_wnd ? src_wnd->w : src->width;
	src_h = src_wnd ? src_wnd->h : src->height;
	dst_w = dst_wnd ? dst_wnd->w : dst->width;
	dst_h = dst_wnd ? dst_wnd->h : dst->height;

	if (!src_w || !src_h || !dst_w || !dst_h)
		return GF_OK;
		
	if (yuv_planar_type && (src_w%2)) src_w++;

	tmp = (u8 *) gf_malloc(sizeof(u8) * src_w * (yuv_planar_type ? 8 : 4) );
	rows = tmp;

	if ( (src_h / dst_h) * dst_h != src_h) force_load_odd_yuv_lines = GF_TRUE;

	pos_y = 0x10000;
	inc_y = (src_h << 16) / dst_h;
	inc_x = (src_w << 16) / dst_w;
	x_off = src_wnd ? src_wnd->x : 0;
	src_row = src_wnd ? src_wnd->y : 0;

	prev_row = -1;

	dst_bits = (u8 *) dst->video_buffer;
	if (dst_wnd) dst_bits += ((s32)dst_wnd->x) * dst_x_pitch + ((s32)dst_wnd->y) * dst->pitch_y;

	dst_w_size = dst_bpp*dst_w;

	if (key) {
		ka = key->alpha;
		kr = key->r;
		kg = key->g;
		kb = key->b;
		kl = key->low;
		kh = key->high;
		if (kh==kl) kh++;
	}

	/*do NOT use memcpy if the target buffer is not in systems memory*/
	no_memcpy = (has_alpha || dst->is_hardware_memory || (dst_bpp!=dst_x_pitch)) ? GF_TRUE : GF_FALSE;

	while (dst_h) {
		while ( pos_y >= 0x10000L ) {
			src_row++;
			pos_y -= 0x10000L;
		}
		/*new row, check if conversion is needed*/
		if (prev_row != src_row) {
			u32 the_row = src_row - 1;
			if (yuv_planar_type) {
				if (the_row % 2) {
					if (!yuv_init || force_load_odd_yuv_lines) {
						yuv_init = GF_TRUE;
						the_row--;
						if (flip) the_row = src->height - 2 - the_row;
						if (yuv_planar_type == 1) {
							load_line_yv12(src->video_buffer, x_off, the_row, src->pitch_y, src_w, src->height, tmp, (u8 *)src->u_ptr, (u8 *)src->v_ptr, dst_yuv);
						}
						else if (yuv_planar_type == 4) {
							load_line_yuv422(src->video_buffer, x_off, the_row, src->pitch_y, src_w, src->height, tmp, (u8 *)src->u_ptr, (u8 *)src->v_ptr, dst_yuv);
						}
						else if (yuv_planar_type == 5) {
							load_line_yuv444(src->video_buffer, x_off, the_row, src->pitch_y, src_w, src->height, tmp, (u8 *)src->u_ptr, (u8 *)src->v_ptr, dst_yuv);
						}
						else if (yuv_planar_type == 3) {
							load_line_yv12_10((char *)src->video_buffer, x_off, the_row, src->pitch_y, src_w, src->height, tmp, (u8 *)src->u_ptr, (u8 *)src->v_ptr, dst_yuv);
						}
						else if (yuv_planar_type == 6) {
							load_line_yuv422_10((char *)src->video_buffer, x_off, the_row, src->pitch_y, src_w, src->height, tmp, (u8 *)src->u_ptr, (u8 *)src->v_ptr, dst_yuv);
						}
						else if (yuv_planar_type == 7) {
							load_line_yuv444_10((char *)src->video_buffer, x_off, the_row, src->pitch_y, src_w, src->height, tmp, (u8 *)src->u_ptr, (u8 *)src->v_ptr, dst_yuv);
						}
						else if (yuv_planar_type == 8) {
							load_line_nv21((char *)src->video_buffer, x_off, the_row, src->pitch_y, src_w, src->height, tmp, (u8 *)src->u_ptr, dst_yuv);
						}
						else if (yuv_planar_type == 9) {
							load_line_nv12((char *)src->video_buffer, x_off, the_row, src->pitch_y, src_w, src->height, tmp, (u8 *)src->u_ptr, dst_yuv);
						}
						else {
							load_line_yuva(src->video_buffer, x_off, the_row, src->pitch_y, src_w, src->height, tmp, (u8 *)src->u_ptr, (u8 *)src->v_ptr, (u8 *)src->a_ptr, dst_yuv);
						}

						if (cmat) {
							for (i=0; i<2*src_w; i++) {
								u32 idx = 4*i;
								gf_cmx_apply_argb(cmat, (u8 *) &tmp[idx+3], (u8 *) &tmp[idx], (u8 *) &tmp[idx+1], (u8 *) &tmp[idx+2]);
							}
						}
						if (key) {
							for (i=0; i<2*src_w; i++) {
								u32 idx = 4*i;
								s32 thres, v;
								v = tmp[idx]-kr;
								thres = ABS(v);
								v = tmp[idx+1]-kg;
								thres += ABS(v);
								v = tmp[idx+2]-kb;
								thres += ABS(v);
								thres/=3;
#ifdef COLORKEY_MPEG4_STRICT
								if (thres < kl) tmp[idx+3] = 0;
								else if (thres <= kh) tmp[idx+3] = (thres-kl)*ka / (kh-kl);
#else
								if (thres < kh) tmp[idx+3] = 0;
#endif
								else tmp[idx+3] = ka;
							}
						}
					}
					rows = flip ? tmp : tmp + src_w * 4;
				}
				else {
					if (flip) the_row = src->height - 2 - the_row;
					if (yuv_planar_type == 1) {
						load_line_yv12(src->video_buffer, x_off, the_row, src->pitch_y, src_w, src->height, tmp, (u8 *)src->u_ptr, (u8 *)src->v_ptr, dst_yuv);
					}
					else if (yuv_planar_type == 4) {
						load_line_yuv422(src->video_buffer, x_off, the_row, src->pitch_y, src_w, src->height, tmp, (u8 *)src->u_ptr, (u8 *)src->v_ptr, dst_yuv);
					}
					else if (yuv_planar_type == 5) {
						load_line_yuv444(src->video_buffer, x_off, the_row, src->pitch_y, src_w, src->height, tmp, (u8 *)src->u_ptr, (u8 *)src->v_ptr, dst_yuv);
					}
					else if (yuv_planar_type == 3) {
						load_line_yv12_10((char *)src->video_buffer, x_off, the_row, src->pitch_y, src_w, src->height, tmp, (u8 *)src->u_ptr, (u8 *)src->v_ptr, dst_yuv);
					}
					else if (yuv_planar_type == 6) {
						load_line_yuv422_10((char *)src->video_buffer, x_off, the_row, src->pitch_y, src_w, src->height, tmp, (u8 *)src->u_ptr, (u8 *)src->v_ptr, dst_yuv);
					}
					else if (yuv_planar_type == 7) {
						load_line_yuv444_10((char *)src->video_buffer, x_off, the_row, src->pitch_y, src_w, src->height, tmp, (u8 *)src->u_ptr, (u8 *)src->v_ptr, dst_yuv);
					}
					else if (yuv_planar_type == 8) {
						load_line_nv21((char *)src->video_buffer, x_off, the_row, src->pitch_y, src_w, src->height, tmp, (u8 *)src->u_ptr, dst_yuv);
					}
					else if (yuv_planar_type == 9) {
						load_line_nv12((char *)src->video_buffer, x_off, the_row, src->pitch_y, src_w, src->height, tmp, (u8 *)src->u_ptr, dst_yuv);
					}
					else {
						load_line_yuva(src->video_buffer, x_off, the_row, src->pitch_y, src_w, src->height, tmp, (u8 *)src->u_ptr, (u8 *)src->v_ptr, (u8 *)src->a_ptr, dst_yuv);
					}
					yuv_init = GF_TRUE;
					rows = flip ? tmp + src_w * 4 : tmp;

					if (cmat) {
						for (i=0; i<2*src_w; i++) {
							u32 idx = 4*i;
							gf_cmx_apply_argb(cmat, &tmp[idx+3], &tmp[idx], &tmp[idx+1], &tmp[idx+2]);
						}
					}
					if (key) {
						for (i=0; i<2*src_w; i++) {
							u32 idx = 4*i;
							s32 thres, v;
							v = tmp[idx]-kr;
							thres = ABS(v);
							v = tmp[idx+1]-kg;
							thres += ABS(v);
							v = tmp[idx+2]-kb;
							thres += ABS(v);
							thres/=3;
#ifdef COLORKEY_MPEG4_STRICT
							if (thres < kl) tmp[idx+3] = 0;
							else if (thres <= kh) tmp[idx+3] = (thres-kl)*ka / (kh-kl);
#else
							if (thres < kh) tmp[idx+3] = 0;
#endif
							else tmp[idx+3] = ka;
						}
					}
				}
			} else {
				if (flip) the_row = src->height-1 - the_row;
				load_line((u8*)src->video_buffer, x_off, the_row, src->pitch_y, src_w, src->height, tmp, dst_yuv);
				rows = tmp;
				if (cmat) {
					for (i=0; i<src_w; i++) {
						u32 idx = 4*i;
						gf_cmx_apply_argb(cmat, &tmp[idx+3], &tmp[idx], &tmp[idx+1], &tmp[idx+2]);
					}
				}
				if (key) {
					for (i=0; i<src_w; i++) {
						u32 idx = 4*i;
						s32 thres, v;
						v = tmp[idx]-kr;
						thres = ABS(v);
						v = tmp[idx+1]-kg;
						thres += ABS(v);
						v = tmp[idx+2]-kb;
						thres += ABS(v);
						thres/=3;
#ifdef COLORKEY_MPEG4_STRICT
						if (thres < kl) tmp[idx+3] = 0;
						else if (thres <= kh) tmp[idx+3] = (thres-kl)*ka / (kh-kl);
#else
						if (thres < kh) tmp[idx+3] = 0;
#endif
						else tmp[idx+3] = ka;
					}
				}
			}
			copy_row(rows, src_w, dst_bits, dst_w, inc_x, dst_x_pitch, alpha, dst->pitch_y, dst->height);
		}
		/*do NOT use memcpy if the target buffer is not in systems memory*/
		else if (no_memcpy) {
			copy_row(rows, src_w, dst_bits, dst_w, inc_x, dst_x_pitch, alpha, dst->pitch_y, dst->height);
		} else if (dst_bits && dst_bits_prev) {
			memcpy(dst_bits, dst_bits_prev, dst_w_size);
		}

		pos_y += inc_y;
		prev_row = src_row;

		dst_bits_prev = dst_bits;
		dst_bits += dst->pitch_y;
		dst_h--;
	}
	if (dst_temp_bits) gf_free(dst_temp_bits);
	gf_free(tmp);
	return GF_OK;
}

#else
GF_EXPORT
GF_Err gf_stretch_bits(GF_VideoSurface *dst, GF_VideoSurface *src, GF_Window *dst_wnd, GF_Window *src_wnd, u8 alpha, Bool flip, GF_ColorKey *key, GF_ColorMatrix *cmat)
{
	return GF_NOT_SUPPORTED;
}
#endif // GPAC_DISABLE_COMPOSITOR


/*
	COLOR MATRIX TOOLS
 */

GF_EXPORT
void gf_cmx_init(GF_ColorMatrix *_this)
{
	if (!_this) return;
	memset(_this->m, 0, sizeof(Fixed)*20);
	_this->m[0] = _this->m[6] = _this->m[12] = _this->m[18] = FIX_ONE;
	_this->identity = 1;
}


static void gf_cmx_identity(GF_ColorMatrix *_this)
{
	GF_ColorMatrix mat;
	gf_cmx_init(&mat);
	_this->identity = memcmp(_this->m, mat.m, sizeof(Fixed)*20) ? 0 : 1;
}

GF_EXPORT
void gf_cmx_set(GF_ColorMatrix *_this,
                Fixed c1, Fixed c2, Fixed c3, Fixed c4, Fixed c5,
                Fixed c6, Fixed c7, Fixed c8, Fixed c9, Fixed c10,
                Fixed c11, Fixed c12, Fixed c13, Fixed c14, Fixed c15,
                Fixed c16, Fixed c17, Fixed c18, Fixed c19, Fixed c20)
{
	if (!_this) return;
	_this->m[0] = c1;
	_this->m[1] = c2;
	_this->m[2] = c3;
	_this->m[3] = c4;
	_this->m[4] = c5;
	_this->m[5] = c6;
	_this->m[6] = c7;
	_this->m[7] = c8;
	_this->m[8] = c9;
	_this->m[9] = c10;
	_this->m[10] = c11;
	_this->m[11] = c12;
	_this->m[12] = c13;
	_this->m[13] = c14;
	_this->m[14] = c15;
	_this->m[15] = c16;
	_this->m[16] = c17;
	_this->m[17] = c18;
	_this->m[18] = c19;
	_this->m[19] = c20;
	gf_cmx_identity(_this);
}

GF_EXPORT
void gf_cmx_copy(GF_ColorMatrix *_this, GF_ColorMatrix *from)
{
	if (!_this || !from) return;
	memcpy(_this->m, from->m, sizeof(Fixed)*20);
	gf_cmx_identity(_this);
}


GF_EXPORT
void gf_cmx_multiply(GF_ColorMatrix *_this, GF_ColorMatrix *w)
{
	Fixed res[20];
	if (!_this || !w || w->identity) return;
	if (_this->identity) {
		gf_cmx_copy(_this, w);
		return;
	}

	res[0] = gf_mulfix(_this->m[0], w->m[0]) + gf_mulfix(_this->m[1], w->m[5]) + gf_mulfix(_this->m[2], w->m[10]) + gf_mulfix(_this->m[3], w->m[15]);
	res[1] = gf_mulfix(_this->m[0], w->m[1]) + gf_mulfix(_this->m[1], w->m[6]) + gf_mulfix(_this->m[2], w->m[11]) + gf_mulfix(_this->m[3], w->m[16]);
	res[2] = gf_mulfix(_this->m[0], w->m[2]) + gf_mulfix(_this->m[1], w->m[7]) + gf_mulfix(_this->m[2], w->m[12]) + gf_mulfix(_this->m[3], w->m[17]);
	res[3] = gf_mulfix(_this->m[0], w->m[3]) + gf_mulfix(_this->m[1], w->m[8]) + gf_mulfix(_this->m[2], w->m[13]) + gf_mulfix(_this->m[3], w->m[18]);
	res[4] = gf_mulfix(_this->m[0], w->m[4]) + gf_mulfix(_this->m[1], w->m[9]) + gf_mulfix(_this->m[2], w->m[14]) + gf_mulfix(_this->m[3], w->m[19]) + _this->m[4];

	res[5] = gf_mulfix(_this->m[5], w->m[0]) + gf_mulfix(_this->m[6], w->m[5]) + gf_mulfix(_this->m[7], w->m[10]) + gf_mulfix(_this->m[8], w->m[15]);
	res[6] = gf_mulfix(_this->m[5], w->m[1]) + gf_mulfix(_this->m[6], w->m[6]) + gf_mulfix(_this->m[7], w->m[11]) + gf_mulfix(_this->m[8], w->m[16]);
	res[7] = gf_mulfix(_this->m[5], w->m[2]) + gf_mulfix(_this->m[6], w->m[7]) + gf_mulfix(_this->m[7], w->m[12]) + gf_mulfix(_this->m[8], w->m[17]);
	res[8] = gf_mulfix(_this->m[5], w->m[3]) + gf_mulfix(_this->m[6], w->m[8]) + gf_mulfix(_this->m[7], w->m[13]) + gf_mulfix(_this->m[8], w->m[18]);
	res[9] = gf_mulfix(_this->m[5], w->m[4]) + gf_mulfix(_this->m[6], w->m[9]) + gf_mulfix(_this->m[7], w->m[14]) + gf_mulfix(_this->m[8], w->m[19]) + _this->m[9];

	res[10] = gf_mulfix(_this->m[10], w->m[0]) + gf_mulfix(_this->m[11], w->m[5]) + gf_mulfix(_this->m[12], w->m[10]) + gf_mulfix(_this->m[13], w->m[15]);
	res[11] = gf_mulfix(_this->m[10], w->m[1]) + gf_mulfix(_this->m[11], w->m[6]) + gf_mulfix(_this->m[12], w->m[11]) + gf_mulfix(_this->m[13], w->m[16]);
	res[12] = gf_mulfix(_this->m[10], w->m[2]) + gf_mulfix(_this->m[11], w->m[7]) + gf_mulfix(_this->m[12], w->m[12]) + gf_mulfix(_this->m[13], w->m[17]);
	res[13] = gf_mulfix(_this->m[10], w->m[3]) + gf_mulfix(_this->m[11], w->m[8]) + gf_mulfix(_this->m[12], w->m[13]) + gf_mulfix(_this->m[13], w->m[18]);
	res[14] = gf_mulfix(_this->m[10], w->m[4]) + gf_mulfix(_this->m[11], w->m[9]) + gf_mulfix(_this->m[12], w->m[14]) + gf_mulfix(_this->m[13], w->m[19]) + _this->m[14];

	res[15] = gf_mulfix(_this->m[15], w->m[0]) + gf_mulfix(_this->m[16], w->m[5]) + gf_mulfix(_this->m[17], w->m[10]) + gf_mulfix(_this->m[18], w->m[15]);
	res[16] = gf_mulfix(_this->m[15], w->m[1]) + gf_mulfix(_this->m[16], w->m[6]) + gf_mulfix(_this->m[17], w->m[11]) + gf_mulfix(_this->m[18], w->m[16]);
	res[17] = gf_mulfix(_this->m[15], w->m[2]) + gf_mulfix(_this->m[16], w->m[7]) + gf_mulfix(_this->m[17], w->m[12]) + gf_mulfix(_this->m[18], w->m[17]);
	res[18] = gf_mulfix(_this->m[15], w->m[3]) + gf_mulfix(_this->m[16], w->m[8]) + gf_mulfix(_this->m[17], w->m[13]) + gf_mulfix(_this->m[18], w->m[18]);
	res[19] = gf_mulfix(_this->m[15], w->m[4]) + gf_mulfix(_this->m[16], w->m[9]) + gf_mulfix(_this->m[17], w->m[14]) + gf_mulfix(_this->m[18], w->m[19]) + _this->m[19];
	memcpy(_this->m, res, sizeof(Fixed)*20);
	gf_cmx_identity(_this);
}

#define CLIP_COMP(val)	{ if (val<0) { val=0; } else if (val>FIX_ONE) { val=FIX_ONE;} }

GF_EXPORT
void gf_cmx_apply_argb(GF_ColorMatrix *_this, u8 *a_, u8 *r_, u8 *g_, u8 *b_)
{
	Fixed _a, _r, _g, _b, a, r, g, b;
	if (!_this || _this->identity) return;

	a = INT2FIX(*a_)/255;
	r = INT2FIX(*r_)/255;
	g = INT2FIX(*g_)/255;
	b = INT2FIX(*b_)/255;
	_r = gf_mulfix(r, _this->m[0]) + gf_mulfix(g, _this->m[1]) + gf_mulfix(b, _this->m[2]) + gf_mulfix(a, _this->m[3]) + _this->m[4];
	_g = gf_mulfix(r, _this->m[5]) + gf_mulfix(g, _this->m[6]) + gf_mulfix(b, _this->m[7]) + gf_mulfix(a, _this->m[8]) + _this->m[9];
	_b = gf_mulfix(r, _this->m[10]) + gf_mulfix(g, _this->m[11]) + gf_mulfix(b, _this->m[12]) + gf_mulfix(a, _this->m[13]) + _this->m[14];
	_a = gf_mulfix(r, _this->m[15]) + gf_mulfix(g, _this->m[16]) + gf_mulfix(b, _this->m[17]) + gf_mulfix(a, _this->m[18]) + _this->m[19];
	CLIP_COMP(_a);
	CLIP_COMP(_r);
	CLIP_COMP(_g);
	CLIP_COMP(_b);

	*a_ = FIX2INT(_a*255);
	*r_ = FIX2INT(_r*255);
	*g_ = FIX2INT(_g*255);
	*b_ = FIX2INT(_b*255);
}


GF_EXPORT
GF_Color gf_cmx_apply(GF_ColorMatrix *_this, GF_Color col)
{
	Fixed _a, _r, _g, _b, a, r, g, b;
	if (!_this || _this->identity) return col;

	a = INT2FIX(col>>24);
	a /= 255;
	r = INT2FIX((col>>16)&0xFF);
	r /= 255;
	g = INT2FIX((col>>8)&0xFF);
	g /= 255;
	b = INT2FIX((col)&0xFF);
	b /= 255;
	_r = gf_mulfix(r, _this->m[0]) + gf_mulfix(g, _this->m[1]) + gf_mulfix(b, _this->m[2]) + gf_mulfix(a, _this->m[3]) + _this->m[4];
	_g = gf_mulfix(r, _this->m[5]) + gf_mulfix(g, _this->m[6]) + gf_mulfix(b, _this->m[7]) + gf_mulfix(a, _this->m[8]) + _this->m[9];
	_b = gf_mulfix(r, _this->m[10]) + gf_mulfix(g, _this->m[11]) + gf_mulfix(b, _this->m[12]) + gf_mulfix(a, _this->m[13]) + _this->m[14];
	_a = gf_mulfix(r, _this->m[15]) + gf_mulfix(g, _this->m[16]) + gf_mulfix(b, _this->m[17]) + gf_mulfix(a, _this->m[18]) + _this->m[19];
	CLIP_COMP(_a);
	CLIP_COMP(_r);
	CLIP_COMP(_g);
	CLIP_COMP(_b);
	return GF_COL_ARGB(FIX2INT(_a*255),FIX2INT(_r*255),FIX2INT(_g*255),FIX2INT(_b*255));
}

GF_EXPORT
u64 gf_cmx_apply_wide(GF_ColorMatrix *_this, u64 col)
{
	u64 res;
	Fixed _a, _r, _g, _b, a, r, g, b;
	if (!_this || _this->identity) return col;

	a = INT2FIX(col>>48);
	a /= 0xFFFF;
	r = INT2FIX((col>>32)&0xFFFF);
	r /= 0xFFFF;
	g = INT2FIX((col>>16)&0xFFFF);
	g /= 0xFFFF;
	b = INT2FIX((col)&0xFFFF);
	b /= 0xFFFF;
	_r = gf_mulfix(r, _this->m[0]) + gf_mulfix(g, _this->m[1]) + gf_mulfix(b, _this->m[2]) + gf_mulfix(a, _this->m[3]) + _this->m[4];
	_g = gf_mulfix(r, _this->m[5]) + gf_mulfix(g, _this->m[6]) + gf_mulfix(b, _this->m[7]) + gf_mulfix(a, _this->m[8]) + _this->m[9];
	_b = gf_mulfix(r, _this->m[10]) + gf_mulfix(g, _this->m[11]) + gf_mulfix(b, _this->m[12]) + gf_mulfix(a, _this->m[13]) + _this->m[14];
	_a = gf_mulfix(r, _this->m[15]) + gf_mulfix(g, _this->m[16]) + gf_mulfix(b, _this->m[17]) + gf_mulfix(a, _this->m[18]) + _this->m[19];
	CLIP_COMP(_a);
	CLIP_COMP(_r);
	CLIP_COMP(_g);
	CLIP_COMP(_b);
	res = (u32) (_a*0xFFFF)&0xFFFF;
	res<<=16;
	res |= (u32) (_r*0xFFFF)&0xFFFF;
	res<<=16;
	res |= (u32) (_g*0xFFFF)&0xFFFF;
	res<<=16;
	res |= (u32) (_b*0xFFFF)&0xFFFF;
	return res;
}

GF_EXPORT
void gf_cmx_apply_fixed(GF_ColorMatrix *_this, Fixed *a, Fixed *r, Fixed *g, Fixed *b)
{
	u32 col = GF_COL_ARGB_FIXED(*a, *r, *g, *b);
	col = gf_cmx_apply(_this, col);
	*a = INT2FIX(GF_COL_A(col)) / 255;
	*r = INT2FIX(GF_COL_R(col)) / 255;
	*g = INT2FIX(GF_COL_G(col)) / 255;
	*b = INT2FIX(GF_COL_B(col)) / 255;
}


#ifndef GPAC_DISABLE_COMPOSITOR


//intrinsic code segfaults on 32 bit, need to check why
#if defined(GPAC_64_BITS)
# if defined(WIN32) && !defined(__GNUC__)
#  include <intrin.h>
#  define GPAC_HAS_SSE2
# else
#  ifdef __SSE2__
#   include <emmintrin.h>
#   define GPAC_HAS_SSE2
#  endif
# endif
#endif

#ifdef GPAC_HAS_SSE2

static GF_Err color_write_yv12_10_to_yuv_intrin(GF_VideoSurface *vs_dst, unsigned char *pY, unsigned char *pU, unsigned char*pV, u32 src_stride, u32 src_width, u32 src_height, const GF_Window *_src_wnd, Bool swap_uv)
{
	u32 i, j, w, h;
	__m128i val1, val2, val_dst, *src1, *src2, *dst;
	if (!pY) return GF_BAD_PARAM;
	
	if (!pU) {
		pU = pY + src_stride * src_height;
		pV = pY + 5*src_stride * src_height/4;
	}

	if (_src_wnd) {
		pY = pY + src_stride * _src_wnd->y + _src_wnd->x;
		/*because of U and V downsampling by 2x2, working with odd Y offset will lead to a half-line shift between Y and UV components. We
		therefore force an even Y offset for U and V planes.*/
		pU = pU + (src_stride * (_src_wnd->y / 2) + _src_wnd->x) / 2;
		pV = pV + (src_stride * (_src_wnd->y / 2) + _src_wnd->x) / 2;
		w = _src_wnd->w;
		h = _src_wnd->h;
	} else {
		w = src_width;
		h = src_height;
	}

	if (swap_uv) {
		u8 *t = pV;
		pV = pU;
		pU = t;
	}


	
		for (i=0; i<h; i++) {
			src1 = (__m128i *)(pY + i*src_stride);
			src2 = src1+1;
			dst = (__m128i *)(vs_dst->video_buffer + i*vs_dst->pitch_y);

			for (j=0; j<w/16; j++, src1+=2, src2+=2, dst++) {
				val1 = _mm_load_si128(src1);
				val1 = _mm_srli_epi16(val1, 2);
				val2 = _mm_load_si128(src2);
				val2 = _mm_srli_epi16(val2, 2);
				val_dst = _mm_packus_epi16(val1, val2);
				_mm_store_si128(dst, val_dst);
			}
		}

		for (i=0; i<h/2; i++) {
			src1 = (__m128i *) (pU + i*src_stride/2);
			src2 = src1+1;
			if (vs_dst->u_ptr) dst = (__m128i *) (vs_dst->u_ptr + i*vs_dst->pitch_y/2);
			else dst = (__m128i *)(vs_dst->video_buffer + vs_dst->pitch_y * vs_dst->height + i*vs_dst->pitch_y/2);

			for (j=0; j<w/32; j++, src1+=2, src2+=2, dst++) {
				val1 = _mm_load_si128(src1);
				val1 = _mm_srli_epi16(val1, 2);
				val2 = _mm_load_si128(src2);
				val2 = _mm_srli_epi16(val2, 2);
				val_dst = _mm_packus_epi16(val1, val2);
				_mm_store_si128(dst, val_dst);
			}
		}

		for (i=0; i<h/2; i++) {
			src1 = (__m128i *) (pV + i*src_stride/2);
			src2 = src1+1;
			if (vs_dst->v_ptr) dst = (__m128i *) (vs_dst->v_ptr + i*vs_dst->pitch_y/2);
			else dst = (__m128i *)(vs_dst->video_buffer + 5*vs_dst->pitch_y * vs_dst->height/4  + i*vs_dst->pitch_y/2);

			for (j=0; j<w/32; j++, src1+=2, src2+=2, dst++) {
				val1 = _mm_load_si128(src1);
				val1 = _mm_srli_epi16(val1, 2);
				val2 = _mm_load_si128(src2);
				val2 = _mm_srli_epi16(val2, 2);
				val_dst = _mm_packus_epi16(val1, val2);
				_mm_store_si128(dst, val_dst);
			}
		}
		return GF_OK;
	
}

static GF_Err color_write_yuv422_10_to_yuv422_intrin(GF_VideoSurface *vs_dst,  unsigned char *pY, unsigned char *pU, unsigned char*pV, u32 src_stride, u32 src_width, u32 src_height, const GF_Window *_src_wnd, Bool swap_uv)
{
	u32 i, j, w, h;
	__m128i val1, val2, val_dst, *src1, *src2, *dst;
	if (!pU) {
		pU = pY + src_stride * src_height;
		pV = pY + 3*src_stride * src_height/2;
	}

	if (_src_wnd) {
		pY = pY + src_stride * _src_wnd->y + _src_wnd->x;
		pU = pU + (src_stride * _src_wnd->y  + _src_wnd->x) / 2;
		pV = pV + (src_stride * _src_wnd->y  + _src_wnd->x) / 2;
		w = _src_wnd->w;
		h = _src_wnd->h;
	} else {
		w = src_width;
		h = src_height;
	}

	if (swap_uv) {
		u8 *t = pV;
		pV = pU;
		pU = t;
	}


	
	for (i=0; i<h; i++) {
		src1 = (__m128i *)(pY + i*src_stride);
		src2 = src1+1;
		dst = (__m128i *)(vs_dst->video_buffer + i*vs_dst->pitch_y);

		for (j=0; j<w/16; j++, src1+=2, src2+=2, dst++) {
			val1 = _mm_load_si128(src1);
			val1 = _mm_srli_epi16(val1, 2);
			val2 = _mm_load_si128(src2);
			val2 = _mm_srli_epi16(val2, 2);
			val_dst = _mm_packus_epi16(val1, val2);
			_mm_store_si128(dst, val_dst);
		}
	}

	for (i=0; i<h; i++) {
		src1 = (__m128i *) (pU + i*src_stride/2);
		src2 = src1+1;
		if (vs_dst->u_ptr) dst = (__m128i *) (vs_dst->u_ptr + i*vs_dst->pitch_y/2);
		else dst = (__m128i *)(vs_dst->video_buffer + vs_dst->pitch_y * vs_dst->height + i*vs_dst->pitch_y/2);

		for (j=0; j<w/32; j++, src1+=2, src2+=2, dst++) {
			val1 = _mm_load_si128(src1);
			val1 = _mm_srli_epi16(val1, 2);
			val2 = _mm_load_si128(src2);
			val2 = _mm_srli_epi16(val2, 2);
			val_dst = _mm_packus_epi16(val1, val2);
			_mm_store_si128(dst, val_dst);
		}
	}

	for (i=0; i<h; i++) {
		src1 = (__m128i *) (pV + i*src_stride/2);
		src2 = src1+1;
		if (vs_dst->v_ptr) dst = (__m128i *) (vs_dst->v_ptr + i*vs_dst->pitch_y/2);
		else dst = (__m128i *)(vs_dst->video_buffer + 3*vs_dst->pitch_y * vs_dst->height/2  + i*vs_dst->pitch_y/2);

		for (j=0; j<w/32; j++, src1+=2, src2+=2, dst++) {
			val1 = _mm_load_si128(src1);
			val1 = _mm_srli_epi16(val1, 2);
			val2 = _mm_load_si128(src2);
			val2 = _mm_srli_epi16(val2, 2);
			val_dst = _mm_packus_epi16(val1, val2);
			_mm_store_si128(dst, val_dst);
		}
	}
	return GF_OK;
	
}

static GF_Err color_write_yuv444_10_to_yuv444_intrin(GF_VideoSurface *vs_dst, unsigned char *pY, unsigned char *pU, unsigned char*pV, u32 src_stride, u32 src_width, u32 src_height, const GF_Window *_src_wnd, Bool swap_uv)
{
	u32 i, j, w, h;
	__m128i val1, val2, val_dst, *src1, *src2, *dst;
	if (!pU) {
		pU = pY + src_stride * src_height;
		pV = pY + 2 * src_stride * src_height ;
	}

	if (_src_wnd) {
		pY = pY + src_stride * _src_wnd->y  + _src_wnd->x;
		pU = pU + src_stride * _src_wnd->y  + _src_wnd->x;
		pV = pV + src_stride * _src_wnd->y  + _src_wnd->x;
		w = _src_wnd->w;
		h = _src_wnd->h;
	}
	else {
		w = src_width;
		h = src_height;
	}

	if (swap_uv) {
		u8 *t = pV;
		pV = pU;
		pU = t;
	}


	for (i = 0; i<h; i++) {
		src1 = (__m128i *)(pY + i*src_stride);
		src2 = src1 + 1;
		dst = (__m128i *)(vs_dst->video_buffer + i*vs_dst->pitch_y);

		for (j = 0; j<w / 16; j++, src1 += 2, src2 += 2, dst++) {
			val1 = _mm_load_si128(src1);
			val1 = _mm_srli_epi16(val1, 2);
			val2 = _mm_load_si128(src2);
			val2 = _mm_srli_epi16(val2, 2);
			val_dst = _mm_packus_epi16(val1, val2);
			_mm_store_si128(dst, val_dst);
		}
	}

	for (i = 0; i<h; i++) {
		src1 = (__m128i *) (pU + i*src_stride );
		src2 = src1 + 1;
		if (vs_dst->u_ptr) dst = (__m128i *) (vs_dst->u_ptr + i*vs_dst->pitch_y );
		else dst = (__m128i *)(vs_dst->video_buffer + vs_dst->pitch_y * vs_dst->height + i*vs_dst->pitch_y );

		for (j = 0; j<w / 16; j++, src1 += 2, src2 += 2, dst++) {
			val1 = _mm_load_si128(src1);
			val1 = _mm_srli_epi16(val1, 2);
			val2 = _mm_load_si128(src2);
			val2 = _mm_srli_epi16(val2, 2);
			val_dst = _mm_packus_epi16(val1, val2);
			_mm_store_si128(dst, val_dst);
		}
	}

	for (i = 0; i<h ; i++) {
		src1 = (__m128i *) (pV + i*src_stride );
		src2 = src1 + 1;
		if (vs_dst->v_ptr) dst = (__m128i *) (vs_dst->v_ptr + i*vs_dst->pitch_y);
		else dst = (__m128i *)(vs_dst->video_buffer + 2 * vs_dst->pitch_y * vs_dst->height  + i*vs_dst->pitch_y );

		for (j = 0; j<w / 16; j++, src1 += 2, src2 += 2, dst++) {
			val1 = _mm_load_si128(src1);
			val1 = _mm_srli_epi16(val1, 2);
			val2 = _mm_load_si128(src2);
			val2 = _mm_srli_epi16(val2, 2);
			val_dst = _mm_packus_epi16(val1, val2);
			_mm_store_si128(dst, val_dst);
		}
	}
	return GF_OK;
	
}
static GF_Err color_write_yuv422_10_to_yuv_intrin(GF_VideoSurface *vs_dst, unsigned char *pY, unsigned char *pU, unsigned char*pV, u32 src_stride, u32 src_width, u32 src_height, const GF_Window *_src_wnd, Bool swap_uv)
{
	u32 i, j, w, h;
	__m128i val1, val2, val_dst, *src1, *src2, *dst;
	if (!pU) {
		pU = pY + src_stride * src_height;
		pV = pY + 3 * src_stride * src_height / 2;
	}

	if (_src_wnd) {
		pY = pY + src_stride * _src_wnd->y + _src_wnd->x;
		pU = pU + (src_stride * _src_wnd->y + _src_wnd->x) / 2;
		pV = pV + (src_stride * _src_wnd->y + _src_wnd->x) / 2;
		w = _src_wnd->w;
		h = _src_wnd->h;
	}
	else {
		w = src_width;
		h = src_height;
	}

	if (swap_uv) {
		u8 *t = pV;
		pV = pU;
		pU = t;
	}



	for (i = 0; i<h; i++) {
		src1 = (__m128i *)(pY + i*src_stride);
		src2 = src1 + 1;
		dst = (__m128i *)(vs_dst->video_buffer + i*vs_dst->pitch_y);

		for (j = 0; j<w / 16; j++, src1 += 2, src2 += 2, dst++) {
			val1 = _mm_load_si128(src1);
			val1 = _mm_srli_epi16(val1, 2);
			val2 = _mm_load_si128(src2);
			val2 = _mm_srli_epi16(val2, 2);
			val_dst = _mm_packus_epi16(val1, val2);
			_mm_store_si128(dst, val_dst);
		}
	}

	for (i = 0; i<h / 2; i++) {
		src1 = (__m128i *) (pU +  i*src_stride);
		src2 = src1 + 1;
		if (vs_dst->u_ptr) dst = (__m128i *) (vs_dst->u_ptr + i*vs_dst->pitch_y / 2);
		else dst = (__m128i *)(vs_dst->video_buffer + vs_dst->pitch_y * vs_dst->height + i*vs_dst->pitch_y / 2);

		for (j = 0; j<w / 32; j++, src1 += 2, src2 += 2, dst++) {
			val1 = _mm_load_si128(src1);
			val1 = _mm_srli_epi16(val1, 2);
			val2 = _mm_load_si128(src2);
			val2 = _mm_srli_epi16(val2, 2);
			val_dst = _mm_packus_epi16(val1, val2);
			_mm_store_si128(dst, val_dst);
		}
	}

	for (i = 0; i<h / 2; i++) {
		src1 = (__m128i *) (pV + i*src_stride);
		src2 = src1 + 1;
		if (vs_dst->v_ptr) dst = (__m128i *) (vs_dst->v_ptr + i*vs_dst->pitch_y / 2);
		else dst = (__m128i *)(vs_dst->video_buffer + 5 * vs_dst->pitch_y * vs_dst->height / 4 + i*vs_dst->pitch_y / 2);

		for (j = 0; j<w / 32; j++, src1 += 2, src2 += 2, dst++) {
			val1 = _mm_load_si128(src1);
			val1 = _mm_srli_epi16(val1, 2);
			val2 = _mm_load_si128(src2);
			val2 = _mm_srli_epi16(val2, 2);
			val_dst = _mm_packus_epi16(val1, val2);
			_mm_store_si128(dst, val_dst);
		}
	}
	return GF_OK;

}
static GF_Err color_write_yuv444_10_to_yuv_intrin(GF_VideoSurface *vs_dst, unsigned char *pY, unsigned char *pU, unsigned char*pV, u32 src_stride, u32 src_width, u32 src_height, const GF_Window *_src_wnd, Bool swap_uv)
{
	u32 i, j, w, h;
	__m128i val1, val2,val3,val4, val12, val34, val_dst, *src1, *src2,*src3,*src4, *dst;
	
	if (!pU) {
		pU = pY + src_stride * src_height;
		pV = pY + 2 * src_stride * src_height;
	}

	if (_src_wnd) {
		pY = pY + src_stride * _src_wnd->y + _src_wnd->x;
		pU = pU + src_stride * _src_wnd->y + _src_wnd->x;
		pV = pV + src_stride * _src_wnd->y + _src_wnd->x;
		w = _src_wnd->w;
		h = _src_wnd->h;
	}
	else {
		w = src_width;
		h = src_height;
	}

	if (swap_uv) {
		u8 *t = pV;
		pV = pU;
		pU = t;
	}


	for (i = 0; i<h; i++) {
		src1 = (__m128i *)(pY + i*src_stride);
		src2 = src1 + 1;
		dst = (__m128i *)(vs_dst->video_buffer + i*vs_dst->pitch_y);

		for (j = 0; j<w / 16; j++, src1 += 2, src2 += 2, dst++) {
			val1 = _mm_load_si128(src1);
			val1 = _mm_srli_epi16(val1, 2);
			val2 = _mm_load_si128(src2);
			val2 = _mm_srli_epi16(val2, 2);
			val_dst = _mm_packus_epi16(val1, val2);
			_mm_store_si128(dst, val_dst);
		}
	}

	for (i = 0; i<h / 2; i++) {
		src1 = (__m128i *) (pU + 2*i*src_stride);
		src2 = src1 + 1;
		src3 = src2 + 1;
		src4 = src3 + 1;
		if (vs_dst->u_ptr) dst = (__m128i *) (vs_dst->u_ptr + i*vs_dst->pitch_y / 2);
		else dst = (__m128i *)(vs_dst->video_buffer + vs_dst->pitch_y * vs_dst->height + i*vs_dst->pitch_y / 2);

		for (j = 0; j<w /32; j++, src1 += 4, src2 += 4,src3 +=4, src4+=4, dst++) {
			val1 = _mm_load_si128(src1);
			val1 = _mm_srli_epi32(val1, 16);
			val2 = _mm_load_si128(src2);
			val2 = _mm_srli_epi32(val2, 16);
			val12 = _mm_packs_epi32(val1, val2); 
			val12 = _mm_srli_epi16(val12, 2);

			val3 = _mm_load_si128(src3);
			
			val3 = _mm_srli_epi32(val3, 16);
			val4 = _mm_load_si128(src4);
		
			val4 = _mm_srli_epi32(val4, 16);
			val34 = _mm_packs_epi32(val3, val4); 
			val34 = _mm_srli_epi16(val34, 2);

			val_dst = _mm_packus_epi16(val12, val34);
			_mm_store_si128(dst, val_dst);
			
		}
	}

	for (i = 0; i<h / 2; i++) {
		src1 = (__m128i *) (pV + 2*i*src_stride );
		src2 = src1 + 1;
		src3 = src1 + 2;
		src4 = src1 + 3;
		if (vs_dst->v_ptr) dst = (__m128i *) (vs_dst->v_ptr + i*vs_dst->pitch_y / 2);
		else dst = (__m128i *)(vs_dst->video_buffer + 5 * vs_dst->pitch_y * vs_dst->height / 4 + i*vs_dst->pitch_y / 2);

		for (j = 0; j<w / 32; j++, src1 += 4, src2 += 4, src3 += 4, src4 += 4, dst++) {
			val1 = _mm_load_si128(src1);
			
			val1 = _mm_srli_epi32(val1, 16);
			val2 = _mm_load_si128(src2);
			
			val2 = _mm_srli_epi32(val2, 16);
			val12 = _mm_packs_epi32(val1, val2); 
			val12 = _mm_srli_epi16(val12, 2);

			val3 = _mm_load_si128(src3);
			
			val3 = _mm_srli_epi32(val3, 16);
			val4 = _mm_load_si128(src4);
			
			val4 = _mm_srli_epi32(val4, 16);
			val34 = _mm_packs_epi32(val3, val4); 
			val34 = _mm_srli_epi16(val34, 2);

			val_dst = _mm_packus_epi16(val12, val34);
			_mm_store_si128(dst, val_dst);
		}
	}
	
	return GF_OK;

}
#endif


static GF_Err color_write_yv12_10_to_yuv(GF_VideoSurface *vs_dst, GF_VideoSurface *vs_src, const GF_Window *_src_wnd, Bool swap_uv)
{
	u32 i, j, w, h;
	u8 *pY = vs_src->video_buffer;
	u8 *pU = vs_src->u_ptr;
	u8 *pV = vs_src->v_ptr;

	if (_src_wnd) {
		w = _src_wnd->w;
		h = _src_wnd->h;
	} else {
		w = vs_src->width;
		h = vs_src->height;
	}

	if (!pU) {
		pU = pY + vs_src->pitch_y * vs_src->height;
		pV = pY + 5 * vs_src->pitch_y * vs_src->height/4;
	}

#ifdef GPAC_HAS_SSE2

#ifdef GPAC_64_BITS
#define GFINTCAST  (u64)
#else
#define GFINTCAST  (u32)
#endif

	if ( (w%32 == 0)
	        && (GFINTCAST (vs_dst->video_buffer + vs_dst->pitch_y)%8 == 0)
	        && (GFINTCAST (vs_dst->video_buffer + vs_dst->pitch_y * vs_dst->height + vs_dst->pitch_y/2)%8 == 0)
	        && (GFINTCAST (pU + vs_src->pitch_y/2)%8 == 0)
	        && (GFINTCAST (pV + vs_src->pitch_y/2)%8 == 0)
	   ) {
		return color_write_yv12_10_to_yuv_intrin(vs_dst, pY, pU, pV, vs_src->pitch_y, vs_src->width, vs_src->height, _src_wnd, swap_uv);
	}
#endif

	if (_src_wnd) {
		pY = pY + vs_src->pitch_y * _src_wnd->y + _src_wnd->x;
		/*because of U and V downsampling by 2x2, working with odd Y offset will lead to a half-line shift between Y and UV components. We
		therefore force an even Y offset for U and V planes.*/
		pU = pU + (vs_src->pitch_y * (_src_wnd->y / 2) + _src_wnd->x) / 2;
		pV = pV + (vs_src->pitch_y * (_src_wnd->y / 2) + _src_wnd->x) / 2;
	}

	if (swap_uv) {
		u8 *t = pV;
		pV = pU;
		pU = t;
	}

	
	for (i=0; i<h; i++) {
		u16 *src = (u16 *) (pY + i*vs_src->pitch_y);
		u8 *dst = (u8 *) vs_dst->video_buffer + i*vs_dst->pitch_y;

		for (j=0; j<w; j++) {
			*dst = (*src) >> 2;
			dst++;
			src++;
		}
	}

	for (i=0; i<h/2; i++) {
		u16 *src = (u16 *) (pU + i*vs_src->pitch_y/2);
		u8 *dst = (u8 *) vs_dst->video_buffer + vs_dst->pitch_y * vs_dst->height + i*vs_dst->pitch_y/2;
		if (vs_dst->u_ptr) dst = (u8 *) (vs_dst->u_ptr + i*vs_dst->pitch_y/2);

		for (j=0; j<w/2; j++) {
			*dst = (*src) >> 2;
			dst++;
			src++;
		}
	}

	for (i=0; i<h/2; i++) {
		u16 *src = (u16 *) (pV + i*vs_src->pitch_y/2);
		u8 *dst = (u8 *) vs_dst->video_buffer + 5*vs_dst->pitch_y * vs_dst->height/4  + i*vs_dst->pitch_y/2;
		if (vs_dst->v_ptr) dst = (u8 *) (vs_dst->v_ptr + i*vs_dst->pitch_y/2);

		for (j=0; j<w/2; j++) {
			*dst = (*src) >> 2;
			dst++;
			src++;
		}
	}
	return GF_OK;
}

static GF_Err color_write_nv12_10_to_yuv(GF_VideoSurface *vs_dst, GF_VideoSurface *vs_src, GF_Window *_src_wnd, Bool swap_uv)
{
	u32 i, j, w, h;
	u8 *pY = vs_src->video_buffer;
	u8 *pUV = vs_src->u_ptr;

	if (_src_wnd) {
		w = _src_wnd->w;
		h = _src_wnd->h;
	} else {
		w = vs_src->width;
		h = vs_src->height;
	}


//#ifdef GPAC_HAS_SSE2
#if 0

#ifdef GPAC_64_BITS
#define GFINTCAST  (u64)
#else
#define GFINTCAST  (u32)
#endif

	if ( (w%32 == 0)
	        && (GFINTCAST (vs_dst->video_buffer + vs_dst->pitch_y)%8 == 0)
	        && (GFINTCAST (vs_dst->video_buffer + vs_dst->pitch_y * vs_dst->height + vs_dst->pitch_y/2)%8 == 0)
	        && (GFINTCAST (pU + src_stride/2)%8 == 0)
	        && (GFINTCAST (pV + src_stride/2)%8 == 0)
	   ) {
		return gf_color_write_yv12_10_to_yuv_intrin(vs_dst, pY, pU, pV, src_stride, src_width, src_height, _src_wnd, swap_uv);
	}
#endif

	if (!pUV) {
		pUV = pY + vs_src->pitch_y * vs_src->height;
	}

	if (_src_wnd) {
		pY = pY + vs_src->pitch_y * _src_wnd->y + _src_wnd->x;
		/*because of U and V downsampling by 2x2, working with odd Y offset will lead to a half-line shift between Y and UV components. We
		therefore force an even Y offset for U and V planes.*/
		pUV = pUV + (vs_src->pitch_y * (_src_wnd->y / 2) + _src_wnd->x) / 2;
	}

	for (i=0; i<h; i++) {
		u16 *src = (u16 *) (pY + i*vs_src->pitch_y);
		u8 *dst = (u8 *) vs_dst->video_buffer + i*vs_dst->pitch_y;

		for (j=0; j<w; j++) {
			*dst = (*src) >> 2;
			dst++;
			src++;
		}
	}

	for (i=0; i<h/2; i++) {
		u16 *src = (u16 *) (pUV + i*vs_src->pitch_y/2);
		u8 *dst = (u8 *) vs_dst->video_buffer + vs_dst->pitch_y * vs_dst->height + i*vs_dst->pitch_y/2;
		if (vs_dst->u_ptr) dst = (u8 *) (vs_dst->u_ptr + i*vs_dst->pitch_y/2);
		if (swap_uv) src += 1;

		for (j=0; j<w/2; j++) {
			*dst = (*src) >> 2;
			dst++;
			src++;
		}
	}

	for (i=0; i<h/2; i++) {
		u16 *src = (u16 *) (pUV + i*vs_src->pitch_y/2);
		u8 *dst = (u8 *) vs_dst->video_buffer + 5*vs_dst->pitch_y * vs_dst->height/4  + i*vs_dst->pitch_y/2;
		if (vs_dst->v_ptr) dst = (u8 *) (vs_dst->v_ptr + i*vs_dst->pitch_y/2);
		if (!swap_uv) src += 1;

		for (j=0; j<w/2; j++) {
			*dst = (*src) >> 2;
			dst++;
			src++;
		}
	}
	return GF_OK;

}

static GF_Err color_write_yuv422_10_to_yuv422(GF_VideoSurface *vs_dst, GF_VideoSurface *vs_src, GF_Window *_src_wnd, Bool swap_uv)
{
	u32 i, j, w, h;
	u8 *pY = vs_src->video_buffer;
	u8 *pU = vs_src->u_ptr;
	u8 *pV = vs_src->v_ptr;

	if (_src_wnd) {
		w = _src_wnd->w;
		h = _src_wnd->h;
	} else {
		w = vs_src->width;
		h = vs_src->height;
	}


#ifdef GPAC_HAS_SSE2

#ifdef GPAC_64_BITS
#define GFINTCAST  (u64)
#else
#define GFINTCAST  (u32)
#endif

	if ( (w%32 == 0)
	        && (GFINTCAST (vs_dst->video_buffer + vs_dst->pitch_y)%8 == 0)
	        && (GFINTCAST (vs_dst->video_buffer + vs_dst->pitch_y * vs_dst->height + vs_dst->pitch_y/2)%8 == 0)
	        && (GFINTCAST (pU + vs_src->pitch_y/2)%8 == 0)
	        && (GFINTCAST (pV + vs_src->pitch_y/2)%8 == 0)
	   ) {
		return color_write_yuv422_10_to_yuv422_intrin(vs_dst, pY, pU, pV, vs_src->pitch_y, vs_src->width, vs_src->height, _src_wnd, swap_uv);
	}
#endif

	if (!pU) {
		pU = pY + vs_src->pitch_y * vs_src->height;
		pV = pY + 3*vs_src->pitch_y * vs_src->height/2;
	}

	if (_src_wnd) {
		pY = pY + vs_src->pitch_y * _src_wnd->y + _src_wnd->x;
		pU = pU + (vs_src->pitch_y * _src_wnd->y + _src_wnd->x) / 2;
		pV = pV + (vs_src->pitch_y * _src_wnd->y + _src_wnd->x) / 2;
	}

	if (swap_uv) {
		u8 *t = pV;
		pV = pU;
		pU = t;
	}
	
	for (i=0; i<h; i++) {
		u16 *src_y = (u16 *) (pY + i*vs_src->pitch_y);
		u8 *dst_y = (u8 *) vs_dst->video_buffer + i*vs_dst->pitch_y;

		for (j=0; j<w; j++) {
			*dst_y = (*src_y) >> 2;
			dst_y++;
			src_y++;
		}
	}
	for (i=0; i<h; i++) {
		u16 *src_u = (u16 *) (pU + i*vs_src->pitch_y/2);
		u16 *src_v = (u16 *) (pV + i*vs_src->pitch_y/2);
		u8 *dst_u = (u8 *) vs_dst->video_buffer + vs_dst->width * vs_dst->height + i*vs_dst->pitch_y/2;
		u8 *dst_v = (u8 *) vs_dst->video_buffer + 3*vs_dst->pitch_y * vs_dst->height/2  + i*vs_dst->pitch_y/2;
		if (vs_dst->u_ptr) dst_u = (u8 *) (vs_dst->u_ptr + i*vs_dst->pitch_y/2);
		if (vs_dst->v_ptr) dst_v = (u8 *) (vs_dst->v_ptr + i*vs_dst->pitch_y/2);
		
		for (j=0; j<w/2; j++) {
			*dst_u = (*src_u) >> 2;
			dst_u++;
			src_u++;
			
			*dst_v = (*src_v) >> 2;
			dst_v++;
			src_v++;
		}
	}
	return GF_OK;
}

static GF_Err color_write_yuv444_10_to_yuv444(GF_VideoSurface *vs_dst, GF_VideoSurface *vs_src, GF_Window *_src_wnd, Bool swap_uv)
{
	u32 i, j, w, h;
	u8 *pY = vs_src->video_buffer;
	u8 *pU = vs_src->u_ptr;
	u8 *pV = vs_src->v_ptr;

	if (_src_wnd) {
		w = _src_wnd->w;
		h = _src_wnd->h;
	} else {
		w = vs_src->width;
		h = vs_src->height;
	}


#ifdef GPAC_HAS_SSE2

#ifdef GPAC_64_BITS
#define GFINTCAST  (u64)
#else
#define GFINTCAST  (u32)
#endif

	if ( (w%32 == 0)
	        && (GFINTCAST (vs_dst->video_buffer + vs_dst->pitch_y)%8 == 0)
	        && (GFINTCAST (vs_dst->video_buffer + vs_dst->pitch_y * vs_dst->height + vs_dst->pitch_y)%8 == 0)
	        && (GFINTCAST (pU + vs_src->pitch_y)%8 == 0)
	        && (GFINTCAST (pV + vs_src->pitch_y)%8 == 0)
	   ) {
		return color_write_yuv444_10_to_yuv444_intrin(vs_dst, pY, pU, pV, vs_src->pitch_y, vs_src->width, vs_src->height, _src_wnd, swap_uv);
	}
#endif

	if (!pU) {
		pU = pY + vs_src->pitch_y * vs_src->height;
		pV = pY + 2*vs_src->pitch_y * vs_src->height;
	}

	if (_src_wnd) {
		pY = pY + vs_src->pitch_y * _src_wnd->y  + _src_wnd->x;
		pU = pU + vs_src->pitch_y * _src_wnd->y  + _src_wnd->x ;
		pV = pV + vs_src->pitch_y * _src_wnd->y  + _src_wnd->x;
	}

	if (swap_uv) {
		u8 *t = pV;
		pV = pU;
		pU = t;
	}

	for (i=0; i<h; i++) {
		u16 *src_y = (u16 *) (pY + i*vs_src->pitch_y);
		u8 *dst_y = (u8 *) vs_dst->video_buffer + i*vs_dst->pitch_y;
		
		u16 *src_u= (u16 *) (pU + i*vs_src->pitch_y);
		u8 *dst_u = (u8 *) vs_dst->video_buffer + vs_dst->pitch_y * vs_dst->height+ i*vs_dst->pitch_y;
		
		u16 *src_v = (u16 *) (pV + i*vs_src->pitch_y);
		u8 *dst_v = (u8 *) vs_dst->video_buffer + 2*vs_dst->pitch_y * vs_dst->height + i*vs_dst->pitch_y;

		if (vs_dst->u_ptr) dst_u = (u8 *)(vs_dst->u_ptr + i*vs_dst->pitch_y);
		if (vs_dst->v_ptr) dst_v = (u8 *)(vs_dst->v_ptr + i*vs_dst->pitch_y);

		for (j=0; j<w; j++) {
			*dst_y = (*src_y) >> 2;
			dst_y++;
			src_y++;
			
			*dst_u = (*src_u) >> 2;
			dst_u++;
			src_u++;
			
		   *dst_v= (*src_v) >> 2;
			dst_v++;
			src_v++;
		}
	}
	return GF_OK;
}

static GF_Err color_write_yuv422_10_to_yuv(GF_VideoSurface *vs_dst, GF_VideoSurface *vs_src, GF_Window *_src_wnd, Bool swap_uv)
{
	u32 i, j, w, h;
	u8 *pY = vs_src->video_buffer;
	u8 *pU = vs_src->u_ptr;
	u8 *pV = vs_src->v_ptr;

	if (_src_wnd) {
		w = _src_wnd->w;
		h = _src_wnd->h;
	} else {
		w = vs_src->width;
		h = vs_src->height;
	}


#ifdef GPAC_HAS_SSE2

#ifdef GPAC_64_BITS
#define GFINTCAST  (u64)
#else
#define GFINTCAST  (u32)
#endif

	if ((w % 32 == 0)
		&& (GFINTCAST(vs_dst->video_buffer + vs_dst->pitch_y) % 8 == 0)
		&& (GFINTCAST(vs_dst->video_buffer + vs_dst->pitch_y * vs_dst->height + vs_dst->pitch_y / 2) % 8 == 0)
		&& (GFINTCAST(pU + vs_src->pitch_y / 2) % 8 == 0)
		&& (GFINTCAST(pV + vs_src->pitch_y / 2) % 8 == 0)
		) {
		return color_write_yuv422_10_to_yuv_intrin(vs_dst, pY, pU, pV, vs_src->pitch_y, vs_src->width, vs_src->height, _src_wnd, swap_uv);
	}
#endif
	
	if (!pU) {
		pU = pY + vs_src->pitch_y * vs_src->height;
		pV = pY + 3 * vs_src->pitch_y * vs_src->height/2;
	}

	if (_src_wnd) {
		pY = pY + vs_src->pitch_y * _src_wnd->y + _src_wnd->x;
		pU = pU + (vs_src->pitch_y * _src_wnd->y + _src_wnd->x) / 2;
		pV = pV + (vs_src->pitch_y * _src_wnd->y + _src_wnd->x) / 2;
	}

	if (swap_uv) {
		u8 *t = pV;
		pV = pU;
		pU = t;
	}


	for (i = 0; i<h; i++) {
		u16 *src = (u16 *)(pY + i*vs_src->pitch_y);
		u8 *dst = (u8 *)vs_dst->video_buffer + i*vs_dst->pitch_y;

		for (j = 0; j<w; j++) {
			*dst = (*src) >> 2;
			dst++;
			src++;
		}
	}

	for (i = 0; i<h/2; i++) {
		u16 *srcu = (u16 *)(pU +  i*vs_src->pitch_y);
		u16 *srcv = (u16 *)(pV +  i*vs_src->pitch_y);
		u8 *dstu, *dstv;

		if (vs_dst->u_ptr)
			dstu = (u8 *)(vs_dst->u_ptr + i*vs_dst->pitch_y / 2);
		else
			dstu = (u8 *)vs_dst->video_buffer + vs_dst->pitch_y * vs_dst->height + i*vs_dst->pitch_y / 2;

		if (vs_dst->v_ptr)
			dstv = (u8 *)(vs_dst->v_ptr + i*vs_dst->pitch_y / 2);
		else
			dstv = (u8 *)vs_dst->video_buffer + 5 * vs_dst->pitch_y * vs_dst->height / 4 + i*vs_dst->pitch_y / 2;

		for (j = 0; j<w / 2; j++) {
			*dstu = ( (srcu[0] + srcu[1]) / 2) >> 2;
			dstu++;
			srcu+=2;

			*dstv = ( (srcv[0] + srcv[1]) / 2) >> 2;
			dstv++;
			srcv+=2;
		}
	}

	return GF_OK;
}

static GF_Err color_write_yuv444_10_to_yuv(GF_VideoSurface *vs_dst, GF_VideoSurface *vs_src, GF_Window *_src_wnd, Bool swap_uv)
{
	u32 i, j, w, h;
	u8 *pY = vs_src->video_buffer;
	u8 *pU = vs_src->u_ptr;
	u8 *pV = vs_src->v_ptr;

	if (_src_wnd) {
		w = _src_wnd->w;
		h = _src_wnd->h;
	} else {
		w = vs_src->width;
		h = vs_src->height;
	}
	

#ifdef GPAC_HAS_SSE2

#ifdef GPAC_64_BITS
#define GFINTCAST  (u64)
#else
#define GFINTCAST  (u32)
#endif

	if ( (w % 32 == 0)
		&& (GFINTCAST(vs_dst->video_buffer + vs_dst->pitch_y) % 8 == 0)
		&& (GFINTCAST(vs_dst->video_buffer + vs_dst->pitch_y * vs_dst->height + vs_dst->pitch_y) % 8 == 0)
		&& (GFINTCAST(pU + vs_src->pitch_y) % 8 == 0)
		&& (GFINTCAST(pV + vs_src->pitch_y) % 8 == 0)
		) {
		return color_write_yuv444_10_to_yuv_intrin(vs_dst, pY, pU, pV, vs_src->pitch_y, vs_src->width, vs_src->height, _src_wnd, swap_uv);
	}
#endif


	if (!pU) {
		pU = pY + vs_src->pitch_y * vs_src->height;
		pV = pY + 2 * vs_src->pitch_y * vs_src->height;
	}

	if (_src_wnd) {
		pY = pY + vs_src->pitch_y * _src_wnd->y + _src_wnd->x;
		pU = pU + vs_src->pitch_y * _src_wnd->y + _src_wnd->x;
		pV = pV + vs_src->pitch_y * _src_wnd->y + _src_wnd->x;
	}

	if (swap_uv) {
		u8 *t = pV;
		pV = pU;
		pU = t;
	}
	
	for (i = 0; i<h; i++) {
		u16 *src = (u16 *)(pY + i*vs_src->pitch_y);
		u8 *dst = (u8 *)vs_dst->video_buffer + i*vs_dst->pitch_y;

		for (j = 0; j<w; j++) {
			*dst = (*src) >> 2;
			dst++;
			src++;
		}
	}

	for (i = 0; i<h/2; i++) {
		u16 *srcu1 = (u16 *)(pU + 2*i*vs_src->pitch_y );
		u16 *srcu2 = (u16 *)(pU + 2*(i+1)*vs_src->pitch_y );
		u16 *srcv1 = (u16 *)(pV + 2*i*vs_src->pitch_y );
		u16 *srcv2 = (u16 *)(pV + 2*(i+1)*vs_src->pitch_y );
		u8 *dstu, *dstv;

		if (vs_dst->u_ptr)
			dstu = (u8 *)(vs_dst->u_ptr + i*vs_dst->pitch_y / 2);
		else
			dstu = (u8 *)vs_dst->video_buffer + vs_dst->pitch_y * vs_dst->height + i*vs_dst->pitch_y / 2;

		if (vs_dst->v_ptr)
			dstv = (u8 *)(vs_dst->v_ptr + i*vs_dst->pitch_y / 2);
		else
			dstv = (u8 *)vs_dst->video_buffer + 5 * vs_dst->pitch_y * vs_dst->height / 4 + i*vs_dst->pitch_y / 2;

		for (j = 0; j<w/2 ;j++) {
			u32 u, v;
			u = (srcu1[0] + srcu1[1] + srcu2[0] + srcu2[1] ) / 4;
			*dstu = u>>2;
			dstu++;
			srcu1+=2;
			srcu2+=2;

			v = (srcv1[0] + srcv1[1] + srcv2[0] + srcv2[1] ) / 4;
			*dstv = v>>2;
			dstv++;
			srcv1+=2;
			srcv2+=2;
		}
	}
	return GF_OK;
}

static Bool is_planar_yuv(u32 pf)
{
	switch (pf) {
	case GF_PIXEL_YUV:
	case GF_PIXEL_YVU:
	case GF_PIXEL_YUV_10:
	case GF_PIXEL_YUV422:
	case GF_PIXEL_YUV422_10:
	case GF_PIXEL_YUV444:
	case GF_PIXEL_YUV444_10:
		return GF_TRUE;
	}
	return GF_FALSE;
}


static GF_Err color_write_yuv420_to_yuv(GF_VideoSurface *vs_dst, GF_VideoSurface *vs_src, GF_Window *_src_wnd, Bool swap_uv)
{
	u32 w, h, ox, oy;
	u8 *pY = vs_src->video_buffer;
	u8 *pU = vs_src->u_ptr;
	u8 *pV = vs_src->v_ptr;

	if (_src_wnd) {
		w = _src_wnd->w;
		h = _src_wnd->h;
		ox = _src_wnd->x;
		oy = _src_wnd->y;
	} else {
		w = vs_src->width;
		h = vs_src->height;
		ox = oy = 0;
	}

	if (!pU) {
		pU = pY + vs_src->pitch_y * vs_src->height;
		pV = pY + 5 * vs_src->pitch_y * vs_src->height / 4;
	}


	pY = pY + vs_src->pitch_y * oy + ox;
	/*because of U and V downsampling by 2x2, working with odd Y offset will lead to a half-line shift between Y and UV components. We
	therefore force an even Y offset for U and V planes.*/
	pU = pU + (vs_src->pitch_y * (oy / 2) + ox) / 2;
	pV = pV + (vs_src->pitch_y * (oy / 2) + ox) / 2;


	if (is_planar_yuv(vs_dst->pixel_format)) {
		/*complete source copy*/
		if ((vs_dst->pitch_y == (s32)vs_src->pitch_y) && (w == vs_src->width) && (h == vs_src->height)) {
			gf_assert(!ox);
			gf_assert(!oy);
			memcpy(vs_dst->video_buffer, pY, sizeof(u8)*w*h);
			if (vs_dst->pixel_format == GF_PIXEL_YUV) {
				memcpy(vs_dst->video_buffer + vs_dst->pitch_y * vs_dst->height, pV, sizeof(u8)*w*h/ 4);
				memcpy(vs_dst->video_buffer + 5 * vs_dst->pitch_y * vs_dst->height / 4, pU, sizeof(u8)*w*h/ 4);
			}
			else {
				memcpy(vs_dst->video_buffer + vs_dst->pitch_y * vs_dst->height, pU, sizeof(u8)*w*h / 4);
				memcpy(vs_dst->video_buffer + 5 * vs_dst->pitch_y * vs_dst->height / 4, pV, sizeof(u8)*w*h/ 4);
			}
		} else {
			u32 i;
			u8 *dst, *src, *dst2, *src2, *dst3, *src3;

			src = pY;
			dst = (u8*)vs_dst->video_buffer;

			src2 = (vs_dst->pixel_format != GF_PIXEL_YUV) ? pU : pV;
			dst2 = (u8*)vs_dst->video_buffer + vs_dst->pitch_y * vs_dst->height;
			src3 = (vs_dst->pixel_format != GF_PIXEL_YUV) ? pV : pU;
			dst3 = (u8*)vs_dst->video_buffer + 5 * vs_dst->pitch_y * vs_dst->height / 4;
			for (i = 0; i<h; i++) {
				memcpy(dst, src, w);
				src += vs_src->pitch_y;
				dst += vs_dst->pitch_y;
				if (i<h / 2) {
					memcpy(dst2, src2, w / 2);
					src2 += vs_src->pitch_y/ 2;
					dst2 += vs_dst->pitch_y / 2;
					memcpy(dst3, src3, w / 2);
					src3 += vs_src->pitch_y / 2;
					dst3 += vs_dst->pitch_y / 2;
				}
			}
		}
	}
	else if (vs_dst->pixel_format == GF_PIXEL_UYVY) {
		u32 i, j;
		for (i = 0; i<h; i++) {
			u8 *dst, *y, *u, *v;
			y = pY + i*vs_src->pitch_y;
			u = pU + (i / 2) * vs_src->pitch_y  / 2;
			v = pV + (i / 2) * vs_src->pitch_y / 2;
			dst = (u8 *)vs_dst->video_buffer + i*vs_dst->pitch_y;

			for (j = 0; j<w / 2; j++) {
				*dst = *u;
				dst++;
				u++;
				*dst = *y;
				dst++;
				y++;
				*dst = *v;
				dst++;
				v++;
				*dst = *y;
				dst++;
				y++;
			}
		}
	}
	else if (vs_dst->pixel_format == GF_PIXEL_VYUY) {
		u32 i, j;
		for (i = 0; i<h; i++) {
			u8 *dst, *y, *u, *v;
			y = pY + i*vs_src->pitch_y;
			u = pU + (i / 2) * vs_src->pitch_y / 2;
			v = pV + (i / 2) * vs_src->pitch_y / 2;
			dst = (u8 *)vs_dst->video_buffer + i*vs_dst->pitch_y;

			for (j = 0; j<w / 2; j++) {
				*dst = *v;
				dst++;
				v++;
				*dst = *y;
				dst++;
				y++;
				*dst = *u;
				dst++;
				u++;
				*dst = *y;
				dst++;
				y++;
			}
		}
	}
	else if (vs_dst->pixel_format == GF_PIXEL_YUYV) {
		u32 i, j;
		for (i = 0; i<h; i++) {
			u8 *dst, *y, *u, *v;
			y = pY + i*vs_src->pitch_y;
			u = pU + (i / 2) * vs_src->pitch_y / 2;
			v = pV + (i / 2) * vs_src->pitch_y / 2;
			dst = (u8*)vs_dst->video_buffer + i*vs_dst->pitch_y;

			for (j = 0; j<w / 2; j++) {
				*dst = *y;
				dst++;
				y++;
				*dst = *u;
				dst++;
				u++;
				*dst = *y;
				dst++;
				y++;
				*dst = *v;
				dst++;
				v++;
			}
		}
	}
	else if (vs_dst->pixel_format == GF_PIXEL_YVYU) {
		u32 i, j;
		for (i = 0; i<h; i++) {
			u8 *dst, *y, *u, *v;
			y = pY + i*vs_src->pitch_y;
			u = pU + (i / 2) * vs_src->pitch_y / 2;
			v = pV + (i / 2) * vs_src->pitch_y / 2;
			dst = (u8*)vs_dst->video_buffer + i*vs_dst->pitch_y;

			for (j = 0; j<w / 2; j++) {
				*dst = *y;
				dst++;
				y++;
				*dst = *v;
				dst++;
				v++;
				*dst = *y;
				dst++;
				y++;
				*dst = *u;
				dst++;
				u++;
			}
		}
	}
	else {
		return GF_NOT_SUPPORTED;
	}
	return GF_OK;
}

static GF_Err color_write_yuv422_to_yuv(GF_VideoSurface *vs_dst, GF_VideoSurface *vs_src, GF_Window *_src_wnd, Bool swap_uv)
{
	u32 w, h, ox, oy;
	u8 *pY = vs_src->video_buffer;
	u8 *pU = vs_src->u_ptr;
	u8 *pV = vs_src->v_ptr;

	if (_src_wnd) {
		w = _src_wnd->w;
		h = _src_wnd->h;
		ox = _src_wnd->x;
		oy = _src_wnd->y;
	}
	else {
		w = vs_src->width;
		h = vs_src->height;
		ox = oy = 0;
	}

	if (!pU) {
		pU = pY + vs_src->pitch_y * vs_src->height;
		pV = pY + 3 * vs_src->pitch_y * vs_src->height / 2;
	}


	pY = pY + vs_src->pitch_y * oy + ox;
	pU = pU + (vs_src->pitch_y * oy + ox) / 2;
	pV = pV + (vs_src->pitch_y * oy + ox) / 2;


	if (is_planar_yuv(vs_dst->pixel_format)) {
		/*complete source copy*/
		u32 i;
		u8 *dst, *src, *dst2, *src2, *dst3, *src3, *_src2, *_src3;

		src = pY;
		_src2 = (vs_dst->pixel_format != GF_PIXEL_YUV) ? pU : pV;
		_src3 = (vs_dst->pixel_format != GF_PIXEL_YUV) ? pV : pU;
		dst = (u8*)vs_dst->video_buffer;
		dst2 = (u8*)vs_dst->video_buffer + vs_dst->pitch_y * vs_dst->height;
		dst3 = (u8*)vs_dst->video_buffer + 5 * vs_dst->pitch_y * vs_dst->height / 4;
		for (i = 0; i<h; i++) {
			memcpy(dst, src, w);
			src += vs_src->pitch_y;
			dst += vs_dst->pitch_y;
			if (i < h / 2) {
				src2 = _src2 + i*vs_src->pitch_y;
				src3 = _src3 + i*vs_src->pitch_y;
				memcpy(dst2, src2, w / 2);
				memcpy(dst3, src3, w / 2);
				dst2 += vs_dst->pitch_y / 2;
				dst3 += vs_dst->pitch_y / 2;
			}
		}
		return GF_OK;
	}
	return GF_NOT_SUPPORTED;
}


static GF_Err color_write_yuv444_to_yuv(GF_VideoSurface *vs_dst, GF_VideoSurface *vs_src, GF_Window *_src_wnd, Bool swap_uv)
{
	u32 w, h, ox, oy;
	u8 *pY = vs_src->video_buffer;
	u8 *pU = vs_src->u_ptr;
	u8 *pV = vs_src->v_ptr;

	if (_src_wnd) {
		w = _src_wnd->w;
		h = _src_wnd->h;
		ox = _src_wnd->x;
		oy = _src_wnd->y;
	}
	else {
		w = vs_src->width;
		h = vs_src->height;
		ox = oy = 0;
	}

	if (!pU) {
		pU = pY + vs_src->pitch_y* vs_src->height;
		pV = pY + 2 * vs_src->pitch_y * vs_src->height;
	}

	pY = pY + vs_src->pitch_y * oy + ox;
	pU = pU + vs_src->pitch_y * oy + ox;
	pV = pV + vs_src->pitch_y * oy + ox;

	if (is_planar_yuv(vs_dst->pixel_format)) {
		/*complete source copy*/
		u32 i, j;
		u8 *dst, *src, *_src2, *_src3;

		src = pY;
		_src2 = (vs_dst->pixel_format != GF_PIXEL_YUV) ? pU : pV;
		_src3 = (vs_dst->pixel_format != GF_PIXEL_YUV) ? pV : pU;
		dst = (u8*)vs_dst->video_buffer;

		for (i = 0; i<h; i++) {
			memcpy(dst, src, w);
			src += vs_src->pitch_y;
			dst += vs_dst->pitch_y;

		}
		for (i = 0; i < h / 2; i++) {
			u8 *dst2, *src2, *dst3, *src3;
			src2 = _src2 + 2 * i*vs_src->pitch_y;
			dst2 = (u8*)vs_dst->video_buffer + vs_dst->pitch_y * vs_dst->height + i* vs_dst->pitch_y / 2;
			src3 = _src3 + 2 * i*vs_src->pitch_y;
			dst3 = (u8*)vs_dst->video_buffer + 5 * vs_dst->pitch_y * vs_dst->height / 4 + i* vs_dst->pitch_y / 2;
			for (j = 0; j<w / 2; j++) {
				*dst2 = *src2;
				dst2++;
				src2 += 2;

				*dst3 = *src3;
				dst3++;
				src3 += 2;
			}
		}
		return GF_OK;
	}
	return GF_NOT_SUPPORTED;
}

static GF_Err color_write_yvyu_to_yuv(GF_VideoSurface *vs_dst, GF_VideoSurface *vs_src, GF_Window *_src_wnd, Bool swap_uv)
{
	u32 i, j;
	u32 w, h, ox, oy;
	u8 *pY, *pU, *pV;
	
	if (_src_wnd) {
		w = _src_wnd->w;
		h = _src_wnd->h;
		ox = _src_wnd->x;
		oy = _src_wnd->y;
	}
	else {
		w = vs_src->width;
		h = vs_src->height;
		ox = oy = 0;
	}

	switch (vs_src->pixel_format) {
	case GF_PIXEL_UYVY:
		pU = vs_src->video_buffer + vs_src->pitch_y* oy + ox;
		pY = vs_src->video_buffer + vs_src->pitch_y * oy + ox + 1;
		pV = vs_src->video_buffer + vs_src->pitch_y * oy + ox + 3;
		break;
	case GF_PIXEL_YUYV:
		pY = vs_src->video_buffer + vs_src->pitch_y * oy + ox;
		pU = vs_src->video_buffer + vs_src->pitch_y * oy + ox + 1;
		pV = vs_src->video_buffer + vs_src->pitch_y * oy + ox + 3;
		break;
	case GF_PIXEL_YVYU:
		pY = vs_src->video_buffer + vs_src->pitch_y * oy + ox;
		pV = vs_src->video_buffer + vs_src->pitch_y * oy + ox + 1;
		pU = vs_src->video_buffer + vs_src->pitch_y * oy + ox + 3;
		break;
	case GF_PIXEL_VYUY:
		pV = vs_src->video_buffer + vs_src->pitch_y* oy + ox;
		pY = vs_src->video_buffer + vs_src->pitch_y * oy + ox + 1;
		pU = vs_src->video_buffer + vs_src->pitch_y * oy + ox + 3;
		break;
	default:
		return GF_NOT_SUPPORTED;
	}

	if (is_planar_yuv(vs_dst->pixel_format)) {
		u8 *dst_y, *dst_u, *dst_v;

		dst_y = (u8*)vs_dst->video_buffer;
		if (vs_dst->pixel_format == GF_PIXEL_YUV) {
			dst_v = (u8*)vs_dst->video_buffer + vs_dst->pitch_y * vs_dst->height;
			dst_u = (u8*)vs_dst->video_buffer + 5 * vs_dst->pitch_y * vs_dst->height / 4;
		}
		else {
			dst_u = (u8*)vs_dst->video_buffer + vs_dst->pitch_y * vs_dst->height;
			dst_v = (u8*)vs_dst->video_buffer + 5 * vs_dst->pitch_y * vs_dst->height / 4;
		}
		for (i = 0; i<h; i++) {
			for (j = 0; j<w; j += 2) {
				*dst_y = *pY;
				*(dst_y + 1) = *(pY + 2);
				dst_y += 2;
				pY += 4;
				if (i % 2) continue;

				*dst_u = (*pU + *(pU + vs_src->pitch_y)) / 2;
				*dst_v = (*pV + *(pV + vs_src->pitch_y)) / 2;
				dst_u++;
				dst_v++;
				pU += 4;
				pV += 4;
			}
			if (i % 2) {
				pU += vs_src->pitch_y;
				pV += vs_src->pitch_y;
			}
		}
		return GF_OK;
	}

	if (vs_src->pixel_format == vs_dst->pixel_format) {
		for (i = 0; i<h; i++) {
			char *dst = vs_dst->video_buffer + i*vs_dst->pitch_y;
			pY = vs_src->video_buffer + vs_src->pitch_y * (i + oy) + ox;
			memcpy(dst, pY, sizeof(char) * 2 * w);
		}
		return GF_OK;
	}

	for (i = 0; i<h; i++) {
		u8 *dst = vs_dst->video_buffer + i*vs_dst->pitch_y;
		u8 *y = pY + vs_src->pitch_y * i;
		u8 *u = pU + vs_src->pitch_y * i;
		u8 *v = pV + vs_src->pitch_y * i;
		switch (vs_dst->pixel_format) {
		case GF_PIXEL_UYVY:
			for (j = 0; j<w; j += 2) {
				dst[0] = *u;
				dst[1] = *y;
				dst[2] = *v;
				dst[3] = *(y + 2);
				dst += 4;
				y += 4;
				u += 4;
				v += 4;
			}
			break;
		case GF_PIXEL_YVYU:
			for (j = 0; j<w; j += 2) {
				dst[0] = *y;
				dst[1] = *v;
				dst[2] = *(y + 2);
				dst[3] = *u;
				dst += 4;
				y += 4;
				u += 4;
				v += 4;
			}
			break;
		case GF_PIXEL_YUYV:
			for (j = 0; j<w; j += 2) {
				dst[0] = *y;
				dst[1] = *u;
				dst[2] = *(y + 2);
				dst[3] = *v;
				dst += 4;
				y += 4;
				u += 4;
				v += 4;
			}
			break;
		case GF_PIXEL_VYUY:
			for (j = 0; j<w; j += 2) {
				dst[0] = *v;
				dst[1] = *y;
				dst[2] = *u;
				dst[3] = *(y + 2);
				dst += 4;
				y += 4;
				u += 4;
				v += 4;
			}
			break;
		default:
			return GF_NOT_SUPPORTED;
		}
	}
	return GF_OK;
}


u32 get_bpp(u32 pf)
{
	switch (pf) {
	case GF_PIXEL_RGB_555:
	case GF_PIXEL_RGB_565:
		return 2;
	case GF_PIXEL_RGB:
	case GF_PIXEL_BGR:
		return 3;
	case GF_PIXEL_RGBX:
	case GF_PIXEL_BGRX:
	case GF_PIXEL_XRGB:
	case GF_PIXEL_XBGR:
	case GF_PIXEL_ARGB:
	case GF_PIXEL_RGBD:
	case GF_PIXEL_RGBDS:
		return 4;
	}
	return 0;
}

static GF_Err color_write_rgb_to_24(GF_VideoSurface *vs_dst, GF_VideoSurface *vs_src, GF_Window *_src_wnd)
{
	u32 i;
	u32 w, h, ox, oy;
	u8 *src;
	u32 BPP;

	if (vs_src->pixel_format != vs_dst->pixel_format) return GF_NOT_SUPPORTED;
	BPP = get_bpp(vs_src->pixel_format);
	if (!BPP) return GF_NOT_SUPPORTED;

	if (_src_wnd) {
		w = _src_wnd->w;
		h = _src_wnd->h;
		ox = _src_wnd->x;
		oy = _src_wnd->y;
	} else {
		w = vs_src->width;
		h = vs_src->height;
		ox = oy = 0;
	}

	/*go to start of src*/
	src = vs_src->video_buffer + vs_src->pitch_x * oy + BPP * ox;

	for (i = 0; i<h; i++) {
		memcpy(vs_dst->video_buffer + i*vs_dst->pitch_y, src, sizeof(u8) * BPP * w);
		src += vs_src->pitch_y;
	}
	return GF_OK;
}


static GF_Err color_write_rgb_to_32(GF_VideoSurface *vs_dst, GF_VideoSurface *vs_src, GF_Window *_src_wnd)
{
	u32 i, j, w, h, ox, oy;
	u8 *src;
	Bool isBGR;
	u8 *dst, *cur;
	u32 BPP = get_bpp(vs_src->pixel_format);
	if (!BPP) return GF_NOT_SUPPORTED;

	if (_src_wnd) {
		w = _src_wnd->w;
		h = _src_wnd->h;
		ox = _src_wnd->x;
		oy = _src_wnd->y;
	}
	else {
		w = vs_src->width;
		h = vs_src->height;
		ox = oy = 0;
	}

	/*go to start of src*/
	src = vs_src->video_buffer + vs_src->pitch_y * oy + BPP * ox;

	if (vs_src->pixel_format == vs_dst->pixel_format) {
		for (i = 0; i<h; i++) {
			memcpy(vs_dst->video_buffer + i*vs_dst->pitch_y, src, sizeof(u8) * BPP * w);
		}
		return GF_OK;
	}
	/*get all pixels*/
	isBGR = (vs_dst->pixel_format == GF_PIXEL_BGRX) ? GF_TRUE : GF_FALSE;
	if (isBGR) {
		switch (vs_src->pixel_format) {
		case GF_PIXEL_RGB:
			for (i = 0; i<h; i++) {
				dst = (u8*)vs_dst->video_buffer + i*vs_dst->pitch_y;
				cur = src + i*vs_src->pitch_y;
				for (j = 0; j<w; j++) {
					dst[0] = *cur++;
					dst[1] = *cur++;
					dst[2] = *cur++;
					dst += 4;
				}
			}
			break;
		case GF_PIXEL_RGBDS:
		case GF_PIXEL_RGBD:
			for (i = 0; i<h; i++) {
				dst = (u8*)vs_dst->video_buffer + i*vs_dst->pitch_y;
				cur = src + i*vs_src->pitch_y;
				for (j = 0; j<w; j++) {
					dst[0] = *cur++;
					dst[1] = *cur++;
					dst[2] = *cur++;
					cur++;
					dst += 4;
				}
			}
			break;
		case GF_PIXEL_BGR:
			for (i = 0; i<h; i++) {
				dst = (u8*)vs_dst->video_buffer + i*vs_dst->pitch_y;
				cur = src + i*vs_src->pitch_y;
				for (j = 0; j<w; j++) {
					dst[2] = *cur++;
					dst[1] = *cur++;
					dst[0] = *cur++;
					dst += 4;
				}
			}
			break;
		default:
			return GF_NOT_SUPPORTED;
		}
	}
	else {
		switch (vs_src->pixel_format) {
		case GF_PIXEL_RGB:
			for (i = 0; i<h; i++) {
				dst = (u8*)vs_dst->video_buffer + i*vs_dst->pitch_y;
				cur = src + i*vs_src->pitch_y;
				for (j = 0; j<w; j++) {
					dst[2] = *cur++;
					dst[1] = *cur++;
					dst[0] = *cur++;
					dst += 4;
				}
			}
			break;
		case GF_PIXEL_RGBD:
		case GF_PIXEL_RGBDS:
			for (i = 0; i<h; i++) {
				dst = (u8*)vs_dst->video_buffer + i*vs_dst->pitch_y;
				cur = src + i*vs_src->pitch_y;
				for (j = 0; j<w; j++) {
					dst[2] = *cur++;
					dst[1] = *cur++;
					dst[0] = *cur++;
					cur++;
					dst += 4;
				}
			}
			break;
		case GF_PIXEL_BGR:
			for (i = 0; i<h; i++) {
				dst = (u8*)vs_dst->video_buffer + i*vs_dst->pitch_y;
				cur = src + i*vs_src->pitch_y;
				for (j = 0; j<w; j++) {
					dst[0] = *cur++;
					dst[1] = *cur++;
					dst[2] = *cur++;
					dst += 4;
				}
			}
			break;
		default:
			return GF_NOT_SUPPORTED;
		}
	}
	return GF_OK;
}

#endif


/* Basic SVG datatype parsing functions */
static const struct predef_col {
	const char *name;
	u8 r;
	u8 g;
	u8 b;
} predefined_colors[] =
{
	{"aliceblue",240, 248, 255},
	{"antiquewhite",250, 235, 215},
	{"aquamarine",127, 255, 212},
	{"azure",240, 255, 255},
	{"beige",245, 245, 220},
	{"bisque",255, 228, 196},
	{"black", 0, 0, 0},
	{"blanchedalmond",255, 235, 205},
	{"blue", 0, 0, 255},
	{"blueviolet",138, 43, 226},
	{"brown",165, 42, 42},
	{"burlywood",222, 184, 135},
	{"cadetblue", 95, 158, 160},
	{"chartreuse",127, 255, 0},
	{"chocolate",210, 105, 30},
	{"coral",255, 127, 80},
	{"lightpink",255, 182, 193},
	{"lightsalmon",255, 160, 122},
	{"lightseagreen", 32, 178, 170},
	{"lightskyblue",135, 206, 250},
	{"lightslategray",119, 136, 153},
	{"lightslategrey",119, 136, 153},
	{"lightsteelblue",176, 196, 222},
	{"lightyellow",255, 255, 224},
	{"lime", 0, 255, 0},
	{"limegreen", 50, 205, 50},
	{"linen",250, 240, 230},
	{"magenta",255, 0, 255},
	{"maroon",128, 0, 0},
	{"mediumaquamarine",102, 205, 170},
	{"mediumblue", 0, 0, 205},
	{"mediumorchid",186, 85, 211},
	{"cornflowerblue",100, 149, 237},
	{"cornsilk",255, 248, 220},
	{"crimson",220, 20, 60},
	{"cyan", 0, 255, 255},
	{"darkblue", 0, 0, 139},
	{"darkcyan", 0, 139, 139},
	{"darkgoldenrod",184, 134, 11},
	{"darkgray",169, 169, 169},
	{"darkgreen", 0, 100, 0},
	{"darkgrey",169, 169, 169},
	{"darkkhaki",189, 183, 107},
	{"darkmagenta",139, 0, 139},
	{"darkolivegreen", 85, 107, 47},
	{"darkorange",255, 140, 0},
	{"darkorchid",153, 50, 204},
	{"darkred",139, 0, 0},
	{"darksalmon",233, 150, 122},
	{"darkseagreen",143, 188, 143},
	{"darkslateblue", 72, 61, 139},
	{"darkslategray", 47, 79, 79},
	{"darkslategrey", 47, 79, 79},
	{"darkturquoise", 0, 206, 209},
	{"darkviolet",148, 0, 211},
	{"deeppink",255, 20, 147},
	{"deepskyblue", 0, 191, 255},
	{"dimgray",105, 105, 105},
	{"dimgrey",105, 105, 105},
	{"dodgerblue", 30, 144, 255},
	{"firebrick",178, 34, 34},
	{"floralwhite",255, 250, 240},
	{"forestgreen", 34, 139, 34},
	{"fuchsia",255, 0, 255},
	{"gainsboro",220, 220, 220},
	{"ghostwhite",248, 248, 255},
	{"gold",255, 215, 0},
	{"goldenrod",218, 165, 32},
	{"gray",128, 128, 128},
	{"grey",128, 128, 128},
	{"green", 0, 128, 0},
	{"greenyellow",173, 255, 47},
	{"honeydew",240, 255, 240},
	{"hotpink",255, 105, 180},
	{"indianred",205, 92, 92},
	{"indigo", 75, 0, 130},
	{"ivory",255, 255, 240},
	{"khaki",240, 230, 140},
	{"lavender",230, 230, 25},
	{"lavenderblush",255, 240, 245},
	{"mediumpurple",147, 112, 219},
	{"mediumseagreen", 60, 179, 113},
	{"mediumslateblue",123, 104, 238},
	{"mediumspringgreen", 0, 250, 154},
	{"mediumturquoise", 72, 209, 204},
	{"mediumvioletred",199, 21, 133},
	{"midnightblue", 25, 25, 112},
	{"mintcream",245, 255, 250},
	{"mistyrose",255, 228, 225},
	{"moccasin",255, 228, 181},
	{"navajowhite",255, 222, 173},
	{"navy", 0, 0, 128},
	{"oldlace",253, 245, 230},
	{"olive",128, 128, 0},
	{"olivedrab",107, 142, 35},
	{"orange",255, 165, 0},
	{"orangered",255, 69, 0},
	{"orchid",218, 112, 214},
	{"palegoldenrod",238, 232, 170},
	{"palegreen",152, 251, 152},
	{"paleturquoise",175, 238, 238},
	{"palevioletred",219, 112, 147},
	{"papayawhip",255, 239, 213},
	{"peachpuff",255, 218, 185},
	{"peru",205, 133, 63},
	{"pink",255, 192, 203},
	{"plum",221, 160, 221},
	{"powderblue",176, 224, 230},
	{"purple",128, 0, 128},
	{"red",255, 0, 0},
	{"rosybrown",188, 143, 143},
	{"royalblue", 65, 105, 225},
	{"saddlebrown",139, 69, 19},
	{"salmon",250, 128, 114},
	{"sandybrown",244, 164, 96},
	{"seagreen", 46, 139, 87},
	{"seashell",255, 245, 238},
	{"sienna",160, 82, 45},
	{"silver",192, 192, 192},
	{"skyblue",135, 206, 235},
	{"slateblue",106, 90, 205},
	{"slategray",112, 128, 144},
	{"slategrey",112, 128, 144},
	{"snow",255, 250, 250},
	{"springgreen", 0, 255, 127},
	{"steelblue", 70, 130, 180},
	{"tan",210, 180, 140},
	{"teal", 0, 128, 128},
	{"lawngreen",124, 252, 0},
	{"lemonchiffon",255, 250, 205},
	{"lightblue",173, 216, 230},
	{"lightcoral",240, 128, 128},
	{"lightcyan",224, 255, 255},
	{"lightgoldenrodyellow",250, 250, 210},
	{"lightgray",211, 211, 211},
	{"lightgreen",144, 238, 144},
	{"lightgrey",211, 211, 211},
	{"thistle",216, 191, 216},
	{"tomato",255, 99, 71},
	{"turquoise", 64, 224, 208},
	{"violet",238, 130, 238},
	{"wheat",245, 222, 179},
	{"white",255, 255, 255},
	{"whitesmoke",245, 245, 245},
	{"yellow",255, 255, 0},
	{"yellowgreen",154, 205, 50},
	{"aqua", 0, 255, 255},

};


GF_EXPORT
GF_Color gf_color_parse(const char *name)
{
	u32 i, count;
	u32 res;
	if (!strcmp(name, "none") )
		return 0;
	if ((name[0]=='$') || (name[0]=='#')) {
		sscanf(name+1, "%x", &res);
		if (strlen(name+1) == 8) return res;
		return res | 0xFF000000;
	}
	if (!strnicmp(name, "0x", 2) ) {
		sscanf(name+2, "%x", &res);
		if (strlen(name+2) == 8) return res;
		return res | 0xFF000000;
	}

	count = sizeof(predefined_colors) / sizeof(struct predef_col);
	for (i=0; i<count; i++) {
		if (!strcmp(name, predefined_colors[i].name)) {
			res = GF_COL_ARGB(0xFF, predefined_colors[i].r, predefined_colors[i].g, predefined_colors[i].b);
			return res;
		}
	}
	GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Unknown color code %s, using 0x00000000\n", name));
	return 0;

}

GF_EXPORT
const char *gf_color_get_name(GF_Color col)
{
	u32 i, count;
	u8 r, g, b;

	r = GF_COL_R(col);
	g = GF_COL_G(col);
	b = GF_COL_B(col);

	count = sizeof(predefined_colors) / sizeof(struct predef_col);
	for (i=0; i<count; i++) {
		if (predefined_colors[i].r != r) continue;
		if (predefined_colors[i].g != g) continue;
		if (predefined_colors[i].b != b) continue;
		return predefined_colors[i].name;
	}
	return NULL;
}


GF_EXPORT
Bool gf_color_enum(u32 *idx, GF_Color *col, const char **color_name)
{
	u32 count = sizeof(predefined_colors) / sizeof(struct predef_col);
	if (*idx>=count) return GF_FALSE;
	if (col) *col = GF_COL_ARGB(0xFF, predefined_colors[*idx].r, predefined_colors[*idx].g, predefined_colors[*idx].b);
	if (color_name) *color_name = predefined_colors[*idx].name;
	(*idx)++;
	return GF_TRUE;
}
