/*---------------------------------------------------------------------------* Project: TwlSDK - GX - demos - UnitTours/DEMOLib File: DEMOBitmap.c Copyright 2007-2008 Nintendo. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Date:: 2008-09-17 #$ $Rev: 8556 $ $Author: okubata_ryoma $ *---------------------------------------------------------------------------*/ #ifdef SDK_TWL #include #else #include #endif #include "DEMOBitmap.h" typedef struct DEMOBitmapContext { u32 require_flip : 1; u32 bitmap_modified : 1; u32 main_bg_modified : 1; u32 main_oam_modified : 1; u32 sub_bg_modified : 1; u32 sub_oam_modified : 1; u32 : 26; OSThreadQueue flip_wait_q[1]; void (*HookedConsole)(int console, const char *string); GXRgb bitmap_ground_color; GXRgb bitmap_text_color; u8 padding[12]; GXRgb bitmap_frame[GX_LCD_SIZE_X * GX_LCD_SIZE_Y] ATTRIBUTE_ALIGN(32); u16 main_bg[32 * 24] ATTRIBUTE_ALIGN(32); GXOamAttr main_oam[128] ATTRIBUTE_ALIGN(32); u16 sub_bg[32 * 24] ATTRIBUTE_ALIGN(32); GXOamAttr sub_oam[128] ATTRIBUTE_ALIGN(32); char sub_log[24][32 + 1]; int sub_log_count; } DEMOBitmapContext; static DEMOBitmapContext bitmap ATTRIBUTE_ALIGN(32); extern void VBlankIntr(void); /*---------------------------------------------------------------------------* Name: VBlankIntr Description: V-Blank interrupt handler Arguments: None Returns: None *---------------------------------------------------------------------------*/ SDK_WEAK_SYMBOL void VBlankIntr(void) { OS_SetIrqCheckFlag(OS_IE_V_BLANK); } /*---------------------------------------------------------------------------* Name: LogConsole Description: Accumulates printf output in a log and also outputs it to the original console Arguments: string: Output string Returns: None *---------------------------------------------------------------------------*/ static void LogConsole(int console, const char *string) { DEMOBitmapContext *const context = &bitmap; DEMOPutLog("%s", string); (*context->HookedConsole)(console, string); } /*---------------------------------------------------------------------------* Name: OnVBlank Description: V-Blank processing Arguments: None Returns: None *---------------------------------------------------------------------------*/ static void OnVBlank(void) { DEMOBitmapContext *const context = &bitmap; if (context->require_flip) { if (context->bitmap_modified) { context->bitmap_modified = 0; GX_LoadBG3Bmp(context->bitmap_frame, 0, sizeof(context->bitmap_frame)); } if (context->main_bg_modified) { context->main_bg_modified = 0; GX_LoadBG1Scr(context->main_bg, 0, sizeof(context->main_bg)); } if (context->main_oam_modified) { context->main_oam_modified = 0; GX_LoadOAM(context->main_oam, 0, sizeof(context->main_oam)); } if (context->sub_bg_modified) { context->sub_bg_modified = 0; GXS_LoadBG0Scr(context->sub_bg, 0, sizeof(context->sub_bg)); } if (context->sub_oam_modified) { context->sub_oam_modified = 0; GXS_LoadOAM(context->sub_oam, 0, sizeof(context->sub_oam)); } context->require_flip = 0; OS_WakeupThread(context->flip_wait_q); } VBlankIntr(); } /*---------------------------------------------------------------------------* Name: DEMOInitDisplayBitmap Description: Initializes the DEMO in bitmap render mode Arguments: None Returns: None *---------------------------------------------------------------------------*/ void DEMOInitDisplayBitmap(void) { // Initialize GX master settings // VRAM-A: 3D 128kB (BG0) // VRAM-B: // VRAM-C: (ext) 128kB // VRAM-D: main-BG 128kB (BG1=scr:2kB,chr:24kB) (BG3=scr:2kB,chr:96kB) // VRAM-E: 3D-PLT 64kB (BG0) // VRAM-FG: main-OBJ 32kB (OBJ) // VRAM-H: sub-BG 32kB (BG0=scr:2kB,chr:24kB) // VRAM-I: sub-OBJ 16kB (OBJ) // // main LCD: mode4,3D-on,BG0,BG1,BG3,OBJ // BG0: 3D // BG1: TXT-BG,256x256,4bit,scr=1E800,chr=18000 // BG3: BMP-BG,256x192,16bit,scr=1E000,chr=00000 // OBJ: multi-purpose // // sub LCD: mode0,BG0,OBJ // BG0: TXT-BG,256x256,4bit,scr06000,chr00000 // OBJ: multi-purpose G3X_Init(); G3X_InitMtxStack(); GX_SetBankForTex(GX_VRAM_TEX_0_A); GX_SetBankForBG(GX_VRAM_BG_128_D); GX_SetBankForTexPltt(GX_VRAM_TEXPLTT_0123_E); GX_SetBankForOBJ(GX_VRAM_OBJ_32_FG); GX_SetBankForSubBG(GX_VRAM_SUB_BG_32_H); GX_SetBankForSubOBJ(GX_VRAM_SUB_OBJ_16_I); GX_SetGraphicsMode(GX_DISPMODE_GRAPHICS, GX_BGMODE_4, GX_BG0_AS_3D); GX_SetVisiblePlane(GX_PLANEMASK_BG0 | GX_PLANEMASK_BG1 | GX_PLANEMASK_BG3 | GX_PLANEMASK_OBJ); GXS_SetGraphicsMode(GX_BGMODE_0); GXS_SetVisiblePlane(GX_PLANEMASK_BG0 | GX_PLANEMASK_OBJ); G2_BlendNone(); G2S_BlendNone(); GX_Power2DSub(TRUE); // Main-3D { G2_SetBG0Priority(2); G3X_SetShading(GX_SHADING_TOON); G3X_AntiAlias(TRUE); } // Main-TXT-BG { G2_SetBG1Control(GX_BG_SCRSIZE_TEXT_256x256, GX_BG_COLORMODE_16, GX_BG_SCRBASE_0xe800, GX_BG_CHARBASE_0x18000, GX_BG_EXTPLTT_01); GX_SetBGScrOffset(GX_BGSCROFFSET_0x10000); G2_SetBG1Priority(0); G2_BG1Mosaic(FALSE); } // Main-BMP-BG { G2_SetBG3ControlDCBmp(GX_BG_SCRSIZE_DCBMP_256x256, GX_BG_AREAOVER_XLU, GX_BG_BMPSCRBASE_0x00000); G2_SetBG3Priority(1); } // Sub-TXT-BG { G2S_SetBG0Control(GX_BG_SCRSIZE_TEXT_256x256, GX_BG_COLORMODE_16, GX_BG_SCRBASE_0x6000, GX_BG_CHARBASE_0x00000, GX_BG_EXTPLTT_01); G2S_SetBG0Priority(1); G2S_BG0Mosaic(FALSE); } G3_SwapBuffers(GX_SORTMODE_AUTO, GX_BUFFERMODE_W); G3X_SetClearColor(GX_RGB(0, 0, 0), 31, 0x7fff, 63, FALSE); DEMOSetViewPort(0, 0, GX_LCD_SIZE_X, GX_LCD_SIZE_Y); { static const GXRgb pal[16] = { GX_RGB(0, 0, 0), GX_RGB(31, 31, 31), }; MI_CpuFill32(&bitmap, 0x00000000, sizeof(bitmap)); GX_LoadBGPltt(pal, 0x0000, sizeof(pal)); GX_LoadBG1Char(DEMOAsciiChr, 0x00000, sizeof(DEMOAsciiChr)); GXS_LoadBGPltt(pal, 0x0000, sizeof(pal)); GXS_LoadBG0Char(DEMOAsciiChr, 0x00000, sizeof(DEMOAsciiChr)); OS_InitThreadQueue(bitmap.flip_wait_q); OS_SetIrqFunction(OS_IE_V_BLANK, OnVBlank); (void)OS_EnableIrqMask(OS_IE_V_BLANK); } } /*---------------------------------------------------------------------------* Name: DEMO_DrawFlip Description: Applies the current rendering content to VRAM Arguments: None Returns: None *---------------------------------------------------------------------------*/ void DEMO_DrawFlip() { DEMOBitmapContext *const context = &bitmap; if (context->bitmap_modified) { DC_StoreRange(context->bitmap_frame, sizeof(context->bitmap_frame)); } if (context->main_bg_modified) { DC_StoreRange(context->main_bg, sizeof(context->main_bg)); } if (context->main_oam_modified) { DC_StoreRange(context->main_oam, sizeof(context->main_oam)); } if (context->sub_bg_modified) { const int visible_lines = 20; const int palette = 0; int offset = (context->sub_log_count + 1 > visible_lines) ? (context->sub_log_count + 1 - visible_lines) : 0; int x, y; for (y = 0; y < visible_lines; ++y) { u16 *dst = &context->sub_bg[y * 32]; const char *src = context->sub_log[(offset + y) % 24]; for (x = 0; (x < 32) && src[x] && (src[x] != '\n'); ++x) { dst[x] = (u16)((u8)src[x] | (palette << 12)); } for (; x < 32; ++x) { dst[x] = (u16)(0 | (palette << 12)); } } DC_StoreRange(context->sub_bg, sizeof(context->sub_bg)); } if (context->sub_oam_modified) { DC_StoreRange(context->sub_oam, sizeof(context->sub_oam)); } DC_WaitWriteBufferEmpty(); { OSIntrMode bak_intr = OS_DisableInterrupts(); context->require_flip = 1; // while (context->require_flip) // { // OS_SleepThread(context->flip_wait_q); // } (void)OS_RestoreInterrupts(bak_intr); } } /*---------------------------------------------------------------------------* Name: DEMOVerifyGXRgb Description: Checks the validity of color values. Arguments: color: Color value that is thought to be within the GXRgb range Returns: Returns color unchanged if it is a valid GXRgb value and 0xFFFF if it is out of range *---------------------------------------------------------------------------*/ GXRgb DEMOVerifyGXRgb(int color) { if((color < 0) || (color > 0xFFFF)) { static BOOL once = FALSE; if(!once) { OS_TWarning("color(%04X) exceeds GXRgb range.\n", color); once = TRUE; } color = 0xFFFF; } return (GXRgb)color; } /*---------------------------------------------------------------------------* Name: DEMOSetBitmapTextColor Description: Sets the text color for bitmap rendering Arguments: color: The color to set Returns: None *---------------------------------------------------------------------------*/ void DEMOiSetBitmapTextColor(GXRgb color) { DEMOBitmapContext *context = &bitmap; context->bitmap_text_color = color; } /*---------------------------------------------------------------------------* Name: DEMOSetBitmapGroundColor Description: Sets the background color for bitmap rendering Arguments: color: The color to set Returns: None *---------------------------------------------------------------------------*/ void DEMOiSetBitmapGroundColor(GXRgb color) { DEMOBitmapContext *context = &bitmap; context->bitmap_ground_color = color; } /*---------------------------------------------------------------------------* Name: DEMOiFillRect Description: Renders a rectangle to the bitmap. Arguments: x: x coordinate for rendering y: y coordinate for rendering wx: x width for rendering wy: y width for rendering color: The color to set Returns: None *---------------------------------------------------------------------------*/ void DEMOiFillRect(int x, int y, int wx, int wy, GXRgb color) { if ((x < GX_LCD_SIZE_X) && (y < GX_LCD_SIZE_Y)) { if (wx > GX_LCD_SIZE_X - x) { wx = GX_LCD_SIZE_X - x; } if (wy > GX_LCD_SIZE_Y - y) { wy = GX_LCD_SIZE_Y - y; } if ((wx > 0) && (wy > 0)) { DEMOBitmapContext *context = &bitmap; GXRgb *dst = &context->bitmap_frame[x + y * GX_LCD_SIZE_X]; while (--wy >= 0) { MI_CpuFill16(dst, color, wx * sizeof(GXRgb)); dst += GX_LCD_SIZE_X; } context->bitmap_modified = 1; } } } /*---------------------------------------------------------------------------* Name: DEMOBlitRect Description: Transfers a rectangle to a bitmap Arguments: x: x coordinate for rendering y: y coordinate for rendering wx: x width for rendering wy: y width for rendering image: The original image to transfer stroke: Pixel count per each of the original lines to transfer Returns: None *---------------------------------------------------------------------------*/ void DEMOBlitRect(int x, int y, int wx, int wy, const GXRgb *image, int stroke) { if ((x < GX_LCD_SIZE_X) && (y < GX_LCD_SIZE_Y)) { if (wx > GX_LCD_SIZE_X - x) { wx = GX_LCD_SIZE_X - x; } if (wy > GX_LCD_SIZE_Y - y) { wy = GX_LCD_SIZE_Y - y; } if ((wx > 0) && (wy > 0)) { DEMOBitmapContext *context = &bitmap; GXRgb *dst = &context->bitmap_frame[x + y * GX_LCD_SIZE_X]; while (--wy >= 0) { MI_CpuCopy16(image, dst, wx * sizeof(GXRgb)); image += stroke; dst += GX_LCD_SIZE_X; } context->bitmap_modified = 1; } } } /*---------------------------------------------------------------------------* Name: DEMOBlitTex16 Description: Transfers a 16-color texture to a bitmap Arguments: x: x coordinate for rendering y: y coordinate for rendering wx: Rendering width in the x-direction (this must be an integer multiple of 8 pixels) wy: Rendering width in the y-direction (this must be an integer multiple of 8 pixels) chr: Character image (4x8x8 one-dimensional character format) plt: Palette image (index 0 is treated as transparent) Returns: None *---------------------------------------------------------------------------*/ void DEMOBlitTex16(int x, int y, int wx, int wy, const void *chr, const GXRgb *plt) { if ((x < GX_LCD_SIZE_X) && (y < GX_LCD_SIZE_Y)) { int stroke = wx; if (wx > GX_LCD_SIZE_X - x) { wx = GX_LCD_SIZE_X - x; } if (wy > GX_LCD_SIZE_Y - y) { wy = GX_LCD_SIZE_Y - y; } if ((wx > 0) && (wy > 0)) { DEMOBitmapContext *context = &bitmap; const u8 *src = (const u8 *)chr; GXRgb *dst = &context->bitmap_frame[x + y * GX_LCD_SIZE_X]; int tx, ty, dx, dy, nx, ny; for (ty = 0; ty < wy; ty += 8) { ny = MATH_MIN(wy - ty, 8); for (tx = 0; tx < wx; tx += 8) { nx = MATH_MIN(wx - tx, 8); for (dy = 0; dy < ny; ++dy) { for (dx = 0; dx < nx; ++dx) { u8 index = (u8)((src[(dy * 8 + dx) / 2] >> ((dx & 1) * 4)) & 0x0F); if (index > 0) { dst[(ty + dy) * GX_LCD_SIZE_X + tx + dx] = (GXRgb)(plt[index] | 0x8000); } } } src += 8 * 8 / 2; } src += (stroke - tx) * 8 / 2; } context->bitmap_modified = 1; } } } /*---------------------------------------------------------------------------* Name: DEMODrawLine Description: Draws a line on a bitmap Arguments: sx: X-coordinate of the starting point sy: Y-coordinate of the starting point tx : Width to the ending point in the x-direction ty: Width to the ending point in the Y-direction color: the color to set Returns: None *---------------------------------------------------------------------------*/ void DEMOiDrawLine(int sx, int sy, int tx, int ty, GXRgb color) { DEMOBitmapContext *context = &bitmap; int tmp; // Normalize in the x-direction if (sx > tx) { tmp = sx + 1, sx = tx + 1, tx = tmp; tmp = sy, sy = ty, ty = tmp; } { GXRgb *dst = NULL; int wx = tx - sx; int wy = ty - sy; // Optimize vertically if (!wx) { if (wy < 0) { wy = -wy; tmp = sy + 1, sy = ty + 1, ty = tmp; } if ((sx >= 0) && (sx < GX_LCD_SIZE_X) && (sy < GX_LCD_SIZE_Y)) { if (sy < 0) { wy += sy, sy = 0; } if (wy > GX_LCD_SIZE_Y - sy) { wy = GX_LCD_SIZE_Y - sy; } dst = &context->bitmap_frame[sx + sy * GX_LCD_SIZE_X]; while (--wy >= 0) { *dst = color; dst += GX_LCD_SIZE_X; } } } // Optimize horizontally else if (!wy) { if ((sy >= 0) && (sy < GX_LCD_SIZE_Y) && (sx < GX_LCD_SIZE_X)) { if (sx < 0) { wx += sx, sx = 0; } if (wx > GX_LCD_SIZE_X - sx) { wx = GX_LCD_SIZE_X - sx; } dst = &context->bitmap_frame[sx + sy * GX_LCD_SIZE_X]; MI_CpuFill16(dst, color, wx * sizeof(GXRgb)); } } // A general all-purpose line with a slope else { int n, dx, dy; int y_delta = +1; int y_ofs = GX_LCD_SIZE_X; dst = &context->bitmap_frame[sx + sy * GX_LCD_SIZE_X]; if (wy < 0) { wy = -wy; y_delta = -y_delta; y_ofs = -y_ofs; } dx = wy - 1, dy = wx - 1; --sx, sy -= y_delta; for (n = wx * wy; --n >= 0;) { BOOL moved = FALSE; if (++dx >= wy) { moved = TRUE, dx = 0, ++sx, dst += 1; } if (++dy >= wx) { moved = TRUE, dy = 0, sy += y_delta, dst += y_ofs; } if (moved && (sx >= 0) && (sx < GX_LCD_SIZE_X) && (sy >= 0) && (sy < GX_LCD_SIZE_Y)) { *dst = color; } } } } } /*---------------------------------------------------------------------------* Name: DEMODrawFrame Description: Renders a frame to the bitmap. Arguments: x: x coordinate for rendering y: y coordinate for rendering wx: x width for rendering wy: y width for rendering color: The color to set Returns: None *---------------------------------------------------------------------------*/ void DEMOiDrawFrame(int x, int y, int wx, int wy, GXRgb color) { DEMOiFillRect(x, y, wx, 1, color); DEMOiFillRect(x, y + wy - 1, wx, 1, color); DEMOiFillRect(x, y + 1, 1, wy - 1, color); DEMOiFillRect(x + wx - 1, y + 1, 1, wy - 1, color); } /*---------------------------------------------------------------------------* Name: DEMODrawText Description: Renders a string on a bitmap Arguments: x: x coordinate for rendering y: y coordinate for rendering format: Formatted character string Returns: None *---------------------------------------------------------------------------*/ void DEMODrawText(int x, int y, const char *format, ...) { static char tmp[512]; { va_list va; va_start(va, format); (void)OS_VSNPrintf(tmp, sizeof(tmp) - 2, format, va); va_end(va); } { DEMOBitmapContext *context = &bitmap; const char *s = tmp; int n = sizeof(tmp) - 1; GXRgb *p = &context->bitmap_frame[x + y * GX_LCD_SIZE_X]; GXRgb txt = context->bitmap_text_color; GXRgb gnd = context->bitmap_ground_color; int px = 0; int rep = 0; for (; (n > 0) && *s; ++s, --n) { int c = (u8)*s; if (STD_IsSjisLeadByte(c)) { c = (c << 8) | (u8)*++s; } switch (c) { case '\0': return; case '\r': c = s[1]; if (c == '\n') { ++s, --n; } case '\n': y += 8; p += GX_LCD_SIZE_X * 8; px = 0; break; case '\t': { const int align = 4; rep = align - ((px / 8) & (align - 1)); c = ' '; } goto put_char; default: rep = 1; goto put_char; put_char: while (--rep >= 0) { int tx = x + px; if ((tx > -8) && (tx < GX_LCD_SIZE_X) && (y > -8) && (y < GX_LCD_SIZE_Y)) { int ox = 0, oy = 0; int wx = 8, wy = 8; if (tx < 0) { ox = -tx; } else { wx = MATH_MIN(wx, GX_LCD_SIZE_X - tx); } if (y < 0) { oy = -y; } else { wy = MATH_MIN(wy, GX_LCD_SIZE_Y - y); } { const u32* DEMO_GetSjisFont(int code); const u32 *src = DEMO_GetSjisFont(c); GXRgb *dst = &p[GX_LCD_SIZE_X * oy + px]; int stroke = GX_LCD_SIZE_X; for (; oy < wy; ++oy) { int i; const u32 bits = src[oy]; for (i = ox; i < wx; ++i) { if (((bits >> (i * 4)) & 0xF) != 0) { dst[i] = txt; } else if (gnd != DEMO_RGB_NONE) { dst[i] = gnd; } } dst += stroke; } } } px += 8; } break; } } context->bitmap_modified = 1; } } /*---------------------------------------------------------------------------* Name: DEMOClearString Description: Clears all BG text Arguments: None Returns: None *---------------------------------------------------------------------------*/ void DEMOClearString(void) { DEMOBitmapContext *context = &bitmap; MI_CpuFillFast(context->main_bg, 0x00000000, sizeof(context->main_bg)); context->main_bg_modified = 1; } /*---------------------------------------------------------------------------* Name: DEMOPutString Description: Renders BG text. Arguments: x: x coordinate for rendering y: y coordinate for rendering format: Formatted character string Returns: None *---------------------------------------------------------------------------*/ void DEMOPutString(int x, int y, const char *format, ...) { DEMOBitmapContext *context = &bitmap; u16 *dst = &context->main_bg[y * 32]; const int palette = 0; char tmp[32 + 1]; { va_list va; va_start(va, format); (void)OS_VSNPrintf(tmp + x, sizeof(tmp) - x, format, va); va_end(va); } for (; (x < 32) && tmp[x]; ++x) { dst[x] = (u16)((u8)tmp[x] | (palette << 12)); } context->main_bg_modified = 1; } /*---------------------------------------------------------------------------* Name: DEMOPutLog Description: Displays a log string to the sub screen Arguments: format: Formatted character string Returns: None *---------------------------------------------------------------------------*/ void DEMOPutLog(const char *format, ...) { DEMOBitmapContext *context = &bitmap; static char tmp[512]; { va_list va; va_start(va, format); (void)OS_VSNPrintf(tmp, sizeof(tmp), format, va); va_end(va); } { const char *src = tmp; char *dst = context->sub_log[context->sub_log_count % 24]; int x = STD_GetStringLength(dst); for (; *src; ++src) { switch (*src) { case '\r': if (src[1] == '\n') { break; } // fall case '\n': dst[(x < 32) ? x : 32] = '\0'; ++context->sub_log_count; dst = context->sub_log[context->sub_log_count % 24]; x = 0; break; default: if (x < 32) { dst[x++] = *src; } break; } } dst[x] = '\0'; } context->sub_bg_modified = 1; } /*---------------------------------------------------------------------------* Name: DEMOHookConsole Description: Adds a hook with OS_PutString and configures output to also be sent to the log Arguments: None Returns: None *---------------------------------------------------------------------------*/ void DEMOHookConsole(void) { #ifndef SDK_FINALROM DEMOBitmapContext *context = &bitmap; // Dummy output to fix the output target to an unchanging value OS_FPutString(0, ""); // Switch the output function context->HookedConsole = OS_FPutString; OS_FPutString = LogConsole; #endif // SDK_FINALROM } /*---------------------------------------------------------------------------* Name: DEMOSetViewPort Description: Configures the viewport and projection Arguments: x: X-coordinate at the upper left y: Y-coordinate at the upper left wx: Width of the viewport in the x-direction wy: Width of the viewport in the y-direction Returns: None *---------------------------------------------------------------------------*/ void DEMOSetViewPort(int x, int y, int wx, int wy) { G3_ViewPort(x, (GX_LCD_SIZE_Y - (y + wy)), (x + wx) - 1, (GX_LCD_SIZE_Y - y) - 1); { fx32 right = FX32_ONE; fx32 top = FX32_ONE * wy / wx; fx32 near = FX32_ONE; fx32 far = FX32_ONE * 400; G3_Perspective(FX32_SIN30, FX32_COS30, FX32_ONE * wx / wy, near, far, NULL); G3_StoreMtx(0); } }