1 /*---------------------------------------------------------------------------*
2   Project:  TwlSDK - demos - CTRDG - backup-1
3   File:     draw.c
4 
5   Copyright 2008 Nintendo. All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law. They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Date:: $
14   $Rev: $
15   $Author: $
16  *---------------------------------------------------------------------------*/
17 
18 
19 #include "draw.h"
20 
21 
22 
23 /********************************************************************/
24 /* Constants */
25 
26 #define	DEMO_DRAW_INIT		(1 << 0)
27 #define	DEMO_DRAW_ENABLE	(1 << 1)
28 #define	DEMO_DRAW_FLIPPING	(1 << 2)
29 
30 
31 /********************************************************************/
32 /* Variables */
33 
34 Point   cur_pt;                        /* Current position */
35 RGB555  col_text;                      /* Current text color */
36 RGB555  col_gnd;                       /* Current ground color */
37 RGB555  col_clear;
38 
39 
40 u16     tmp_surface[GX_LCD_SIZE_X * GX_LCD_SIZE_Y] ATTRIBUTE_ALIGN(32);
41 
42 typedef struct DrawiCommon
43 {
44     Rect    clip;
45     RGB555 *next_surface;              /* VRAM A/B/C/D */
46     u32     draw_flag;
47 
48     OSThreadQueue flip_wait_q[1];      /* Flip wait */
49 #if defined(OS_SIZEOF_OSTHREADQUEUE) && (OS_SIZEOF_OSTHREADQUEUE == 16)
50     u8      padding[2];
51 #endif
52 }
53 DrawiCommon;
54 
55 static DrawiCommon drawi_common;
56 
57 
58 /********************************************************************/
59 /* Functions */
60 
61 
GetSurface(int x,int y)62 static inline RGB555 *GetSurface(int x, int y)
63 {
64     return drawi_common.next_surface + x + y * GX_LCD_SIZE_X;
65 }
66 
IsBoundX(DrawiCommon * p,int x)67 static inline BOOL IsBoundX(DrawiCommon * p, int x)
68 {
69     return (x >= p->clip.o.x) && (x < p->clip.t.x);
70 }
IsBoundY(DrawiCommon * p,int y)71 static inline BOOL IsBoundY(DrawiCommon * p, int y)
72 {
73     return (y >= p->clip.o.y) && (y < p->clip.t.y);
74 }
75 
NormalizeRegion(Region * p)76 static BOOL NormalizeRegion(Region * p)
77 {
78     DrawiCommon *const pc = &drawi_common;
79     if (p->pos.x >= pc->clip.t.x)
80         return FALSE;
81     else
82     {
83         if (p->pos.x - pc->clip.o.x < 0)
84             p->wid.x += p->pos.x - pc->clip.o.x, p->pos.x = pc->clip.o.x;
85         if (p->wid.x <= 0)
86             return FALSE;
87         else if (p->wid.x > pc->clip.t.x - p->pos.x)
88             p->wid.x = pc->clip.t.x - p->pos.x;
89     }
90     if (p->pos.y >= pc->clip.t.y)
91         return FALSE;
92     else
93     {
94         if (p->pos.y - pc->clip.o.y < 0)
95             p->wid.y += p->pos.y - pc->clip.o.y, p->pos.y = pc->clip.o.y;
96         if (p->wid.y <= 0)
97             return FALSE;
98         else if (p->wid.y > pc->clip.t.y - p->pos.y)
99             p->wid.y = pc->clip.t.y - p->pos.y;
100     }
101     return TRUE;
102 }
103 
OnVBlank(void)104 static void OnVBlank(void)
105 {
106     DrawiCommon *const p = &drawi_common;
107     if ((p->draw_flag & DEMO_DRAW_FLIPPING) != 0)
108     {
109         RGB555 *const dst_vram = (RGB555 *) HW_LCDC_VRAM_C;
110         /* Transfer the surface that was being drawn up until just now */
111         MI_CpuCopyFast(tmp_surface, dst_vram, sizeof(tmp_surface));
112         /* Clear if an initial color has been set */
113         if (col_clear != RGB555_CLEAR)
114         {
115             MI_CpuFillFast(tmp_surface,
116                            (u32)((col_clear << 0) | (col_clear << 16)), sizeof(tmp_surface));
117         }
118         p->draw_flag &= ~DEMO_DRAW_FLIPPING;
119         p->draw_flag |= DEMO_DRAW_ENABLE;
120         OS_WakeupThread(p->flip_wait_q);
121     }
122     OS_SetIrqCheckFlag(OS_IE_V_BLANK);
123 }
124 
125 
InitDraw(void)126 void InitDraw(void)
127 {
128     DrawiCommon *const p = &drawi_common;
129     BOOL    bak_irq = OS_DisableIrq();
130     if (!p->draw_flag)
131     {
132         p->draw_flag |= DEMO_DRAW_INIT;
133         GX_Init();
134         GX_SetPower(GX_POWER_ALL);
135         OS_SetIrqFunction(OS_IE_V_BLANK, OnVBlank);
136         (void)OS_EnableIrqMask(OS_IE_V_BLANK);
137 
138 #if	!defined(SDK_NO_THREAD)
139         OS_InitThreadQueue(p->flip_wait_q);
140 #endif
141 
142         /* Configuration of shared render variables */
143         col_clear = RGB555_CLEAR;
144         cur_pt.x = 0, cur_pt.y = 0;
145         col_text = RGB555_WHITE;
146         col_gnd = RGB555_CLEAR;
147         p->draw_flag |= DEMO_DRAW_ENABLE;
148         p->clip.o.x = 0;
149         p->clip.o.y = 0;
150         p->clip.t.x = GX_LCD_SIZE_X;
151         p->clip.t.y = GX_LCD_SIZE_Y;
152 
153         /* VRAM-C direct display for dump displays and so on in half-width characters */
154         GX_SetGraphicsMode(GX_DISPMODE_VRAM_C, (GXBGMode)0, (GXBG0As)0);
155         GX_SetBankForLCDC(GX_VRAM_LCDC_C);
156 
157         p->next_surface = (RGB555 *) tmp_surface;
158         MI_CpuFillFast(tmp_surface,
159                        (u32)((col_clear << 0) | (col_clear << 16)), sizeof(tmp_surface));
160 
161         (void)GX_VBlankIntr(TRUE);
162         GX_SetDispSelect(GX_DISP_SELECT_SUB_MAIN);
163 
164     }
165     (void)OS_RestoreIrq(bak_irq);
166 }
167 
DrawBegin(void)168 void DrawBegin(void)
169 {
170     DrawiCommon *const p = &drawi_common;
171     OSIntrMode bak_psr = OS_DisableInterrupts();
172     while (!(p->draw_flag & DEMO_DRAW_ENABLE))
173         OS_SleepThread(p->flip_wait_q);
174     (void)OS_RestoreInterrupts(bak_psr);
175 }
176 
DrawEnd(void)177 void DrawEnd(void)
178 {
179     DrawiCommon *const p = &drawi_common;
180     OSIntrMode bak_psr;
181 
182     DC_FlushRange(tmp_surface, sizeof(tmp_surface));
183     DC_WaitWriteBufferEmpty();
184 
185     bak_psr = OS_DisableInterrupts();
186     p->draw_flag &= ~DEMO_DRAW_ENABLE;
187     p->draw_flag |= DEMO_DRAW_FLIPPING;
188     (void)OS_RestoreInterrupts(bak_psr);
189 }
190 
ClipWindow(int ox,int oy,int tx,int ty,Rect * p_bak)191 void ClipWindow(int ox, int oy, int tx, int ty, Rect * p_bak)
192 {
193     DrawiCommon *const p = &drawi_common;
194     if (p_bak)
195     {
196         p_bak->o.x = p->clip.o.x;
197         p_bak->o.y = p->clip.o.y;
198         p_bak->t.x = p->clip.t.x;
199         p_bak->t.y = p->clip.t.y;
200     }
201     if (ox < 0)
202         ox = 0;
203     if (tx > GX_LCD_SIZE_X)
204         tx = GX_LCD_SIZE_X;
205     if (oy < 0)
206         oy = 0;
207     if (ty > GX_LCD_SIZE_Y)
208         ty = GX_LCD_SIZE_Y;
209     p->clip.o.x = ox;
210     p->clip.o.y = oy;
211     p->clip.t.x = tx;
212     p->clip.t.y = ty;
213 }
214 
RestoreClipWindow(const Rect * p_bak)215 void RestoreClipWindow(const Rect * p_bak)
216 {
217     DrawiCommon *const p = &drawi_common;
218     if (p_bak)
219     {
220         p->clip.o.x = p_bak->o.x;
221         p->clip.o.y = p_bak->o.y;
222         p->clip.t.x = p_bak->t.x;
223         p->clip.t.y = p_bak->t.y;
224     }
225 }
226 
ClearFrame(RGB555 col)227 void ClearFrame(RGB555 col)
228 {
229     DrawiCommon *const p = &drawi_common;
230     MI_CpuFillFast(p->next_surface,
231                    (u32)((col << 0) | (col << 16)), GX_LCD_SIZE_X * GX_LCD_SIZE_Y * sizeof(RGB555));
232 }
233 
FillRect(int x,int y,int wx,int wy,RGB555 col)234 void FillRect(int x, int y, int wx, int wy, RGB555 col)
235 {
236     Region  r;
237     r.pos.x = x, r.pos.y = y;
238     r.wid.x = wx, r.wid.y = wy;
239     if (NormalizeRegion(&r))
240     {
241         RGB555 *p = GetSurface(r.pos.x, r.pos.y);
242         while (--r.wid.y >= 0)
243         {
244             MI_CpuFill16(p, col, r.wid.x * sizeof(RGB555));
245             p += GX_LCD_SIZE_X;
246         }
247     }
248 }
249 
BlitRect(int x,int y,int wx,int wy,RGB555 * src,int stroke)250 void BlitRect(int x, int y, int wx, int wy, RGB555 * src, int stroke)
251 {
252     Region  r;
253     r.pos.x = x, r.pos.y = y;
254     r.wid.x = wx, r.wid.y = wy;
255     if (NormalizeRegion(&r))
256     {
257         RGB555 *p = GetSurface(r.pos.x, r.pos.y);
258         while (--r.wid.y >= 0)
259         {
260             MI_CpuCopy16(src, p, r.wid.x * sizeof(RGB555));
261             src += stroke;
262             p += GX_LCD_SIZE_X;
263         }
264     }
265 }
266 
TransRect(int x,int y,int wx,int wy,RGB555 * src,int stroke)267 void TransRect(int x, int y, int wx, int wy, RGB555 * src, int stroke)
268 {
269     Region  r;
270     r.pos.x = x, r.pos.y = y;
271     r.wid.x = wx, r.wid.y = wy;
272     if (NormalizeRegion(&r))
273     {
274         RGB555 *p = GetSurface(r.pos.x, r.pos.y);
275         RGB555  trans = col_gnd;
276         while (--r.wid.y >= 0)
277         {
278             int     i;
279             for (i = 0; i < r.wid.x; ++i)
280             {
281                 if (p[i] != trans)
282                     p[i] = src[i];
283             }
284             src += stroke;
285             p += GX_LCD_SIZE_X;
286         }
287     }
288 }
289 
DrawLine(int sx,int sy,int tx,int ty,RGB555 col)290 void DrawLine(int sx, int sy, int tx, int ty, RGB555 col)
291 {
292     DrawiCommon *const pc = &drawi_common;
293 
294     RGB555 *p;
295     int     tmp;
296     int     wx, wy;
297     if (sx > tx)
298     {
299         tmp = sx + 1, sx = tx + 1, tx = tmp;
300         tmp = sy, sy = ty, ty = tmp;
301     }
302     wx = tx - sx, wy = ty - sy;
303 
304     if (!wx)
305     {
306         if (wy < 0)
307         {
308             wy = -wy;
309             tmp = sy + 1, sy = ty + 1, ty = tmp;
310         }
311         if (!IsBoundX(pc, sx) || (sy >= pc->clip.t.y))
312             return;
313         else
314         {
315             if (sy < pc->clip.o.y)
316                 wy += sy, sy = pc->clip.o.y;
317             if (wy > pc->clip.t.y - sy)
318                 wy = pc->clip.t.y - sy;
319             p = GetSurface(sx, sy);
320             while (--wy >= 0)
321             {
322                 *p = col;
323                 p += GX_LCD_SIZE_X;
324             }
325         }
326     }
327     else if (!wy)
328     {
329         if (!IsBoundY(pc, sy) || (sx >= pc->clip.t.x))
330             return;
331         else
332         {
333             if (sx < pc->clip.o.x)
334                 wx += sx, sx = pc->clip.o.x;
335             if (wx > pc->clip.t.x - sx)
336                 wx = pc->clip.t.x - sx;
337             p = GetSurface(sx, sy);
338             MI_CpuFill16(p, col, wx * sizeof(RGB555));
339         }
340     }
341     else
342     {
343         int     n, dx, dy;
344         int     y_delta = +1;
345         int     y_ofs = GX_LCD_SIZE_X;
346         if (wy < 0)
347         {
348             wy = -wy;
349             y_delta = -y_delta;
350             y_ofs = -y_ofs;
351         }
352         p = GetSurface(sx, sy);
353         dx = wy - 1, dy = wx - 1;
354         --sx, sy -= y_delta;
355         for (n = wx * wy; --n >= 0;)
356         {
357             BOOL    moved = FALSE;
358             if (++dx >= wy)
359                 moved = TRUE, dx = 0, ++sx, p += 1;
360             if (++dy >= wx)
361                 moved = TRUE, dy = 0, sy += y_delta, p += y_ofs;
362             if (moved && IsBoundX(pc, sx) && IsBoundY(pc, sy))
363                 *p = col;
364         }
365     }
366 }
367 
DrawCircle(int ox,int oy,int r)368 void DrawCircle(int ox, int oy, int r)
369 {
370     DrawiCommon *const pc = &drawi_common;
371 
372     int     n, dx, dy, vx, vy;
373     RGB555  col = col_text;
374     vx = 65536 * r, vy = 0;
375     dx = ox * 65536, dy = (oy - r) * 65536;
376 
377     for (n = (int)(2 * 3.14 * 256); n > 0; --n)
378     {
379         int     ax, ay, x, y;
380         ax = -vy / 256, ay = +vx / 256;
381         vx += ax, vy += ay;
382         dx += ay, dy -= ax;
383         x = dx / 65536, y = dy / 65536;
384         if (IsBoundX(pc, x) && IsBoundY(pc, y))
385             *GetSurface(x, y) = col;
386     }
387 }
388 
DrawText(int x,int y,const char * s,...)389 void DrawText(int x, int y, const char *s, ...)
390 {
391     va_list vlist;
392     static char temp[512];
393 
394     va_start(vlist, s);
395     (void)OS_VSNPrintf(temp, sizeof(temp) - 2, s, vlist);
396     va_end(vlist);
397     DrawTextLen(x, y, temp, sizeof(temp) - 1);
398 }
399 
DrawTextLen(int x,int y,const char * s,int n)400 void DrawTextLen(int x, int y, const char *s, int n)
401 {
402     DrawiCommon *const pc = &drawi_common;
403 
404     RGB555 *p = GetSurface(x, y);
405     RGB555  txt = col_text, gnd = col_gnd;
406     int     px = 0;
407     int     rep = 0;
408 
409     for (; n > 0; ++s, --n)
410     {
411 
412         int     c = MI_ReadByte(s);
413 
414         switch (c)
415         {
416 
417         case '\0':
418             return;
419 
420         case '\r':
421             c = MI_ReadByte(s + 1);
422             if (c == '\n')
423                 ++s, --n;
424         case '\n':
425             y += 8;
426             p += GX_LCD_SIZE_X * 8;
427             px = 0;
428             break;
429 
430         case '\t':
431             {
432                 const int align = 4;
433                 rep = align - ((px / 8) & (align - 1));
434                 c = ' ';
435             }
436             goto put_char;
437 
438         default:
439             rep = 1;
440             goto put_char;
441           put_char:
442             while (--rep >= 0)
443             {
444                 int     tx = x + px;
445                 if ((tx > pc->clip.o.x - 8) && (y > pc->clip.o.y - 8) &&
446                     (tx < pc->clip.t.x) && (y < pc->clip.t.y))
447                 {
448                     const u32 *pf = sampleCharData + c * 8;
449                     RGB555 *dst = p + px;
450                     int     base = 0;
451                     int     wx = 8, wy = 8;
452                     if (pc->clip.o.x - tx > 0)
453                         base = pc->clip.o.x - tx;
454                     else if (wx > pc->clip.t.x - tx)
455                         wx = pc->clip.t.x - tx;
456                     if (pc->clip.o.y - y > 0)
457                     {
458                         wy -= pc->clip.o.y - y;
459                         pf += pc->clip.o.y - y;
460                         dst += GX_LCD_SIZE_X * (pc->clip.o.y - y);
461                     }
462                     else if (wy > pc->clip.t.y - y)
463                         wy = pc->clip.t.y - y;
464                     for (; --wy >= 0; ++pf, dst += GX_LCD_SIZE_X)
465                     {
466                         int     i;
467                         const u32 d = *pf;
468                         for (i = base; i < wx; ++i)
469                         {
470                             if ((d & (0xF << (i * 4))) != 0)
471                                 dst[i] = txt;
472                             else if (gnd != RGB555_CLEAR)
473                                 dst[i] = gnd;
474                         }
475                     }
476                 }
477                 px += 8;
478             }
479             break;
480         }
481     }
482 }
483