/*---------------------------------------------------------------------------* Project: TwlSDK - demos - CTRDG - backup-1 File: draw.c Copyright 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:: $ $Rev: $ $Author: $ *---------------------------------------------------------------------------*/ #include "draw.h" /********************************************************************/ /* Constants */ #define DEMO_DRAW_INIT (1 << 0) #define DEMO_DRAW_ENABLE (1 << 1) #define DEMO_DRAW_FLIPPING (1 << 2) /********************************************************************/ /* Variables */ Point cur_pt; /* Current position */ RGB555 col_text; /* Current text color */ RGB555 col_gnd; /* Current ground color */ RGB555 col_clear; u16 tmp_surface[GX_LCD_SIZE_X * GX_LCD_SIZE_Y] ATTRIBUTE_ALIGN(32); typedef struct DrawiCommon { Rect clip; RGB555 *next_surface; /* VRAM A/B/C/D */ u32 draw_flag; OSThreadQueue flip_wait_q[1]; /* Flip wait */ #if defined(OS_SIZEOF_OSTHREADQUEUE) && (OS_SIZEOF_OSTHREADQUEUE == 16) u8 padding[2]; #endif } DrawiCommon; static DrawiCommon drawi_common; /********************************************************************/ /* Functions */ static inline RGB555 *GetSurface(int x, int y) { return drawi_common.next_surface + x + y * GX_LCD_SIZE_X; } static inline BOOL IsBoundX(DrawiCommon * p, int x) { return (x >= p->clip.o.x) && (x < p->clip.t.x); } static inline BOOL IsBoundY(DrawiCommon * p, int y) { return (y >= p->clip.o.y) && (y < p->clip.t.y); } static BOOL NormalizeRegion(Region * p) { DrawiCommon *const pc = &drawi_common; if (p->pos.x >= pc->clip.t.x) return FALSE; else { if (p->pos.x - pc->clip.o.x < 0) p->wid.x += p->pos.x - pc->clip.o.x, p->pos.x = pc->clip.o.x; if (p->wid.x <= 0) return FALSE; else if (p->wid.x > pc->clip.t.x - p->pos.x) p->wid.x = pc->clip.t.x - p->pos.x; } if (p->pos.y >= pc->clip.t.y) return FALSE; else { if (p->pos.y - pc->clip.o.y < 0) p->wid.y += p->pos.y - pc->clip.o.y, p->pos.y = pc->clip.o.y; if (p->wid.y <= 0) return FALSE; else if (p->wid.y > pc->clip.t.y - p->pos.y) p->wid.y = pc->clip.t.y - p->pos.y; } return TRUE; } static void OnVBlank(void) { DrawiCommon *const p = &drawi_common; if ((p->draw_flag & DEMO_DRAW_FLIPPING) != 0) { RGB555 *const dst_vram = (RGB555 *) HW_LCDC_VRAM_C; /* Transfer the surface that was being drawn up until just now */ MI_CpuCopyFast(tmp_surface, dst_vram, sizeof(tmp_surface)); /* Clear if an initial color has been set */ if (col_clear != RGB555_CLEAR) { MI_CpuFillFast(tmp_surface, (u32)((col_clear << 0) | (col_clear << 16)), sizeof(tmp_surface)); } p->draw_flag &= ~DEMO_DRAW_FLIPPING; p->draw_flag |= DEMO_DRAW_ENABLE; OS_WakeupThread(p->flip_wait_q); } OS_SetIrqCheckFlag(OS_IE_V_BLANK); } void InitDraw(void) { DrawiCommon *const p = &drawi_common; BOOL bak_irq = OS_DisableIrq(); if (!p->draw_flag) { p->draw_flag |= DEMO_DRAW_INIT; GX_Init(); GX_SetPower(GX_POWER_ALL); OS_SetIrqFunction(OS_IE_V_BLANK, OnVBlank); (void)OS_EnableIrqMask(OS_IE_V_BLANK); #if !defined(SDK_NO_THREAD) OS_InitThreadQueue(p->flip_wait_q); #endif /* Configuration of shared render variables */ col_clear = RGB555_CLEAR; cur_pt.x = 0, cur_pt.y = 0; col_text = RGB555_WHITE; col_gnd = RGB555_CLEAR; p->draw_flag |= DEMO_DRAW_ENABLE; p->clip.o.x = 0; p->clip.o.y = 0; p->clip.t.x = GX_LCD_SIZE_X; p->clip.t.y = GX_LCD_SIZE_Y; /* VRAM-C direct display for dump displays and so on in half-width characters */ GX_SetGraphicsMode(GX_DISPMODE_VRAM_C, (GXBGMode)0, (GXBG0As)0); GX_SetBankForLCDC(GX_VRAM_LCDC_C); p->next_surface = (RGB555 *) tmp_surface; MI_CpuFillFast(tmp_surface, (u32)((col_clear << 0) | (col_clear << 16)), sizeof(tmp_surface)); (void)GX_VBlankIntr(TRUE); GX_SetDispSelect(GX_DISP_SELECT_SUB_MAIN); } (void)OS_RestoreIrq(bak_irq); } void DrawBegin(void) { DrawiCommon *const p = &drawi_common; OSIntrMode bak_psr = OS_DisableInterrupts(); while (!(p->draw_flag & DEMO_DRAW_ENABLE)) OS_SleepThread(p->flip_wait_q); (void)OS_RestoreInterrupts(bak_psr); } void DrawEnd(void) { DrawiCommon *const p = &drawi_common; OSIntrMode bak_psr; DC_FlushRange(tmp_surface, sizeof(tmp_surface)); DC_WaitWriteBufferEmpty(); bak_psr = OS_DisableInterrupts(); p->draw_flag &= ~DEMO_DRAW_ENABLE; p->draw_flag |= DEMO_DRAW_FLIPPING; (void)OS_RestoreInterrupts(bak_psr); } void ClipWindow(int ox, int oy, int tx, int ty, Rect * p_bak) { DrawiCommon *const p = &drawi_common; if (p_bak) { p_bak->o.x = p->clip.o.x; p_bak->o.y = p->clip.o.y; p_bak->t.x = p->clip.t.x; p_bak->t.y = p->clip.t.y; } if (ox < 0) ox = 0; if (tx > GX_LCD_SIZE_X) tx = GX_LCD_SIZE_X; if (oy < 0) oy = 0; if (ty > GX_LCD_SIZE_Y) ty = GX_LCD_SIZE_Y; p->clip.o.x = ox; p->clip.o.y = oy; p->clip.t.x = tx; p->clip.t.y = ty; } void RestoreClipWindow(const Rect * p_bak) { DrawiCommon *const p = &drawi_common; if (p_bak) { p->clip.o.x = p_bak->o.x; p->clip.o.y = p_bak->o.y; p->clip.t.x = p_bak->t.x; p->clip.t.y = p_bak->t.y; } } void ClearFrame(RGB555 col) { DrawiCommon *const p = &drawi_common; MI_CpuFillFast(p->next_surface, (u32)((col << 0) | (col << 16)), GX_LCD_SIZE_X * GX_LCD_SIZE_Y * sizeof(RGB555)); } void FillRect(int x, int y, int wx, int wy, RGB555 col) { Region r; r.pos.x = x, r.pos.y = y; r.wid.x = wx, r.wid.y = wy; if (NormalizeRegion(&r)) { RGB555 *p = GetSurface(r.pos.x, r.pos.y); while (--r.wid.y >= 0) { MI_CpuFill16(p, col, r.wid.x * sizeof(RGB555)); p += GX_LCD_SIZE_X; } } } void BlitRect(int x, int y, int wx, int wy, RGB555 * src, int stroke) { Region r; r.pos.x = x, r.pos.y = y; r.wid.x = wx, r.wid.y = wy; if (NormalizeRegion(&r)) { RGB555 *p = GetSurface(r.pos.x, r.pos.y); while (--r.wid.y >= 0) { MI_CpuCopy16(src, p, r.wid.x * sizeof(RGB555)); src += stroke; p += GX_LCD_SIZE_X; } } } void TransRect(int x, int y, int wx, int wy, RGB555 * src, int stroke) { Region r; r.pos.x = x, r.pos.y = y; r.wid.x = wx, r.wid.y = wy; if (NormalizeRegion(&r)) { RGB555 *p = GetSurface(r.pos.x, r.pos.y); RGB555 trans = col_gnd; while (--r.wid.y >= 0) { int i; for (i = 0; i < r.wid.x; ++i) { if (p[i] != trans) p[i] = src[i]; } src += stroke; p += GX_LCD_SIZE_X; } } } void DrawLine(int sx, int sy, int tx, int ty, RGB555 col) { DrawiCommon *const pc = &drawi_common; RGB555 *p; int tmp; int wx, wy; if (sx > tx) { tmp = sx + 1, sx = tx + 1, tx = tmp; tmp = sy, sy = ty, ty = tmp; } wx = tx - sx, wy = ty - sy; if (!wx) { if (wy < 0) { wy = -wy; tmp = sy + 1, sy = ty + 1, ty = tmp; } if (!IsBoundX(pc, sx) || (sy >= pc->clip.t.y)) return; else { if (sy < pc->clip.o.y) wy += sy, sy = pc->clip.o.y; if (wy > pc->clip.t.y - sy) wy = pc->clip.t.y - sy; p = GetSurface(sx, sy); while (--wy >= 0) { *p = col; p += GX_LCD_SIZE_X; } } } else if (!wy) { if (!IsBoundY(pc, sy) || (sx >= pc->clip.t.x)) return; else { if (sx < pc->clip.o.x) wx += sx, sx = pc->clip.o.x; if (wx > pc->clip.t.x - sx) wx = pc->clip.t.x - sx; p = GetSurface(sx, sy); MI_CpuFill16(p, col, wx * sizeof(RGB555)); } } else { int n, dx, dy; int y_delta = +1; int y_ofs = GX_LCD_SIZE_X; if (wy < 0) { wy = -wy; y_delta = -y_delta; y_ofs = -y_ofs; } p = GetSurface(sx, sy); 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, p += 1; if (++dy >= wx) moved = TRUE, dy = 0, sy += y_delta, p += y_ofs; if (moved && IsBoundX(pc, sx) && IsBoundY(pc, sy)) *p = col; } } } void DrawCircle(int ox, int oy, int r) { DrawiCommon *const pc = &drawi_common; int n, dx, dy, vx, vy; RGB555 col = col_text; vx = 65536 * r, vy = 0; dx = ox * 65536, dy = (oy - r) * 65536; for (n = (int)(2 * 3.14 * 256); n > 0; --n) { int ax, ay, x, y; ax = -vy / 256, ay = +vx / 256; vx += ax, vy += ay; dx += ay, dy -= ax; x = dx / 65536, y = dy / 65536; if (IsBoundX(pc, x) && IsBoundY(pc, y)) *GetSurface(x, y) = col; } } void DrawText(int x, int y, const char *s, ...) { va_list vlist; static char temp[512]; va_start(vlist, s); (void)OS_VSNPrintf(temp, sizeof(temp) - 2, s, vlist); va_end(vlist); DrawTextLen(x, y, temp, sizeof(temp) - 1); } void DrawTextLen(int x, int y, const char *s, int n) { DrawiCommon *const pc = &drawi_common; RGB555 *p = GetSurface(x, y); RGB555 txt = col_text, gnd = col_gnd; int px = 0; int rep = 0; for (; n > 0; ++s, --n) { int c = MI_ReadByte(s); switch (c) { case '\0': return; case '\r': c = MI_ReadByte(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 > pc->clip.o.x - 8) && (y > pc->clip.o.y - 8) && (tx < pc->clip.t.x) && (y < pc->clip.t.y)) { const u32 *pf = sampleCharData + c * 8; RGB555 *dst = p + px; int base = 0; int wx = 8, wy = 8; if (pc->clip.o.x - tx > 0) base = pc->clip.o.x - tx; else if (wx > pc->clip.t.x - tx) wx = pc->clip.t.x - tx; if (pc->clip.o.y - y > 0) { wy -= pc->clip.o.y - y; pf += pc->clip.o.y - y; dst += GX_LCD_SIZE_X * (pc->clip.o.y - y); } else if (wy > pc->clip.t.y - y) wy = pc->clip.t.y - y; for (; --wy >= 0; ++pf, dst += GX_LCD_SIZE_X) { int i; const u32 d = *pf; for (i = base; i < wx; ++i) { if ((d & (0xF << (i * 4))) != 0) dst[i] = txt; else if (gnd != RGB555_CLEAR) dst[i] = gnd; } } } px += 8; } break; } } }