/* * dc-yuv2rgb.c * by Mike Melanson (mike at multimedia.cx) * No license (or warranty). Use this program however you see fit. * * This program demonstrates how to use 3D texturing/blending hardware to * convert YUV data to RGB as outlined in this paper: * * http://www.multimedia.cx/yuv-3d-rgb.txt * * The program is written for the Sega Dreamcast video game console * with its PowerVR graphics chip. It is designed to be * run with a standard KOS development setup available from: * * http://cadcdev.sourceforge.net/ * * It was developed with KOS v1.2.0 but probably operates fine under * earlier versions as well. * * Function: This program simply uses the YUV -> RGB conversion technique * to display a 512x256-pixel, quad-color texture on the screen until the * start button is pressed on the Dreamcast controller. */ #include #include static unsigned short SubVTable[256]; void init_palettes(void) { int i; unsigned char r, g, b; pvr_set_pal_format(PVR_PAL_ARGB8888); /* palette #0 (entries 0-255) is the base Y values */ for (i = 0; i < 256; i++) pvr_set_pal_entry(i, 0xFF000000 | (i << 16) | (i << 8) | (i << 0)); /* palette #1 (entries 256-511) are the additive U values */ /* red will not be altered by this pass */ r = 0; for (i = 0; i < 256; i++) { /* only U values < 128 will affect the green plane on this pass */ if (i < 128) g = (128 - i) * 0.338; else g = 0; /* only U values > 128 will affect the blue plane on this pass */ if (i > 128) b = (i - 128) * 1.732; else b = 0; pvr_set_pal_entry(i + 256, 0xFF000000 | (r << 16) | (g << 8) | (b << 0)); } /* palette #2 (entries 512-767) is the subtractive U values */ /* red will not be altered by this pass */ r = 0; for (i = 0; i < 256; i++) { /* only U values > 128 will affect the green plane on this pass */ if (i > 128) g = (i - 128) * 0.338; else g = 0; /* only U values < 128 will affect the blue plane on this pass */ if (i < 128) b = (128 - i) * 1.732; else b = 0; pvr_set_pal_entry(i + 512, 0xFF000000 | (r << 16) | (g << 8) | (b << 0)); } /* palette #3 (entries 768-1023) is the additive V values */ /* blue will not be altered by this pass */ b = 0; for (i = 0; i < 256; i++) { /* only V values < 128 will affect the green plane on this pass */ if (i < 128) g = (128 - i) * 0.698; else g = 0; /* only V values > 128 will affect the blue plane on this pass */ if (i > 128) r = (i - 128) * 1.370; else r = 0; pvr_set_pal_entry(i + 768, 0xFF000000 | (r << 16) | (g << 8) | (b << 0)); } /* palette #4 (not an official palette) is the subtractive V values */ /* blue will not be altered by this pass */ b = 0; for (i = 0; i < 256; i++) { /* only V values > 128 will affect the green plane on this pass */ if (i > 128) g = (i - 128) * 0.698; else g = 0; /* only V values < 128 will affect the blue plane on this pass */ if (i < 128) r = (128 - i) * 1.370; else r = 0; /* top 5 bits of r, top 6 bits of green, b will always be 0 */ SubVTable[i] = ((r >> 3) << 11) | ((g >> 2) << 5); } } void submit_tr_poly(int x1, int y1, int x2, int y2, int width, int height, int palette, pvr_ptr_t texture_ptr, int texture_format, int poly_type, int pass_type) { pvr_poly_cxt_t cxt; pvr_poly_hdr_t hdr; pvr_vertex_t vert; pvr_poly_cxt_txr(&cxt, poly_type, texture_format, width, height, texture_ptr, PVR_FILTER_BILINEAR); cxt.txr.format |= PVR_TXRFMT_8BPP_PAL(palette); if (poly_type != PVR_LIST_OP_POLY) { if (pass_type == 0) { cxt.blend.src = PVR_BLEND_ONE; cxt.blend.dst = PVR_BLEND_ONE; } else { cxt.blend.src = PVR_BLEND_INVDESTCOLOR; cxt.blend.dst = PVR_BLEND_INVDESTCOLOR; } } pvr_poly_compile(&hdr, &cxt); pvr_prim(&hdr, sizeof(hdr)); vert.argb = PVR_PACK_COLOR(1.0f, 1.0f, 1.0f, 1.0f); vert.oargb = 0; vert.flags = PVR_CMD_VERTEX; vert.x = x1; vert.y = y1; vert.z = 1; vert.u = 0.0; vert.v = 0.0; pvr_prim(&vert, sizeof(vert)); vert.x = x2 - 1; vert.y = y1; vert.z = 1; vert.u = 1.0; vert.v = 0.0; pvr_prim(&vert, sizeof(vert)); vert.x = x1; vert.y = y2 - 1; vert.z = 1; vert.u = 0.0; vert.v = 1.0; pvr_prim(&vert, sizeof(vert)); vert.x = x2 - 1; vert.y = y2 - 1; vert.z = 1; vert.u = 1.0; vert.v = 1.0; vert.flags = PVR_CMD_VERTEX_EOL; pvr_prim(&vert, sizeof(vert)); } #define WIDTH 512 #define HEIGHT 256 int main() { unsigned char y_plane[WIDTH * HEIGHT]; unsigned char u_plane[WIDTH * HEIGHT / 4]; unsigned char v_plane[WIDTH * HEIGHT / 4]; unsigned short sub_v_plane[WIDTH * HEIGHT / 4]; pvr_ptr_t y_texture; pvr_ptr_t u_texture; pvr_ptr_t v_texture; pvr_ptr_t sub_v_texture; int i, x, y; cont_cond_t cont; vid_set_mode(DM_640x480_NTSC_IL, PM_RGB565); /* init pvr subsystem */ pvr_init_defaults(); /* fill up the palettes */ init_palettes(); /* allocate the textures */ y_texture = pvr_mem_malloc(WIDTH * HEIGHT); u_texture = pvr_mem_malloc(WIDTH * HEIGHT / 4); v_texture = pvr_mem_malloc(WIDTH * HEIGHT / 4); sub_v_texture = pvr_mem_malloc(WIDTH * HEIGHT / 2); if (!y_texture || !u_texture || !v_texture || !sub_v_texture) { printf ("*** could not allocate textures\n"); return 0; } /* contrive a quad-colored YUV texture: * white red * green blue * * white = (255, 128, 128) * red = (65, 90, 240) * green = (129, 91, 24) * blue = (25, 240, 110) */ for (y = 0; y < HEIGHT / 2; y++) { for (x = 0; x < WIDTH / 2; x++) { y_plane[y * WIDTH + x] = 0xFF; y_plane[y * WIDTH + (WIDTH / 2) + x] = 65; y_plane[(y + (HEIGHT / 2)) * WIDTH + x] = 129; y_plane[(y + (HEIGHT / 2)) * WIDTH + (WIDTH / 2) + x] = 25; } } for (y = 0; y < HEIGHT / 4; y++) { for (x = 0; x < WIDTH / 4; x++) { u_plane[y * (WIDTH / 2) + x] = 0x80; u_plane[y * (WIDTH / 2) + (WIDTH / 4) + x] = 90; u_plane[(y + (HEIGHT / 4)) * (WIDTH / 2) + x] = 91; u_plane[(y + (HEIGHT / 4)) * (WIDTH / 2) + (WIDTH / 4) + x] = 240; v_plane[y * (WIDTH / 2) + x] = 0x80; v_plane[y * (WIDTH / 2) + (WIDTH / 4) + x] = 240; v_plane[(y + (HEIGHT / 4)) * (WIDTH / 2) + x] = 24; v_plane[(y + (HEIGHT / 4)) * (WIDTH / 2) + (WIDTH / 4) + x] = 110; } } /* build the subtractive V plane */ for (i = 0; i < (WIDTH * HEIGHT) / 4; i++) { sub_v_plane[i] = SubVTable[v_plane[i]]; } /* twiddle and transfer the planes into texture memory */ pvr_txr_load_ex(y_plane, y_texture, WIDTH, HEIGHT, PVR_TXRLOAD_8BPP); pvr_txr_load_ex(u_plane, u_texture, WIDTH / 2, HEIGHT / 2, PVR_TXRLOAD_8BPP); pvr_txr_load_ex(v_plane, v_texture, WIDTH / 2, HEIGHT / 2, PVR_TXRLOAD_8BPP); pvr_txr_load_ex(sub_v_plane, sub_v_texture, WIDTH / 2, HEIGHT / 2, PVR_TXRLOAD_16BPP); /* do the sequence twice because of the double-buffering */ for (i = 0; i < 2; i++) { /* plot the textures and do the math */ /* prep the PVR hardware */ pvr_wait_ready(); pvr_scene_begin(); /* base Y plane */ pvr_list_begin(PVR_LIST_OP_POLY); submit_tr_poly(0, 0, WIDTH, HEIGHT, WIDTH, HEIGHT, 0, y_texture, PVR_TXRFMT_PAL8BPP, PVR_LIST_OP_POLY, 0); pvr_list_finish(); pvr_list_begin(PVR_LIST_TR_POLY); /* additive U pass */ submit_tr_poly(0, 0, WIDTH, HEIGHT, WIDTH / 2, HEIGHT / 2, 1, u_texture, PVR_TXRFMT_PAL8BPP, PVR_LIST_TR_POLY, 0); /* subtractive U pass */ submit_tr_poly(0, 0, WIDTH, HEIGHT, WIDTH / 2, HEIGHT / 2, 2, u_texture, PVR_TXRFMT_PAL8BPP, PVR_LIST_TR_POLY, 1); /* additive V pass */ submit_tr_poly(0, 0, WIDTH, HEIGHT, WIDTH / 2, HEIGHT / 2, 3, v_texture, PVR_TXRFMT_PAL8BPP, PVR_LIST_TR_POLY, 0); /* subtractive V pass with 16-bit texture */ submit_tr_poly(0, 0, WIDTH, HEIGHT, WIDTH / 2, HEIGHT / 2, 0, sub_v_texture, PVR_TXRFMT_RGB565, PVR_LIST_TR_POLY, 1); pvr_list_finish(); pvr_scene_finish(); } printf (" press start to exit...\n"); do { if (cont_get_cond(maple_first_controller(), &cont)) printf ("Error getting controller status\n"); cont.buttons = ~cont.buttons; vid_waitvbl(); } while (!(cont.buttons & CONT_START)); return 0; }