Usermode/AxWin3 - Splitting widget types out into separate files
[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
12 // === TYPES ===
13 typedef struct sGlyph   tGlyph;
14
15 struct sGlyph
16 {
17         struct sGlyph   *Next;
18         struct sGlyph   *Prev;
19         
20         uint32_t        Codepoint;
21         
22         // Effective dimensions (distance to move 'cursor')
23         short   Width;
24         short   Height;
25         
26         // Distance from the current cursor position to render at
27         short   OffsetX;
28         short   OffsetY;
29         
30         // True dimensions (size of the bitmap
31         short   TrueWidth;
32         short   TrueHeight;
33         
34         // Bitmap Data
35         uint8_t Bitmap[];       // 8-bit alpha  
36 };
37
38 struct sFont
39 {
40         struct sFont    *Next;
41          int    ReferenceCount;
42         
43         tGlyph  *AsciiGlyphs[128];      // Glyphs 0-127
44         
45         tGlyph  *FirstGlyph;
46         tGlyph  *LastGlyph;
47         
48         tGlyph  *(*CacheGlyph)(struct sFont *this, uint32_t Codepoint);
49         
50 };
51
52
53 // === PROTOTYPES ===
54  int    WM_Render_DrawText(tWindow *Window, int X, int Y, int W, int H, tFont *Font, tColour Color, const char *Text);
55 void    WM_Render_GetTextDims(tFont *Font, const char *Text, int *W, int *H);
56 tGlyph  *_GetGlyph(tFont *Font, uint32_t Codepoint);
57 void    _RenderGlyph(tWindow *Window, short X, short Y, tGlyph *Glyph, uint32_t Color);
58 tGlyph  *_SystemFont_CacheGlyph(tFont *Font, uint32_t Codepoint);
59  int    ReadUTF8(const char *Input, uint32_t *Output);
60
61 // === GLOBALS ===
62 tFont   gSystemFont = {
63         .CacheGlyph = _SystemFont_CacheGlyph
64 };
65
66 // === CODE ===
67 /**
68  * \brief Draw text to the screen
69  */
70 int WM_Render_DrawText(tWindow *Window, int X, int Y, int W, int H, tFont *Font, tColour Colour, const char *Text)
71 {
72          int    xOfs = 0;
73         tGlyph  *glyph;
74         uint32_t        ch = 0;
75
76 //      _SysDebug("WM_Render_DrawText: (X=%i,Y=%i,W=%i,H=%i,Font=%p,", X, Y, W, H, Font);
77 //      _SysDebug("  Colour=%08x,Text='%s')", Colour, Text);
78         
79         if(!Text)       return 0;
80
81
82         X += Window->BorderL;
83         Y += Window->BorderT;
84
85         // Check the bounds
86         if(W < 0 || X < 0 || X >= Window->RealW)        return 0;
87         if(X + W > Window->RealW)       W = Window->RealW - X;
88         
89         if(H < 0 || Y < 0 || Y >= Window->RealH)        return 0;
90         if(Y + H > Window->RealH)       H = Window->RealH - Y;
91         
92         // TODO: Catch trampling of decorations
93
94         // Handle NULL font (system default monospace)
95         if( !Font )     Font = &gSystemFont;
96         
97         while( *Text )
98         {
99                 // Read character
100                 Text += ReadUTF8(Text, &ch);
101                 
102                 // Find (or load) the glyph
103                 glyph = _GetGlyph(Font, ch);
104                 if( !glyph )    continue ;      // If not found, just don't render it
105                 
106                 // End render if it will overflow the provided range
107                 if( xOfs + glyph->TrueWidth > W )
108                         break;
109                 
110                 _RenderGlyph(Window, X + xOfs, Y, glyph, Colour);
111                 xOfs += glyph->Width;
112         }
113         
114         return xOfs;
115 }
116
117 void WM_Render_GetTextDims(tFont *Font, const char *Text, int *W, int *H)
118 {
119          int    w=0, h=0;
120         uint32_t        ch;
121         tGlyph  *glyph;
122         if( !Font )     Font = &gSystemFont;
123         
124         while( *Text )
125         {
126                 Text += ReadUTF8(Text, &ch);
127                 glyph = _GetGlyph(Font, ch);
128                 if( !glyph )    continue;
129                 
130                 w += glyph->Width;
131                 if( h < glyph->Height ) h = glyph->Height;
132         }
133         
134         if(W)   *W = w;
135         if(H)   *H = h;
136 }
137
138 tGlyph *_GetGlyph(tFont *Font, uint32_t Codepoint)
139 {
140         tGlyph  *next = NULL, *prev = NULL;
141         tGlyph  *new;
142         
143         // Check for ASCII
144         if( Codepoint < 128 )
145         {
146                 if( Font->AsciiGlyphs[Codepoint] == NULL ) {
147                         Font->AsciiGlyphs[Codepoint] = Font->CacheGlyph(Font, Codepoint);
148                 }
149                 
150                 return Font->AsciiGlyphs[Codepoint];
151         }
152         
153         // If within the range
154         if( Font->FirstGlyph && Font->FirstGlyph->Codepoint < Codepoint && Codepoint < Font->LastGlyph->Codepoint )
155         {
156                 // Find what end is "closest"
157                 if( Codepoint - Font->FirstGlyph->Codepoint < Font->LastGlyph->Codepoint - Codepoint )
158                 {
159                         // Start from the bottom
160                         for( next = Font->FirstGlyph;
161                                  next && next->Codepoint < Codepoint;
162                                  prev = next, next = next->Next
163                                  );
164                         
165                         if( next->Codepoint == Codepoint )
166                                 return next;
167                         
168                 }
169                 else
170                 {
171                         // Start at the top
172                         // NOTE: The roles of next and prev are reversed here to allow 
173                         //       the insert to be able to assume that `prev` is the
174                         //       previous entry, and `next` is the next.
175                         for( prev = Font->LastGlyph;
176                                  prev && prev->Codepoint > Codepoint;
177                                  next = prev, prev = prev->Prev
178                                  );
179                         if( prev->Codepoint == Codepoint )
180                                 return prev;
181                 }
182         }
183         else
184         {
185                 // If below first
186                 if( !Font->FirstGlyph ||  Font->FirstGlyph->Codepoint > Codepoint ) {
187                         prev = NULL;
188                         next = Font->FirstGlyph;
189                 }
190                 // Above last
191                 else {
192                         prev = Font->LastGlyph;
193                         next = NULL;
194                 }
195         }
196         
197         // Load new
198         new = Font->CacheGlyph(Font, Codepoint);
199         if( !new )      return NULL;
200         
201         // Add to list
202         // - Forward link
203         if( prev ) {
204                 new->Next = prev->Next;
205                 prev->Next = new;
206         }
207         else {
208                 new->Next = Font->FirstGlyph;
209                 Font->FirstGlyph = new;
210         }
211         
212         // - Backlink
213         if( next ) {
214                 new->Prev = next->Prev;
215                 next->Prev = new;
216         }
217         else {
218                 new->Prev = Font->LastGlyph;
219                 Font->LastGlyph = new;
220         }
221         
222         // Return
223         return new;
224 }
225
226 /**
227  */
228 void _RenderGlyph(tWindow *Window, short X, short Y, tGlyph *Glyph, uint32_t Color)
229 {
230          int    xStart = 0, yStart = 0;
231          int    x, y, dst_x;
232         uint32_t        *outBuf;
233         uint8_t *inBuf;
234
235         X += Glyph->OffsetX;
236         if( X < 0 ) {   // If -ve, skip the first -X pixels
237                 xStart = -X;
238                 X = 0;
239         }
240
241         Y += Glyph->OffsetY;
242         if( Y < 0 ) {   // If -ve, skip the first -Y lines
243                 yStart = -Y;
244                 Y = 0;
245         }
246
247 //      _SysDebug("X = %i, Y = %i", X, Y);
248         outBuf = (uint32_t*)Window->RenderBuffer + Y*Window->RealW + X;
249         inBuf = Glyph->Bitmap + yStart*Glyph->TrueWidth;
250
251         for( y = yStart; y < Glyph->TrueHeight; y ++ )
252         {
253                 for( x = xStart, dst_x = 0; x < Glyph->TrueWidth; x ++, dst_x ++ )
254                 {
255                         outBuf[dst_x] = Video_AlphaBlend( outBuf[dst_x], Color, inBuf[x] );
256                 }
257                 outBuf += Window->RealW;
258                 inBuf += Glyph->TrueWidth;
259         }
260 }
261
262 // Load system font (8x16 monospace)
263 #include "font_8x16.h"
264
265 /*
266  */
267 tGlyph *_SystemFont_CacheGlyph(tFont *Font, uint32_t Codepoint)
268 {
269          int    i;
270         uint8_t index = 0;
271         tGlyph  *ret;
272         uint8_t *data;
273
274 //      _SysDebug("_SystemFont_CacheGlyph: (Font=%p, Codepoint=0x%06x)", Font, Codepoint);
275         
276         if( Codepoint < 128 ) {
277                 index = Codepoint;
278         }
279         else {
280                 index = '?';    // Unknown glyphs come out as a question mark
281         }
282
283 //      _SysDebug(" index = %i", index);
284
285         ret = malloc( sizeof(tGlyph) + FONT_WIDTH*FONT_HEIGHT );
286         if( !ret ) {
287                 _SysDebug("ERROR: malloc(%i) failed", sizeof(tGlyph) + FONT_WIDTH*FONT_HEIGHT);
288                 return NULL;
289         }
290
291         ret->Codepoint = Codepoint;
292
293         ret->Width = FONT_WIDTH;
294         ret->Height = FONT_HEIGHT;
295         
296         ret->TrueWidth = FONT_WIDTH;
297         ret->TrueHeight = FONT_HEIGHT;
298
299         ret->OffsetX = 0;
300         ret->OffsetY = 0;
301         
302         data = &VTermFont[index * FONT_HEIGHT];
303
304         for( i = 0; i < FONT_HEIGHT; i ++ )
305         {
306                 ret->Bitmap[ i * 8 + 0 ] = data[i] & (1 << 7) ? 255 : 0;
307                 ret->Bitmap[ i * 8 + 1 ] = data[i] & (1 << 6) ? 255 : 0;
308                 ret->Bitmap[ i * 8 + 2 ] = data[i] & (1 << 5) ? 255 : 0;
309                 ret->Bitmap[ i * 8 + 3 ] = data[i] & (1 << 4) ? 255 : 0;
310                 ret->Bitmap[ i * 8 + 4 ] = data[i] & (1 << 3) ? 255 : 0;
311                 ret->Bitmap[ i * 8 + 5 ] = data[i] & (1 << 2) ? 255 : 0;
312                 ret->Bitmap[ i * 8 + 6 ] = data[i] & (1 << 1) ? 255 : 0;
313                 ret->Bitmap[ i * 8 + 7 ] = data[i] & (1 << 0) ? 255 : 0;
314         }
315
316         return ret;
317 }
318
319
320 /**
321  * \fn int ReadUTF8(char *Input, uint32_t *Val)
322  * \brief Read a UTF-8 character from a string
323  */
324 int ReadUTF8(const char *Input, uint32_t *Val)
325 {
326         const uint8_t   *str = (const uint8_t *)Input;
327         *Val = 0xFFFD;  // Assume invalid character
328         
329         // ASCII
330         if( !(*str & 0x80) ) {
331                 *Val = *str;
332                 return 1;
333         }
334         
335         // Middle of a sequence
336         if( (*str & 0xC0) == 0x80 ) {
337                 return 1;
338         }
339         
340         // Two Byte
341         if( (*str & 0xE0) == 0xC0 ) {
342                 *Val = (*str & 0x1F) << 6;      // Upper 6 Bits
343                 str ++;
344                 if( (*str & 0xC0) != 0x80)      return -1;      // Validity check
345                 *Val |= (*str & 0x3F);  // Lower 6 Bits
346                 return 2;
347         }
348         
349         // Three Byte
350         if( (*str & 0xF0) == 0xE0 ) {
351                 *Val = (*str & 0x0F) << 12;     // Upper 4 Bits
352                 str ++;
353                 if( (*str & 0xC0) != 0x80)      return -1;      // Validity check
354                 *Val |= (*str & 0x3F) << 6;     // Middle 6 Bits
355                 str ++;
356                 if( (*str & 0xC0) != 0x80)      return -1;      // Validity check
357                 *Val |= (*str & 0x3F);  // Lower 6 Bits
358                 return 3;
359         }
360         
361         // Four Byte
362         if( (*str & 0xF1) == 0xF0 ) {
363                 *Val = (*str & 0x07) << 18;     // Upper 3 Bits
364                 str ++;
365                 if( (*str & 0xC0) != 0x80)      return -1;      // Validity check
366                 *Val |= (*str & 0x3F) << 12;    // Middle-upper 6 Bits
367                 str ++;
368                 if( (*str & 0xC0) != 0x80)      return -1;      // Validity check
369                 *Val |= (*str & 0x3F) << 6;     // Middle-lower 6 Bits
370                 str ++;
371                 if( (*str & 0xC0) != 0x80)      return -1;      // Validity check
372                 *Val |= (*str & 0x3F);  // Lower 6 Bits
373                 return 4;
374         }
375         
376         // UTF-8 Doesn't support more than four bytes
377         return 4;
378 }
379
380
381
382

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