Merge branch 'master' of git://git.ucc.asn.au/tpg/acess2
[tpg/acess2.git] / Usermode / Applications / axwin3_src / WM / wm_render_text.c
1 /*
2  * Acess2 GUI (AxWin) Version 3
3  * - By John Hodge (thePowersGang)
4  *
5  * wm_render_text.c
6  * - WM Text Rendering
7  */
8 #include <common.h>
9 #include <wm_internals.h>
10 #include <stdlib.h>
11 #include <unicode.h>
12 #include <limits.h>     // INT_MAX
13
14 // === TYPES ===
15 typedef struct sGlyph   tGlyph;
16
17 struct sGlyph
18 {
19         struct sGlyph   *Next;
20         struct sGlyph   *Prev;
21         
22         uint32_t        Codepoint;
23         
24         // Effective dimensions (distance to move 'cursor')
25         short   Width;
26         short   Height;
27         
28         // Distance from the current cursor position to render at
29         short   OffsetX;
30         short   OffsetY;
31         
32         // True dimensions (size of the bitmap
33         short   TrueWidth;
34         short   TrueHeight;
35         
36         // Bitmap Data
37         uint8_t Bitmap[];       // 8-bit alpha  
38 };
39
40 struct sFont
41 {
42         struct sFont    *Next;
43          int    ReferenceCount;
44         
45         tGlyph  *AsciiGlyphs[128];      // Glyphs 0-127
46         
47         tGlyph  *FirstGlyph;
48         tGlyph  *LastGlyph;
49         
50         tGlyph  *(*CacheGlyph)(struct sFont *this, uint32_t Codepoint);
51         
52 };
53
54
55 // === PROTOTYPES ===
56  int    WM_Render_DrawText(tWindow *Window, int X, int Y, int W, int H, tFont *Font, tColour Color, const char *Text, int MaxLen);
57 void    WM_Render_GetTextDims(tFont *Font, const char *Text, int MaxLen, int *W, int *H);
58 tGlyph  *_GetGlyph(tFont *Font, uint32_t Codepoint);
59 void    _RenderGlyph(tWindow *Window, short X, short Y, short ClipW, short ClipH, tGlyph *Glyph, uint32_t Color);
60 tGlyph  *_SystemFont_CacheGlyph(tFont *Font, uint32_t Codepoint);
61
62 // === GLOBALS ===
63 tFont   gSystemFont = {
64         .CacheGlyph = _SystemFont_CacheGlyph
65 };
66
67 // === CODE ===
68 /**
69  * \brief Draw text to the screen
70  */
71 int WM_Render_DrawText(tWindow *Window, int X, int Y, int W, int H, tFont *Font, tColour Colour, const char *Text, int MaxLen)
72 {
73          int    xOfs = 0;
74         tGlyph  *glyph;
75         uint32_t        ch = 0;
76
77 //      _SysDebug("WM_Render_DrawText: (X=%i,Y=%i,W=%i,H=%i,Font=%p,", X, Y, W, H, Font);
78 //      _SysDebug("  Colour=%08x,Text='%s')", Colour, Text);
79         
80         if(!Text)       return 0;
81
82         if(MaxLen < 0)  MaxLen = INT_MAX;
83
84         X += Window->BorderL;
85         Y += Window->BorderT;
86
87         // Check the bounds
88         if(W < 0 || X < 0 || X >= Window->RealW)        return 0;
89         if(X + W > Window->RealW)       W = Window->RealW - X;
90         
91         if(H < 0 || Y < 0 || Y >= Window->RealH)        return 0;
92         if(Y + H > Window->RealH)       H = Window->RealH - Y;
93         
94         // TODO: Catch trampling of decorations
95
96         // Handle NULL font (system default monospace)
97         if( !Font )     Font = &gSystemFont;
98         
99         while( MaxLen > 0 && *Text )
100         {
101                  int    len;
102                 // Read character
103                 len = ReadUTF8(Text, &ch);
104                 Text += len;
105                 MaxLen -= len;
106                 
107                 // Find (or load) the glyph
108                 glyph = _GetGlyph(Font, ch);
109                 if( !glyph )    continue ;      // If not found, just don't render it
110                 
111                 // End render if it will not fit at all
112                 if( xOfs >= W )
113                         break;
114                 
115                 _RenderGlyph(Window, X + xOfs, Y, W-xOfs, H, glyph, Colour);
116                 xOfs += glyph->Width;
117         }
118         
119         return xOfs;
120 }
121
122 void WM_Render_GetTextDims(tFont *Font, const char *Text, int MaxLen, int *W, int *H)
123 {
124          int    w=0, h=0;
125         uint32_t        ch;
126         tGlyph  *glyph;
127         if( !Font )     Font = &gSystemFont;
128         
129         if(MaxLen < 0)  MaxLen = INT_MAX;
130
131         while( MaxLen > 0 && *Text )
132         {
133                  int    len;
134                 len = ReadUTF8(Text, &ch);
135                 Text += len;
136                 MaxLen -= len;
137                 glyph = _GetGlyph(Font, ch);
138                 if( !glyph )    continue;
139                 
140                 w += glyph->Width;
141                 if( h < glyph->Height ) h = glyph->Height;
142         }
143         
144         if(W)   *W = w;
145         if(H)   *H = h;
146 }
147
148 tGlyph *_GetGlyph(tFont *Font, uint32_t Codepoint)
149 {
150         tGlyph  *next = NULL, *prev = NULL;
151         tGlyph  *new;
152         
153         // Check for ASCII
154         if( Codepoint < 128 )
155         {
156                 if( Font->AsciiGlyphs[Codepoint] == NULL ) {
157                         Font->AsciiGlyphs[Codepoint] = Font->CacheGlyph(Font, Codepoint);
158                 }
159                 
160                 return Font->AsciiGlyphs[Codepoint];
161         }
162         
163         // If within the range
164         if( Font->FirstGlyph && Font->FirstGlyph->Codepoint < Codepoint && Codepoint < Font->LastGlyph->Codepoint )
165         {
166                 // Find what end is "closest"
167                 if( Codepoint - Font->FirstGlyph->Codepoint < Font->LastGlyph->Codepoint - Codepoint )
168                 {
169                         // Start from the bottom
170                         for( next = Font->FirstGlyph;
171                                  next && next->Codepoint < Codepoint;
172                                  prev = next, next = next->Next
173                                  );
174                         
175                         if( next && next->Codepoint == Codepoint )
176                                 return next;
177                         
178                 }
179                 else
180                 {
181                         // Start at the top
182                         // NOTE: The roles of next and prev are reversed here to allow 
183                         //       the insert to be able to assume that `prev` is the
184                         //       previous entry, and `next` is the next.
185                         for( prev = Font->LastGlyph;
186                                  prev && prev->Codepoint > Codepoint;
187                                  next = prev, prev = prev->Prev
188                                  );
189                         if( prev && prev->Codepoint == Codepoint )
190                                 return prev;
191                 }
192         }
193         else
194         {
195                 // If below first
196                 if( !Font->FirstGlyph ||  Font->FirstGlyph->Codepoint > Codepoint ) {
197                         prev = NULL;
198                         next = Font->FirstGlyph;
199                 }
200                 // Above last
201                 else {
202                         prev = Font->LastGlyph;
203                         next = NULL;
204                 }
205         }
206         
207         // Load new
208         new = Font->CacheGlyph(Font, Codepoint);
209         if( !new )      return NULL;
210         
211         // Add to list
212         // - Forward link
213         if( prev ) {
214                 new->Next = prev->Next;
215                 prev->Next = new;
216         }
217         else {
218                 new->Next = Font->FirstGlyph;
219                 Font->FirstGlyph = new;
220         }
221         
222         // - Backlink
223         if( next ) {
224                 new->Prev = next->Prev;
225                 next->Prev = new;
226         }
227         else {
228                 new->Prev = Font->LastGlyph;
229                 Font->LastGlyph = new;
230         }
231         
232         // Return
233         return new;
234 }
235
236 /**
237  */
238 void _RenderGlyph(tWindow *Window, short X, short Y, short ClipW, short ClipH, tGlyph *Glyph, uint32_t Color)
239 {
240          int    xStart = 0, yStart = 0;
241          int    x, y, dst_x;
242         uint32_t        *outBuf;
243         uint8_t *inBuf;
244
245         X += Glyph->OffsetX;
246         if( X < 0 ) {   // If -ve, skip the first -X pixels
247                 xStart = -X;
248                 X = 0;
249         }
250
251         Y += Glyph->OffsetY;
252         if( Y < 0 ) {   // If -ve, skip the first -Y lines
253                 yStart = -Y;
254                 Y = 0;
255         }
256
257 //      _SysDebug("X = %i, Y = %i", X, Y);
258         outBuf = (uint32_t*)Window->RenderBuffer + Y*Window->RealW + X;
259         inBuf = Glyph->Bitmap + yStart*Glyph->TrueWidth;
260
261         for( y = yStart; y < Glyph->TrueHeight && ClipH--; y ++ )
262         {
263                 for( x = xStart, dst_x = 0; x < Glyph->TrueWidth && dst_x < ClipW; x ++, dst_x ++ )
264                 {
265                         outBuf[dst_x] = Video_AlphaBlend( outBuf[dst_x], Color, inBuf[x] );
266                 }
267                 outBuf += Window->RealW;
268                 inBuf += Glyph->TrueWidth;
269         }
270 }
271
272 // Load system font (8x16 monospace)
273 #include "font_8x16.h"
274
275 /*
276  */
277 tGlyph *_SystemFont_CacheGlyph(tFont *Font, uint32_t Codepoint)
278 {
279          int    i;
280         uint8_t index = 0;
281         tGlyph  *ret;
282         uint8_t *data;
283
284 //      _SysDebug("_SystemFont_CacheGlyph: (Font=%p, Codepoint=0x%06x)", Font, Codepoint);
285         
286         if( Codepoint < 128 ) {
287                 index = Codepoint;
288         }
289         else {
290                 index = '?';    // Unknown glyphs come out as a question mark
291         }
292
293 //      _SysDebug(" index = %i", index);
294
295         ret = malloc( sizeof(tGlyph) + FONT_WIDTH*FONT_HEIGHT );
296         if( !ret ) {
297                 _SysDebug("ERROR: malloc(%i) failed", sizeof(tGlyph) + FONT_WIDTH*FONT_HEIGHT);
298                 return NULL;
299         }
300
301         ret->Codepoint = Codepoint;
302
303         ret->Width = FONT_WIDTH;
304         ret->Height = FONT_HEIGHT;
305         
306         ret->TrueWidth = FONT_WIDTH;
307         ret->TrueHeight = FONT_HEIGHT;
308
309         ret->OffsetX = 0;
310         ret->OffsetY = 0;
311         
312         data = &VTermFont[index * FONT_HEIGHT];
313
314         for( i = 0; i < FONT_HEIGHT; i ++ )
315         {
316                 ret->Bitmap[ i * 8 + 0 ] = data[i] & (1 << 7) ? 255 : 0;
317                 ret->Bitmap[ i * 8 + 1 ] = data[i] & (1 << 6) ? 255 : 0;
318                 ret->Bitmap[ i * 8 + 2 ] = data[i] & (1 << 5) ? 255 : 0;
319                 ret->Bitmap[ i * 8 + 3 ] = data[i] & (1 << 4) ? 255 : 0;
320                 ret->Bitmap[ i * 8 + 4 ] = data[i] & (1 << 3) ? 255 : 0;
321                 ret->Bitmap[ i * 8 + 5 ] = data[i] & (1 << 2) ? 255 : 0;
322                 ret->Bitmap[ i * 8 + 6 ] = data[i] & (1 << 1) ? 255 : 0;
323                 ret->Bitmap[ i * 8 + 7 ] = data[i] & (1 << 0) ? 255 : 0;
324         }
325
326         return ret;
327 }
328

UCC git Repository :: git.ucc.asn.au