ecere/gfx/newFonts: Initial eC port of font engine by Alexis Naveros
[sdk] / ecere / src / gfx / newFonts / fontManager.ec
1 /* *****************************************************************************
2  * Original Version Copyright (c) 2007-2014 Alexis Naveros.
3  *
4  * Ecere Corporation has unlimited/unrestricted rights.
5  * *****************************************************************************/
6 import "LinkList"
7 import "File"
8
9 import "atlasBuilder"
10
11 #include <stdlib.h>
12 #include <math.h>
13
14 #include "cc.h"
15
16 static inline uint32 decodeUTF8( uint32 b, uint32 *state, unichar *retCodePoint ) { return ccUtf8ToUnicode(b, state, (uint *)retCodePoint); }
17
18 ////
19
20 // Based on Exponential blur, Jani Huhtanen, 2006
21
22 #define APREC 16
23 #define ZPREC 7
24
25 static void blurCols( byte *dst, int w, int h, int stride, int alpha )
26 {
27    int y;
28    for( y = 0 ; y < h ; y++ )
29    {
30       int x, z = 0;
31       for( x = 1 ; x < w ; x++ )
32       {
33          z += ( alpha * ( ( (int)( dst[x] ) << ZPREC ) - z ) ) >> APREC;
34          dst[x] = (byte)( z >> ZPREC );
35       }
36       for( x = w-2 ; x >= 0 ; x-- )
37       {
38          z += ( alpha * ( ( (int)( dst[x] ) << ZPREC ) - z ) ) >> APREC;
39          dst[x] = (byte)( z >> ZPREC );
40       }
41       dst += stride;
42    }
43 }
44
45 static void blurRows( byte *dst, int w, int h, int stride, int alpha )
46 {
47    int x;
48    for( x = 0 ; x < w ; x++ )
49    {
50       int y, z = 0;
51       for( y = stride ; y < h*stride ; y += stride )
52       {
53          z += ( alpha * ( ( (int)( dst[y] ) << ZPREC ) - z ) ) >> APREC;
54          dst[y] = (byte)( z >> ZPREC );
55       }
56       for( y = (h-2)*stride ; y >= 0 ; y -= stride )
57       {
58          z += ( alpha * ( ( (int)( dst[y] ) << ZPREC ) - z ) ) >> APREC;
59          dst[y] = (byte)( z >> ZPREC );
60       }
61       dst++;
62    }
63 }
64
65 static void blur( byte *dst, int width, int height, int stride, int radius )
66 {
67    if( radius >= 1 )
68    {
69       int boxcount = 2;
70       // Calculate the alpha such that 90% of the kernel is within the radius. (Kernel extends to infinity)
71       float radiusrange = (float)radius * ( 1.0f / sqrtf( 3 ) );
72       uint32 alpha = (uint32)roundf( ( 1 << APREC ) * ( 1.0f - expf( -2.3f / ( radiusrange + 1.0f ) ) ) );
73       for( ; boxcount ; boxcount-- )
74       {
75          blurRows( dst, width, height, stride, alpha );
76          blurCols( dst, width, height, stride, alpha );
77       }
78   }
79 }
80
81 #undef APREC
82 #undef ZPREC
83
84
85 #define APREC 12
86 #define ZPREC 7
87 #define SPREC (31-(APREC+ZPREC+8))
88
89 static void blurCols32( int *dst, int w, int h, int stride, int alpha )
90 {
91    int y;
92    for( y = 0 ; y < h ; y++ )
93    {
94       int x, z = 0;
95       for( x = 1 ; x < w ; x++ )
96       {
97          z += ( alpha * ( dst[x] - z ) ) >> APREC;
98          dst[x] = z;
99       }
100       for( x = w - 2 ; x >= 0 ; x-- )
101       {
102          z += ( alpha * ( dst[x] - z ) ) >> APREC;
103          dst[x] = z;
104       }
105       dst += stride;
106    }
107 }
108
109 static void blurRows32( int *dst, int w, int h, int stride, int alpha )
110 {
111   int x;
112   for( x = 0 ; x < w ; x++ )
113   {
114     int y, z = 0;
115     for( y = 1 * stride ; y < h * stride ; y += stride )
116     {
117       z += ( alpha * ( dst[y] - z ) ) >> APREC;
118       dst[y] = z;
119     }
120     for( y = ( h - 2 ) * stride ; y >= 0 ; y -= stride )
121     {
122       z += ( alpha * ( dst[y] - z ) ) >> APREC;
123       dst[y] = z;
124     }
125     dst++;
126   }
127 }
128
129 static void blurScale( byte *dst, int width, int height, int stride, int radius, int scale )
130 {
131    if( radius >= 1 )
132    {
133       int x, y, boxcount;
134       float radiusrange;
135       int alpha;
136       int *buffer, *bufferrow;
137       byte *dstrow;
138
139       if( scale > ( 1 << SPREC ) )
140          scale = 1 << SPREC;
141
142       buffer = malloc( width * height * sizeof(int) );
143       for( y = 0 ; y < height ; y++ )
144       {
145          dstrow = &dst[y*stride];
146          bufferrow = &buffer[y*width];
147          for( x = 0 ; x < width ; x++ )
148             bufferrow[x] = ( (int)dstrow[x] << ZPREC ) * scale;
149       }
150
151       boxcount = 3;
152
153       /* Calculate the alpha such that 90% of the kernel is within the radius. (Kernel extends to infinity) */
154       radiusrange = (float)radius * 0.3f;
155       alpha = (uint32)roundf( ( 1 << APREC ) * ( 1.0f - expf( -2.3f / ( radiusrange + 1.0f ) ) ) );
156       for( ; boxcount ; boxcount-- )
157       {
158          blurRows32( buffer, width, height, width, alpha );
159          blurCols32( buffer, width, height, width, alpha );
160       }
161
162       for( y = 0 ; y < height ; y++ )
163       {
164          dstrow = &dst[y*stride];
165          bufferrow = &buffer[y*width];
166          for( x = 0 ; x < width ; x++ )
167             dstrow[x] = (byte)Min(255, bufferrow[x] >> ZPREC);
168       }
169       free( buffer );
170    }
171 }
172
173 #undef APREC
174 #undef ZPREC
175 #undef SPREC
176
177
178 ////
179
180 static void buildCursorGlyph( byte *dst, int glyphwidth, int glyphheight, int dststride )
181 {
182   int x, y, hbarheight, hgap, hline, vline;
183   byte *dstrow;
184
185   if( glyphwidth >= 3 )
186   {
187     hbarheight = 1 + ( glyphheight >> 6 );
188     hgap = ( glyphwidth - ( 1 + ( glyphheight >> 6 ) ) ) >> 1;
189     hline = hgap + glyphwidth - ( hgap << 1 );
190     vline = glyphheight - ( hbarheight << 1 );
191     for( y = 0 ; y < hbarheight ; y++ )
192     {
193       dstrow = dst;
194       for( x = 0 ; x < glyphwidth ; x++ )
195         dstrow[x] = 255;
196       dst += dststride;
197     }
198     for( y = 0 ; y < vline ; y++ )
199     {
200       dstrow = dst;
201       for( x = 0 ; x < hgap ; x++ )
202         dstrow[x] = 0;
203       for( ; x < hline ; x++ )
204         dstrow[x] = 255;
205       for( ; x < glyphwidth ; x++ )
206         dstrow[x] = 0;
207       dst += dststride;
208     }
209     for( y = 0 ; y < hbarheight ; y++ )
210     {
211       dstrow = dst;
212       for( x = 0 ; x < glyphwidth ; x++ )
213         dstrow[x] = 255;
214       dst += dststride;
215     }
216   }
217   else
218   {
219     for( y = 0 ; y < glyphheight ; y++ )
220     {
221       dstrow = dst;
222       for( x = 0 ; x < glyphwidth ; x++ )
223         dstrow[x] = 255;
224       dst += dststride;
225     }
226   }
227
228   return;
229 }
230
231 static inline void addGlyphAdvance( int *x, int *subpixel, FMGlyph *glyph )
232 {
233    *subpixel += glyph->advance;
234 #if FM_SUBPIXEL_ROUNDING_RANGE
235    *subpixel = ( *subpixel + (FM_SUBPIXEL_ROUNDING_RANGE>>1) ) & ~(FM_SUBPIXEL_ROUNDING_RANGE-1);
236 #endif
237    *x += *subpixel >> 6;
238    *subpixel &= 0x3f;
239 }
240
241 public class FontManagerRenderer
242 {
243 public:
244    FontManager fm;
245
246    virtual bool init();
247
248    // Create, resize or update texture data ; rect is [minx,maxx,miny,maxy]
249    virtual int createTexture( int width, int height );
250    virtual int resizeTexture( int width, int height );
251    virtual void updateTexture( int *rect, const byte *data );
252
253    // If the renderer does any buffering of drawImage(), flush it all right now (texture may change immediately after)
254    virtual void flush( );
255
256    // If the renderer records per-image data, return an imageIndex passed to drawImage()
257    virtual int registerImage( int offsetx, int offsety, int width, int height );
258    // Draw an image, imageIndex passed as the value previously returned by registerImage()
259    virtual void drawImage( int targetx, int targety, int imageIndex );
260    // Draw an image, called instead of drawImage() for text cursors, can point to exactly the same function
261    virtual void drawImageCursor( int targetx, int targety, int imageIndex );
262    // If drawImage is zero, then this alternate function is called, passing everything required to render the glyph
263    virtual void drawImageAlt( byte *texdata, int targetx, int targety, int offsetx, int offsety, int width, int height );
264    // Draw a non-aligned image, imageIndex passed as the value previously returned by registerImage()
265    virtual void drawImageFloat( float targetx, float targety, float angsin, float angcos, int imageIndex );
266
267    // The renderer must flush all recorded images, registerImage() will be called for new images
268    virtual void resetImages( );
269 };
270
271
272 ////
273
274
275 #include <ft2build.h>
276
277 #include FT_FREETYPE_H
278 #include FT_ADVANCES_H
279
280
281 ////
282
283
284 #define FM_SUPPORT_GLYPH_ROTATION (1)
285
286
287 ////
288
289
290 #define FM_ENABLE_HINTING (1)
291 #define FM_SUBPIXEL_ROUNDING_RANGE (16)
292 #if FM_SUPPORT_GLYPH_ROTATION
293  #define FM_TEXTURE_PADDING (1)
294 #else
295  #define FM_TEXTURE_PADDING (0)
296 #endif
297 #define FM_DEBUG_WHITERECT (0)
298
299
300 #define FM_HASH_TABLE_SIZE (4096)
301 #define FM_INIT_GLYPHS (1024)
302 #define FM_INIT_ATLAS_NODES (512)
303
304 #define FM_MAX_STATES (16)
305
306
307 ////
308
309 struct FMFreeTypeFont
310 {
311    FT_Face face;
312
313    static inline int ::init()
314    {
315      FT_Error ftError;
316      ftError = FT_Init_FreeType( &ftLibrary2 );
317      return ftError == 0;
318    }
319
320    static inline int loadFont( byte *data, int dataSize )
321    {
322      FT_Error ftError = FT_New_Memory_Face( ftLibrary2, (const FT_Byte*)data, dataSize, 0, &face );
323      return ftError == 0;
324    }
325
326    void free()
327    {
328       FT_Done_Face( face );
329    }
330
331    static inline void getFontVMetrics( float *ascent, float *descent, float *lineHeight, float *limitminy, float *limitmaxy )
332    {
333      *ascent = (float)face->ascender;
334      *descent = (float)face->descender;
335      *lineHeight = (float)face->height / (float)face->units_per_EM;
336      *limitminy = (float)face->bbox.yMin / (float)face->units_per_EM;
337      *limitmaxy = (float)face->bbox.yMax / (float)face->units_per_EM;
338    }
339
340    static inline int getGlyphIndex( unichar codepoint )
341    {
342      return FT_Get_Char_Index( this.face, codepoint );
343    }
344
345    static int buildGlyphBitmap( int glyph, int size, int subpixel, int *advance, int *x0, int *y0, int *x1, int *y1 )
346    {
347      FT_Error ftError;
348      FT_GlyphSlot glyphslot;
349      FT_Vector subvector;
350
351      ftError = FT_Set_Pixel_Sizes( this.face, 0, (FT_UInt)size );
352      if( ftError )
353        return 0;
354
355      subvector.x = subpixel;
356      subvector.y = 0;
357      FT_Set_Transform( face, 0, &subvector );
358
359    #if FM_ENABLE_HINTING
360      ftError = FT_Load_Glyph( face, glyph, FT_LOAD_RENDER );
361    #else
362      ftError = FT_Load_Glyph( face, glyph, FT_LOAD_RENDER | FT_LOAD_NO_HINTING );
363    #endif
364      if( ftError )
365        return 0;
366
367      glyphslot = face->glyph;
368      *advance = glyphslot->metrics.horiAdvance;
369      *x0 = glyphslot->bitmap_left;
370      *x1 = glyphslot->bitmap_left + glyphslot->bitmap.width;
371      *y0 = -glyphslot->bitmap_top;
372      *y1 = -glyphslot->bitmap_top + glyphslot->bitmap.rows;
373
374      return 1;
375    }
376
377    static void copyGlyphBitmap( byte *dst, int glyphwidth, int glyphheight, int dststride, int glyphindex )
378    {
379      int x, y;
380      byte *dstrow, *src;
381      FT_GlyphSlot glyphslot;
382
383      glyphslot = face->glyph;
384      src = glyphslot->bitmap.buffer;
385      for( y = 0 ; y < glyphslot->bitmap.rows ; y++ )
386      {
387        dstrow = &dst[ y * dststride ];
388        for( x = 0 ; x < glyphslot->bitmap.width ; x++ )
389          dstrow[ x ] = src[ x ];
390        src += glyphslot->bitmap.width;
391      }
392      return;
393    }
394
395    static inline int getGlyphKernAdvance( int glyph1, int glyph2 )
396    {
397      FT_Vector ftKerning;
398      FT_Get_Kerning( face, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning );
399      return ftKerning.x;
400    }
401 };
402
403 #define FM_DEF_CODEPOINT_BITS (32)
404 #define FM_DEF_SIZE_BITS (12)
405 #define FM_DEF_SUBPIXEL_BITS (6)
406 #define FM_DEF_BLURRADIUS_BITS (8)
407 #define FM_DEF_BLURSCALE_BITS (6)
408
409 #define FM_DEF_CODEPOINT_SHIFT (0)
410 #define FM_DEF_SIZE_SHIFT (FM_DEF_CODEPOINT_BITS)
411 #define FM_DEF_SUBPIXEL_SHIFT (FM_DEF_CODEPOINT_BITS+FM_DEF_SIZE_BITS)
412 #define FM_DEF_BLURRADIUS_SHIFT (FM_DEF_CODEPOINT_BITS+FM_DEF_SIZE_BITS+FM_DEF_SUBPIXEL_BITS)
413 #define FM_DEF_BLURSCALE_SHIFT (FM_DEF_CODEPOINT_BITS+FM_DEF_SIZE_BITS+FM_DEF_SUBPIXEL_BITS+FM_DEF_BLURRADIUS_BITS)
414
415 #define FM_GLYPH_COMPUTE_DEF(codepoint,size,subpixel,blurradius,blurscale) ( (((uint64)(codepoint))<<FM_DEF_CODEPOINT_SHIFT) | (((uint64)(size))<<FM_DEF_SIZE_SHIFT) | (((uint64)(subpixel))<<FM_DEF_SUBPIXEL_SHIFT) | (((uint64)(blurradius))<<FM_DEF_BLURRADIUS_SHIFT) | (((uint64)(blurscale))<<FM_DEF_BLURSCALE_SHIFT) )
416
417 #define FM_SIZE_MAX ((1<<FM_DEF_SIZE_BITS)-1)
418 #define FM_BLUR_RADIUS_MAX ((1<<FM_DEF_BLURRADIUS_BITS)-1)
419 #define FM_BLUR_SCALE_MAX ((1<<FM_DEF_BLURSCALE_BITS)-1)
420
421 #define FM_GLYPH_CODEPOINT_CURSOR (0x1)
422 #define FM_GLYPH_CODEPOINT_REPLACEMENT (0xfffd)
423
424 #define FM_ALIGN_LEFT (0x0)
425 #define FM_ALIGN_CENTER (0x1)
426 #define FM_ALIGN_RIGHT (0x2)
427
428 #define FM_ALIGN_BASELINE (0x0)
429 #define FM_ALIGN_TOP (0x4)
430 #define FM_ALIGN_MIDDLE (0x8)
431 #define FM_ALIGN_BOTTOM (0x10)
432
433
434 public struct FMPathDraw
435 {
436   int prevGlyphIndex;
437   float middleAlign;
438 };
439
440 struct FMQuad { int x0, y0, x1, y1; };
441
442 struct FMGlyph
443 {
444    int glyphindex;
445    uint64 glyphdef;
446    short x0, y0, x1, y1;
447    short advance, offsetx, offsety;
448    int imageIndex;
449    int listnext;
450
451    static void getQuad( float x, float y, FMQuad q )
452    {
453       int rx = (int)(x + offsetx);
454       int ry = (int)(y + offsety);
455       q = { rx, ry, rx + x1 - x0, ry + y1 - y0 };
456    }
457 };
458
459 public class FMFont : struct
460 {
461    public LinkElement link;
462    FMFreeTypeFont ftFont;
463    void *fontdata;
464    float ascender;
465    float descender;
466    float middleAlign;
467    float lineHeight;
468    float limitminy, limitmaxy;
469    FMGlyph *glyphs;
470    int glyphalloc;
471    int glyphcount;
472    int hashtable[FM_HASH_TABLE_SIZE];
473
474    ~FMFont()
475    {
476       ftFont.free();
477       delete fontdata;
478       delete glyphs;
479    }
480
481    static FMGlyph *allocGlyph( )
482    {
483       if( glyphcount >= glyphalloc )
484       {
485          glyphalloc <<= 1;
486          if( !(glyphs = renew glyphs FMGlyph[glyphalloc]) )
487             return null;
488       }
489       return &glyphs[ glyphcount++ ];
490    }
491
492    ////
493
494    static float getVertAlign( int align, int size )
495    {
496      if( align & FM_ALIGN_TOP )
497        return ascender * size;
498      else if( align & FM_ALIGN_MIDDLE )
499        return middleAlign * size;
500      else if( align & FM_ALIGN_BOTTOM )
501        return descender * (float)size;
502      return 0.0f;
503    }
504
505    static inline void addKerning( int prevGlyphIndex, FMGlyph *glyph, int *x, int *subpixel )
506    {
507      if( prevGlyphIndex != -1 )
508      {
509        *subpixel += ftFont.getGlyphKernAdvance( prevGlyphIndex, glyph->glyphindex );
510    #if FM_SUBPIXEL_ROUNDING_RANGE
511        *subpixel = ( *subpixel + (FM_SUBPIXEL_ROUNDING_RANGE>>1) ) & ~(FM_SUBPIXEL_ROUNDING_RANGE-1);
512    #endif
513        *x += *subpixel >> 6;
514        *subpixel &= 0x3f;
515      }
516    }
517
518 };
519
520 struct FMState
521 {
522    FMFont font;
523    uint16 size;
524    uint16 align;
525    uint16 blurradius;
526    uint16 blurscale;
527 };
528
529 ////
530
531
532 static FT_Library ftLibrary2;
533
534 public class FontManager
535 {
536   FontManagerRenderer renderer;
537   int width, height;
538   float widthinv, heightinv;
539
540   AtlasBuilder atlas { };
541   byte *texdata;
542   int dirtyrect[4];
543
544   LinkList<FMFont, link = link> fontList { };
545
546   FMState states[FM_MAX_STATES];
547   int nstates;
548
549    #if FM_DEBUG_WHITERECT
550    static void addWhiteRect( int w, int h )
551    {
552      int gx, gy;
553      if( atlas.addRect( w, h, &gx, &gy ) )
554      {
555         // Rasterize
556         byte * dst = &texdata[ gx + ( gy * width ) ];
557         int x, y;
558         for( y = 0 ; y < h ; y++)
559         {
560           for( x = 0 ; x < w ; x++ )
561             dst[x] = 0xff;
562           dst += width;
563         }
564
565         dirtyrect[0] = Min( dirtyrect[0], gx );
566         dirtyrect[1] = Min( dirtyrect[1], gy );
567         dirtyrect[2] = Max( dirtyrect[2], gx + w );
568         dirtyrect[3] = Max( dirtyrect[3], gy + h );
569      }
570    }
571    #endif
572
573    static FMGlyph *getGlyph( FMFont font, unichar codepoint, int size, int subpixel, int blurradius, int blurscale )
574    {
575      int i, glyphindex, advance, x0, y0, x1, y1, glyphwidth, glyphheight, gx, gy;
576    #if FM_TEXTURE_PADDING >= 1
577      int x, y;
578    #endif
579      uint64 glyphdef;
580      FMGlyph *glyph;
581      uint32 hashindex;
582      int padding, added;
583      byte *bdst;
584      byte *dst;
585
586      glyph = 0;
587      if( size < 0.2 )
588        return 0;
589      padding = blurradius + FM_TEXTURE_PADDING;
590
591      /* Find code point and size. */
592      glyphdef = FM_GLYPH_COMPUTE_DEF( codepoint, size, subpixel, blurradius, blurscale );
593      hashindex = ccHash32Int64Inline( glyphdef ) & ( FM_HASH_TABLE_SIZE - 1 );
594      i = font.hashtable[hashindex];
595      while( i != -1 )
596      {
597        if( glyphdef == font.glyphs[i].glyphdef )
598          return &font.glyphs[i];
599        i = font.glyphs[i].listnext;
600      }
601
602      /* Could not find glyph, create it. */
603      if( codepoint == FM_GLYPH_CODEPOINT_CURSOR )
604      {
605        glyphindex = -1;
606        advance = 0;
607    #if 0
608        x0 = 0;
609        x1 = 1;
610    #else
611        x0 = -2;
612        x1 = 3;
613    #endif
614        y0 = -(int)ceilf( font.limitmaxy * (float)size );
615        y1 = -(int)floorf( font.limitminy * (float)size );
616        glyphheight = y1 - y0;
617        i = ( glyphheight - size ) / 3;
618        y0 += i;
619        y1 -= i;
620      }
621      else
622      {
623        glyphindex = font.ftFont.getGlyphIndex( codepoint );
624        if( !( font.ftFont.buildGlyphBitmap( glyphindex, size, subpixel, &advance, &x0, &y0, &x1, &y1 ) ) )
625        {
626          if( codepoint != FM_GLYPH_CODEPOINT_REPLACEMENT )
627            return getGlyph(font, FM_GLYPH_CODEPOINT_REPLACEMENT, size, subpixel, blurradius, blurscale );
628          return 0;
629        }
630      }
631      glyphwidth = ( x1 - x0 ) + ( padding << 1 );
632      glyphheight = ( y1 - y0 ) + ( padding << 1 );
633
634      // Find free spot for the rect in the atlas
635      added = atlas.addRect( glyphwidth, glyphheight, &gx, &gy );
636      if( !( added ) && ( onAtlasFull ) )
637      {
638        /* Atlas is full, let the user to resize the atlas (or not), and try again. */
639        onAtlasFull();
640        added = atlas.addRect( glyphwidth, glyphheight, &gx, &gy );
641      }
642      if( !( added ) )
643        return 0;
644
645      /* Build the glyph */
646      glyph = font.allocGlyph();
647      glyph->glyphdef = glyphdef;
648      glyph->glyphindex = glyphindex;
649      glyph->x0 = (short)gx;
650      glyph->y0 = (short)gy;
651      glyph->x1 = (short)( glyph->x0 + glyphwidth );
652      glyph->y1 = (short)( glyph->y0 + glyphheight );
653      glyph->advance = (short)advance;
654      glyph->offsetx = (short)( x0 - padding );
655      glyph->offsety = (short)( y0 - padding );
656      glyph->listnext = 0;
657      glyph->imageIndex = -1;
658      if( renderer.registerImage )
659        glyph->imageIndex = renderer.registerImage( gx, gy, glyphwidth, glyphheight );
660
661      /* Add char to hash table */
662      glyph->listnext = font.hashtable[hashindex];
663      font.hashtable[hashindex] = font.glyphcount - 1;
664
665      /* Rasterize */
666      dst = &texdata[ ( glyph->x0 + padding ) + ( ( glyph->y0 + padding ) * this.width ) ];
667      if( codepoint == FM_GLYPH_CODEPOINT_CURSOR )
668        buildCursorGlyph( dst, glyphwidth - ( padding << 1 ), glyphheight - ( padding << 1 ), this.width );
669      else
670        font.ftFont.copyGlyphBitmap( dst, glyphwidth - ( padding << 1 ), glyphheight - ( padding << 1 ), this.width, glyphindex );
671
672      /* Blur */
673      if( blurradius > 0 )
674      {
675        bdst = &texdata[ glyph->x0 + ( glyph->y0 * this.width ) ];
676        if( blurscale == 1 )
677          blur(bdst, glyphwidth, glyphheight, this.width, blurradius );
678        else
679          blurScale(bdst, glyphwidth, glyphheight, this.width, blurradius, blurscale );
680      }
681
682    #if FM_TEXTURE_PADDING >= 1
683      dst = &texdata[ glyph->x0 + ( glyph->y0 * this.width ) ];
684      for( y = 0 ; y < glyphheight ; y++ )
685      {
686        dst[ y * this.width] = 0;
687        dst[ ( glyphwidth - 1 ) + ( y * this.width ) ] = 0;
688      }
689      for( x = 0 ; x < glyphwidth ; x++ )
690      {
691        dst[ x ] = 0;
692        dst[ x + ( ( glyphheight - 1 ) * this.width ) ] = 0;
693      }
694    #endif
695
696      dirtyrect[0] = Min( dirtyrect[0], glyph->x0 );
697      dirtyrect[1] = Min( dirtyrect[1], glyph->y0 );
698      dirtyrect[2] = Max( dirtyrect[2], glyph->x1 );
699      dirtyrect[3] = Max( dirtyrect[3], glyph->y1 );
700
701      return glyph;
702    }
703
704    static inline void drawTextGlyph( FMFont font, FMGlyph *glyph, int x, int y )
705    {
706       int ptx = x + glyph->offsetx;
707       int pty = y + glyph->offsety;
708       if( renderer.drawImage )
709          renderer.drawImage( ptx, pty, glyph->imageIndex );
710       else if( renderer.drawImageAlt )
711          renderer.drawImageAlt( texdata, ptx, pty, glyph->x0, glyph->y0, glyph->x1 - glyph->x0, glyph->y1 - glyph->y0 );
712    }
713
714    static inline void drawTextCursorGlyph( FMFont font, FMGlyph *glyph, int x, int y )
715    {
716      int ptx, pty;
717      ptx = x + glyph->offsetx;
718      pty = y + glyph->offsety;
719      if( renderer.drawImageCursor )
720        renderer.drawImageCursor( ptx, pty, glyph->imageIndex );
721      else if( renderer.drawImageAlt )
722        renderer.drawImageAlt( texdata, ptx, pty, glyph->x0, glyph->y0, glyph->x1 - glyph->x0, glyph->y1 - glyph->y0 );
723    }
724
725    static inline void drawTextGlyphFloat( FMFont font, FMGlyph *glyph, float x, float y, float vectorx, float vectory, float offsetx, float offsety )
726    {
727       float vectx = (float)glyph->offsetx + offsetx;
728       float vecty = (float)glyph->offsety + offsety;
729       float ptx = x + ( vectorx * vectx ) - ( vectory * vecty );
730       float pty = y + ( vectorx * vecty ) + ( vectory * vectx );
731       renderer.drawImageFloat( ptx, pty, vectory, vectorx, glyph->imageIndex );
732    }
733
734 public:
735
736    property FontManagerRenderer renderer { set { renderer = value; } get { return renderer; } }\
737
738    // When notified of a full atlas, you should call fmExpandAtlas() or fmResetAtlas() within the callback
739    virtual void (*onAtlasFull)();
740
741    // Create and destroy font manager
742    bool create( int width, int height, FontManagerRenderer renderer)
743    {
744       bool result = false;
745
746       this.renderer = renderer;
747       incref renderer;
748       renderer.init();
749
750       // Initialize implementation library
751       if(FMFreeTypeFont::init() )
752       {
753          this.width = width;
754          this.height = height;
755          if(renderer.createTexture( width, height ))
756          {
757             if((atlas.create( this.width, this.height, FM_INIT_ATLAS_NODES )))
758             {
759                // Create texture for the cache.
760                widthinv = 1.0f / width;
761                heightinv = 1.0f / height;
762                texdata = new byte[width * height];
763                if(texdata)
764                {
765                   memset( texdata, 0, width * height );
766
767                   dirtyrect[0] = this.width;
768                   dirtyrect[1] = this.height;
769                   dirtyrect[2] = 0;
770                   dirtyrect[3] = 0;
771
772                // Add white rect at 0,0 for debug drawing.
773                #if FM_DEBUG_WHITERECT
774                  fmAddWhiteRect(2, 2 );
775                #endif
776
777                  pushState();
778                  clearState();
779
780                  result = true;
781                }
782             }
783          }
784       }
785       return result;
786    }
787
788    ~FontManager()
789    {
790      delete renderer;
791
792      fontList.Free();
793
794      delete atlas;
795      delete texdata;
796    }
797
798
799    ////
800
801    // State setting
802    void setState( FMFont font, int size, int align, int blurradius, int blurscale )
803    {
804      FMState *state;
805      if( size >= FM_SIZE_MAX )
806        size = FM_SIZE_MAX;
807      if( blurradius >= FM_BLUR_RADIUS_MAX )
808        blurradius = FM_BLUR_RADIUS_MAX;
809      else if( blurradius < 0 )
810        blurradius = 0;
811      if( blurscale >= FM_BLUR_SCALE_MAX )
812        blurscale = FM_BLUR_SCALE_MAX;
813      else if( blurscale < 1 )
814        blurscale = 1;
815      state = &states[ nstates - 1 ];
816      state->font = font;
817      state->size = (uint16)size;
818      state->align = (uint16)align;
819      state->blurradius = (uint16)blurradius;
820      state->blurscale = (uint16)blurscale;
821      return;
822    }
823
824    void setFont( FMFont font )
825    {
826      states[ nstates - 1 ].font = font;
827    }
828
829    void setSize( int size )
830    {
831      if( size >= FM_SIZE_MAX )
832        size = FM_SIZE_MAX;
833
834      states[ nstates - 1 ].size = (uint16)size;
835    }
836
837    void setAlign( int align )
838    {
839      states[ nstates - 1 ].align = (uint16)align;
840    }
841
842    void setBlur( int radius, int scale )
843    {
844      if( radius >= FM_BLUR_RADIUS_MAX )
845        radius = FM_BLUR_RADIUS_MAX;
846      else if( radius < 0 )
847        radius = 0;
848      if( scale >= FM_BLUR_SCALE_MAX )
849        scale = FM_BLUR_SCALE_MAX;
850      else if( scale < 1 )
851        scale = 1;
852      states[ nstates - 1 ].blurradius = (uint16)radius;
853      states[ nstates - 1 ].blurscale = (uint16)scale;
854    }
855
856
857    // State handling
858    void pushState( )
859    {
860       if(nstates < FM_MAX_STATES)
861       {
862          if( nstates > 0 )
863             memcpy( &states[nstates], &states[nstates-1], sizeof(FMState) );
864          nstates++;
865       }
866    }
867
868    void popState()
869    {
870       if(nstates > 1)
871          nstates--;
872    }
873
874    void clearState()
875    {
876      FMState *state = &states[ nstates - 1 ];
877      state->size = 12;
878      state->font = 0;
879      state->blurradius = 0;
880      state->blurscale = 1;
881      state->align = FM_ALIGN_LEFT | FM_ALIGN_BASELINE;
882    }
883
884
885    ////
886
887    static void freeFont(FMFont font)
888    {
889      if(font)
890      {
891         fontList.Remove((IteratorPointer)font);
892         delete font;
893      }
894    }
895
896    // Add font from file
897    FMFont addFont(const String path )
898    {
899       FMFont font = null;
900       File f = FileOpen(path, read);
901       if(f)
902       {
903          // Read in the font data
904          int dataSize = f.GetSize();
905          byte *data = new byte[dataSize];
906          if(data)
907          {
908             f.Read(data, 1, dataSize);
909             font = addFontData(data, dataSize );
910             if(!font)
911                delete data;
912          }
913          delete f;
914       }
915       return font;
916    }
917
918    // Add font from data ; do not free( data ), the font manager will do that when removing the font
919    FMFont addFontData( byte *data, int dataSize )
920    {
921      FMFont font
922      {
923         glyphs = new FMGlyph[FM_INIT_GLYPHS];
924         glyphalloc = FM_INIT_GLYPHS;
925      };
926      if(font)
927      {
928          int i;
929          float ascent, descent, fontHeight;
930
931          fontList.Add(font);
932
933          // Init hash lookup
934          for( i = 0 ; i < FM_HASH_TABLE_SIZE ; i++ )
935             font.hashtable[i] = -1;
936
937          // Init font
938          if(!font.ftFont.loadFont(data, dataSize))
939          {
940             freeFont(font );
941             return 0;
942          }
943
944          // Store normalized line height. The real line height is got by multiplying by font size.
945          font.ftFont.getFontVMetrics( &ascent, &descent, &font.lineHeight, &font.limitminy, &font.limitmaxy );
946          fontHeight = ascent - descent;
947          font.ascender = ascent / fontHeight;
948          font.descender = descent / fontHeight;
949          font.middleAlign = 0.5f * ( font.ascender + font.descender );
950          font.fontdata = data;
951       }
952       return font;
953    }
954
955    // Free font
956    void removeFont( FMFont font )
957    {
958       freeFont(font );
959    }
960
961    // Draw text
962    int drawText( int x, int y, const char *string, int stringlength )
963    {
964      int subpixel, index;
965      FMState *state;
966      unichar codepoint;
967      uint32 utf8state;
968      FMGlyph *glyph;
969      int prevGlyphIndex;
970      int blurradius, blurscale;
971      FMFont font;
972
973      state = &states[ nstates - 1 ];
974      glyph = 0;
975      utf8state = 0;
976      prevGlyphIndex = -1;
977      blurradius = state->blurradius;
978      blurscale = state->blurscale;
979      if( !( state->font ) )
980        return x;
981      font = state->font;
982      if( !( stringlength ) )
983        stringlength = strlen( string );
984
985      // Align horizontally
986      if( state->align & FM_ALIGN_RIGHT )
987        x -= getTextWidth(string, stringlength );
988      else if( state->align & FM_ALIGN_CENTER )
989        x -= getTextWidth(string, stringlength ) >> 1;
990
991      // Align vertically
992      y += roundf( font.getVertAlign(state->align, state->size ) );
993
994      subpixel = 0;
995      for( index = 0 ; index < stringlength ; index++ )
996      {
997        if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
998          continue;
999        glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1000        if( glyph )
1001        {
1002          font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1003          drawTextGlyph(font, glyph, x, y );
1004          addGlyphAdvance( &x, &subpixel, glyph );
1005        }
1006        prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1007      }
1008
1009      return x + ( subpixel >= 32 );
1010    }
1011
1012    int drawTextWithCursor( int x, int y, const char *string, int stringlength, int cursoroffset )
1013    {
1014      int subpixel, index;
1015      FMState *state;
1016      unichar codepoint;
1017      uint32 utf8state;
1018      FMGlyph *glyph;
1019      int prevGlyphIndex;
1020      int blurradius, blurscale;
1021      FMFont font;
1022
1023      state = &states[ nstates - 1 ];
1024      glyph = 0;
1025      utf8state = 0;
1026      prevGlyphIndex = -1;
1027      blurradius = state->blurradius;
1028      blurscale = state->blurscale;
1029      if( !( state->font ) )
1030        return x;
1031      font = state->font;
1032      if( !( stringlength ) )
1033        stringlength = strlen( string );
1034
1035      // Align horizontally
1036      if( state->align & FM_ALIGN_RIGHT )
1037        x -= getTextWidth(string, stringlength );
1038      else if( state->align & FM_ALIGN_CENTER )
1039        x -= getTextWidth(string, stringlength ) >> 1;
1040
1041      // Align vertically
1042      y += roundf( font.getVertAlign(state->align, state->size ) );
1043
1044      subpixel = 0;
1045      for( index = 0 ; ; index++ )
1046      {
1047        if( index == cursoroffset )
1048        {
1049          glyph = getGlyph(font, FM_GLYPH_CODEPOINT_CURSOR, state->size, subpixel, blurradius, blurscale );
1050          if( glyph )
1051            drawTextCursorGlyph(font, glyph, x, y );
1052        }
1053        if( index >= stringlength )
1054          break;
1055        if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1056          continue;
1057        glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1058        if( glyph )
1059        {
1060          font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1061          drawTextGlyph(font, glyph, x, y );
1062          addGlyphAdvance( &x, &subpixel, glyph );
1063        }
1064        prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1065      }
1066
1067      return x + ( subpixel >= 32 );
1068    }
1069
1070    int drawTextTruncate( int x, int y, int truncatewidth, const char *string, int stringlength, char *extstring, int extwidth )
1071    {
1072      int subpixel, index, textwidth, truncatepoint;
1073      FMState *state;
1074      unichar codepoint;
1075      uint32 utf8state;
1076      FMGlyph *glyph;
1077      int prevGlyphIndex;
1078      int blurradius, blurscale;
1079      FMFont font;
1080
1081      state = &states[ nstates - 1 ];
1082      glyph = 0;
1083      utf8state = 0;
1084      prevGlyphIndex = -1;
1085      blurradius = state->blurradius;
1086      blurscale = state->blurscale;
1087      if( !( state->font ) )
1088        return x;
1089      font = state->font;
1090      if( !( stringlength ) )
1091        stringlength = strlen( string );
1092      textwidth = getTextWidthTruncate(string, stringlength, truncatewidth );
1093
1094      truncatepoint = x + truncatewidth;
1095      if( textwidth <= truncatewidth )
1096        extstring = 0;
1097      else
1098      {
1099        if( extwidth >= truncatewidth )
1100          return x;
1101        truncatepoint -= extwidth;
1102      }
1103
1104      // Align horizontally
1105      if( state->align & FM_ALIGN_RIGHT )
1106        x -= textwidth;
1107      else if( state->align & FM_ALIGN_CENTER )
1108        x -= textwidth >> 1;
1109
1110      // Align vertically
1111      y += roundf( font.getVertAlign(state->align, state->size ) );
1112
1113      subpixel = 0;
1114      for( index = 0 ; index < stringlength ; index++ )
1115      {
1116        if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1117          continue;
1118        glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1119        if( glyph )
1120        {
1121          font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1122          drawTextGlyph(font, glyph, x, y );
1123          addGlyphAdvance( &x, &subpixel, glyph );
1124          if( x > truncatepoint )
1125            break;
1126        }
1127        prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1128      }
1129      x += ( subpixel >= 32 );
1130      if( extstring )
1131        drawText(x, y, extstring, 0 );
1132
1133      return x;
1134    }
1135
1136
1137    ////
1138
1139
1140    // Measure text
1141    int getTextWidth( const char *string, int stringlength )
1142    {
1143      return getTextWidthTruncate(string, stringlength, ( (unsigned)1 << ( ( sizeof(int) * CHAR_BIT ) - 1 ) ) - 1 );
1144    }
1145
1146
1147    int getTextWidthTruncate( const char *string, int stringlength, int truncatewidth )
1148    {
1149      int subpixel, index, advance;
1150      FMState *state;
1151      unichar codepoint;
1152      uint32 utf8state;
1153      FMGlyph *glyph;
1154      int prevGlyphIndex;
1155      int blurradius, blurscale;
1156      FMFont font;
1157
1158      state = &states[ nstates - 1 ];
1159      glyph = 0;
1160      utf8state = 0;
1161      prevGlyphIndex = -1;
1162      blurradius = state->blurradius;
1163      blurscale = state->blurscale;
1164      if( !( state->font ) )
1165        return 0;
1166      font = state->font;
1167      if( !( stringlength ) )
1168        stringlength = strlen( string );
1169
1170      advance = 0;
1171      subpixel = 0;
1172      for( index = 0 ; index < stringlength ; index++ )
1173      {
1174        if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1175          continue;
1176        glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1177        if( glyph )
1178        {
1179          font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1180          addGlyphAdvance( &advance, &subpixel, glyph );
1181          if( advance > truncatewidth )
1182            break;
1183        }
1184        prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1185      }
1186
1187      return advance + ( subpixel >= 32 );
1188    }
1189
1190    int getTextBounds( int x, int y, const char *string, int stringlength, int *bounds )
1191    {
1192      int subpixel, index;
1193      FMState *state;
1194      unichar codepoint;
1195      uint32 utf8state;
1196      FMQuad q;
1197      FMGlyph *glyph;
1198      int prevGlyphIndex;
1199      int blurradius, blurscale;
1200      FMFont font;
1201      int startx, advance;
1202      int minx, miny, maxx, maxy;
1203
1204      state = &states[ nstates - 1 ];
1205      glyph = 0;
1206      utf8state = 0;
1207      prevGlyphIndex = -1;
1208      blurradius = state->blurradius;
1209      blurscale = state->blurscale;
1210      if( !( state->font ) )
1211        return x;
1212      font = state->font;
1213      if( !( stringlength ) )
1214        stringlength = strlen( string );
1215
1216      // Align vertically
1217      y += font.getVertAlign(state->align, state->size );
1218
1219      minx = maxx = x;
1220      miny = maxy = y;
1221      startx = x;
1222
1223      subpixel = 0;
1224      for( index = 0 ; index < stringlength ; index++ )
1225      {
1226        if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1227          continue;
1228        glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1229        if( glyph )
1230        {
1231          font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1232          glyph->getQuad(x, y, q);
1233          if( q.x0 < minx )
1234            minx = q.x0;
1235          if( q.x1 > maxx )
1236            maxx = q.x1;
1237          if( q.y0 < miny )
1238            miny = q.y0;
1239          if( q.y1 > maxy )
1240            maxy = q.y1;
1241          addGlyphAdvance( &x, &subpixel, glyph );
1242        }
1243        prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1244      }
1245
1246      advance = x - startx;
1247
1248      /* Align horizontally */
1249      if( state->align & FM_ALIGN_RIGHT )
1250      {
1251        minx -= advance;
1252        maxx -= advance;
1253      }
1254      else if( state->align & FM_ALIGN_CENTER )
1255      {
1256        minx -= advance * 0.5f;
1257        maxx -= advance * 0.5f;
1258      }
1259
1260      if( bounds )
1261      {
1262        bounds[0] = minx;
1263        bounds[1] = maxx;
1264        bounds[2] = miny;
1265        bounds[3] = maxy;
1266      }
1267
1268      return advance + ( subpixel >= 32 );
1269    }
1270
1271    // Find text offset up to truncatewidth
1272    int getTextTruncateOffset( int truncatewidth, const char *string, int stringlength, int extwidth, int *retextflag, int *retfullwidth )
1273    {
1274      int subpixel, index, advance, truncatewidthext, truncateindex, extflag, fullwidth = 0;
1275      FMState *state;
1276      unichar codepoint;
1277      uint32 utf8state;
1278      FMGlyph *glyph;
1279      int prevGlyphIndex;
1280      int blurradius, blurscale;
1281      FMFont font;
1282
1283      state = &states[ nstates - 1 ];
1284      if( retextflag )
1285        *retextflag = 0;
1286      if( retfullwidth )
1287        *retfullwidth = 0;
1288      if( extwidth >= truncatewidth )
1289        return 0;
1290      glyph = 0;
1291      utf8state = 0;
1292      prevGlyphIndex = -1;
1293      blurradius = state->blurradius;
1294      blurscale = state->blurscale;
1295      if( !( state->font ) )
1296        return 0;
1297      font = state->font;
1298      if( stringlength <= 0 )
1299        stringlength = strlen( string );
1300
1301      truncatewidthext = truncatewidth - extwidth;
1302
1303      advance = 0;
1304      subpixel = 0;
1305      truncateindex = 0;
1306      extflag = 0;
1307      for( index = 0 ; ; index++ )
1308      {
1309        if( index >= stringlength )
1310        {
1311          truncateindex = index;
1312          fullwidth = advance + ( subpixel >= 32 );
1313          break;
1314        }
1315        if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1316          continue;
1317        glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1318        if( glyph )
1319        {
1320          font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1321          addGlyphAdvance( &advance, &subpixel, glyph );
1322          if( advance > truncatewidth )
1323          {
1324            extflag = 1;
1325            break;
1326          }
1327          if( advance <= truncatewidthext )
1328          {
1329            truncateindex = index + 1;
1330            fullwidth = advance + ( subpixel >= 32 );
1331          }
1332        }
1333        prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1334      }
1335
1336      if( retfullwidth )
1337      {
1338        if( extflag )
1339          *retfullwidth = fullwidth;
1340        else
1341          *retfullwidth = fullwidth;
1342      }
1343      if( retextflag )
1344        *retextflag = extflag;
1345
1346      return truncateindex;
1347    }
1348
1349    // Find text offset nearest to the given width
1350    int getTextNearestOffset( int targetwidth, const char *string, int stringlength )
1351    {
1352      int subpixel, index, advance, truncateindex, distance, bestdistance;
1353      FMState *state;
1354      unichar codepoint;
1355      uint32 utf8state;
1356      FMGlyph *glyph;
1357      int prevGlyphIndex;
1358      int blurradius, blurscale;
1359      FMFont font;
1360
1361      state = &states[ nstates - 1 ];
1362      glyph = 0;
1363      utf8state = 0;
1364      prevGlyphIndex = -1;
1365      blurradius = state->blurradius;
1366      blurscale = state->blurscale;
1367      if( !( state->font ) )
1368        return 0;
1369      font = state->font;
1370      if( stringlength <= 0 )
1371        stringlength = strlen( string );
1372
1373      advance = 0;
1374      subpixel = 0;
1375      truncateindex = 0;
1376      bestdistance = abs( targetwidth );
1377      for( index = 0 ; index < stringlength ; index++ )
1378      {
1379        if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1380          continue;
1381        glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1382        if( glyph )
1383        {
1384          font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1385          addGlyphAdvance( &advance, &subpixel, glyph );
1386          distance = abs( targetwidth - ( advance + ( subpixel >= 32 ) ) );
1387          if( distance > bestdistance )
1388            break;
1389          bestdistance = distance;
1390          truncateindex = index + 1;
1391        }
1392        prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1393      }
1394
1395      return truncateindex;
1396    }
1397
1398    ////
1399
1400    static void flush( bool rendererFlush )
1401    {
1402       // Flush texture updates
1403       if( ( dirtyrect[0] < dirtyrect[2] ) && ( dirtyrect[1] < dirtyrect[3] ) )
1404       {
1405          renderer.updateTexture( dirtyrect, texdata );
1406          // Reset dirty rect
1407          dirtyrect[0] = width;
1408          dirtyrect[1] = height;
1409          dirtyrect[2] = 0;
1410          dirtyrect[3] = 0;
1411       }
1412       // Flush buffered glyphs
1413       if( rendererFlush ) renderer.flush( );
1414    }
1415
1416    // Flush buffered texture updates, renderer->updateTexture()
1417    void flushUpdate( )
1418    {
1419       flush(false );
1420    }
1421
1422    ////
1423
1424    // Text metrics
1425
1426    void getFontExtent( int *retascent, int *retdescent )
1427    {
1428      FMFont font;
1429      FMState *state = &states[ nstates - 1 ];
1430      if(state->font)
1431      {
1432          font = state->font;
1433
1434          if( retascent )
1435             *retascent = -(int)ceilf( font.ascender * (float)state->size );
1436          if( retdescent )
1437             *retdescent = -(int)floorf( font.descender * (float)state->size );
1438       }
1439    }
1440
1441    void fmGetFontLimits( int *retlimitminy, int *retlimitmaxy )
1442    {
1443      FMFont font;
1444      FMState *state;
1445
1446      state = &states[ nstates - 1 ];
1447      if( !( state->font ) )
1448        return;
1449      font = state->font;
1450
1451      if( retlimitminy )
1452        *retlimitminy = -(int)ceilf( font.limitmaxy * state->size );
1453      if( retlimitmaxy )
1454        *retlimitmaxy = -(int)floorf( font.limitminy * state->size );
1455
1456      return;
1457    }
1458
1459    int getFontLineHeight( )
1460    {
1461      FMFont font;
1462      FMState *state;
1463
1464      state = &states[ nstates - 1 ];
1465      if( !( state->font ) )
1466        return 0 ;
1467      font = state->font;
1468      return (int)ceilf( font.lineHeight * state->size );
1469    }
1470
1471
1472    ////
1473
1474    // Pull texture changes
1475    const byte *getTextureData( int *width, int *height )
1476    {
1477       if( width )
1478          *width = this.width;
1479       if( height )
1480          *height = this.height;
1481       return texdata;
1482    }
1483
1484    // Retrieve the dirty rectangle, telling fontmanager you will manually perform the update, returns 0 if no part of the texture requires an update
1485    bool validateTexture( int *retdirtyrect )
1486    {
1487       if( ( dirtyrect[0] < dirtyrect[2] ) && ( dirtyrect[1] < dirtyrect[3] ) )
1488       {
1489          retdirtyrect[0] = dirtyrect[0];
1490          retdirtyrect[1] = dirtyrect[1];
1491          retdirtyrect[2] = dirtyrect[2];
1492          retdirtyrect[3] = dirtyrect[3];
1493          // Reset dirty rect
1494          dirtyrect[0] = width;
1495          dirtyrect[1] = height;
1496          dirtyrect[2] = 0;
1497          dirtyrect[3] = 0;
1498          return true;
1499       }
1500       return false;
1501    }
1502
1503    // Returns current atlas size
1504    void getAtlasSize( int *retwidth, int *retheight )
1505    {
1506      *retwidth = this.width;
1507      *retheight = this.height;
1508      return;
1509    }
1510
1511    // Expands the atlas size
1512    bool expandAtlas( int width, int height )
1513    {
1514       width = Max( width, this.width );
1515       height = Max( height, this.height );
1516
1517       if( ( width == this.width ) && ( height == this.height ) )
1518          return true;
1519
1520       // Flush all pending glyphs
1521       flush(true);
1522
1523       // Create new texture
1524       if( renderer.resizeTexture( width, height ) )
1525       {
1526          byte *data;
1527          int i;
1528
1529          // Copy old texture data over.
1530          if( !( data = (byte *)malloc( width * height ) ) )
1531             return false;
1532          for( i = 0 ; i < this.height ; i++ )
1533          {
1534             byte * dst = &data[ i * width ];
1535             byte * src = &this.texdata[ i * this.width ];
1536             memcpy( dst, src, this.width );
1537             if( width > this.width )
1538                memset( dst+this.width, 0, width - this.width );
1539          }
1540          if( height > this.height )
1541             memset( &data[ this.height * width ], 0, ( height - this.height ) * width );
1542
1543          delete this.texdata;
1544          texdata = data;
1545
1546          // Increase atlas size
1547          atlas.expand( width, height );
1548
1549          // Add existing data as dirty.
1550          dirtyrect[0] = 0;
1551          dirtyrect[1] = 0;
1552          dirtyrect[2] = this.width;
1553          dirtyrect[3] = atlas.getAtlasMaxHeight();
1554
1555          this.width = width;
1556          this.height = height;
1557          widthinv = 1.0f / this.width;
1558          heightinv = 1.0f / this.height;
1559
1560          return true;
1561       }
1562       return false;
1563    }
1564
1565    // Reset the whole fm
1566    bool resetAtlas( int width, int height )
1567    {
1568       // Flush all pending glyphs
1569       flush(true);
1570
1571       // Create new texture
1572       if(renderer.resizeTexture( width, height ) )
1573       {
1574          // Reset atlas
1575          atlas.reset( width, height );
1576
1577          // Clear texture data.
1578          texdata = renew texdata byte[width * height];
1579          if(!texdata) return 0;
1580          memset( this.texdata, 0, width * height );
1581
1582          // Reset dirty rect
1583          dirtyrect[0] = width;
1584          dirtyrect[1] = height;
1585          dirtyrect[2] = 0;
1586          dirtyrect[3] = 0;
1587
1588          // Reset cached glyphs
1589          for(font : fontList)
1590          {
1591             int i;
1592             font.glyphcount = 0;
1593             for( i = 0 ; i < FM_HASH_TABLE_SIZE ; i++ )
1594                font.hashtable[i] = -1;
1595          }
1596          renderer.resetImages( );
1597
1598          this.width = width;
1599          this.height = height;
1600          this.widthinv = 1.0f / this.width;
1601          this.heightinv = 1.0f / this.height;
1602
1603          // Add white rect at 0,0 for debug drawing.
1604       #if FM_DEBUG_WHITERECT
1605          fmAddWhiteRect(2, 2 );
1606       #endif
1607
1608          return true;
1609       }
1610       return false;
1611    }
1612
1613    ////
1614    bool initPathDraw( FMPathDraw pathdraw )
1615    {
1616      FMState *state = &states[ nstates - 1 ];
1617      FMFont font = state->font;
1618      if(font)
1619      {
1620         pathdraw.prevGlyphIndex = -1;
1621         pathdraw.middleAlign = font.middleAlign * (float)state->size;
1622         return true;
1623      }
1624      return false;
1625    }
1626
1627    float pathDrawCharacter( FMPathDraw pathdraw, float x, float y, float vectorx, float vectory, int unicode )
1628    {
1629       int subpixel;
1630       FMState *state = &states[ nstates - 1 ];
1631       int blurradius = state->blurradius;
1632       int blurscale = state->blurscale;
1633       FMFont font = state->font;
1634       FMGlyph *glyph = getGlyph(font, unicode, state->size, 0, blurradius, blurscale );
1635       if( glyph )
1636       {
1637          subpixel = font.ftFont.getGlyphKernAdvance( pathdraw.prevGlyphIndex, glyph->glyphindex );
1638          drawTextGlyphFloat(font, glyph, x, y, vectorx, vectory, (float)subpixel * (1.0f/64.0f), pathdraw.middleAlign );
1639          subpixel += glyph->advance;
1640          pathdraw.prevGlyphIndex = glyph->glyphindex;
1641       }
1642       else
1643       {
1644          subpixel = 0;
1645          pathdraw.prevGlyphIndex = -1;
1646       }
1647
1648       return (float)subpixel * (1.0f/64.0f);
1649    }
1650
1651    float pathDrawPredictAdvance( FMPathDraw pathdraw, unichar unicode )
1652    {
1653       int subpixel = 0;
1654       FMState *state = &states[ nstates - 1 ];
1655       FMFont font = state->font;
1656       int blurradius = state->blurradius;
1657       int blurscale = state->blurscale;
1658       FMGlyph *glyph = getGlyph(font, unicode, state->size, 0, blurradius, blurscale );
1659       if( glyph )
1660       {
1661          subpixel = font.ftFont.getGlyphKernAdvance( pathdraw.prevGlyphIndex, glyph->glyphindex );
1662          subpixel += glyph->advance;
1663       }
1664       return (float)subpixel * (1.0f/64.0f);
1665    }
1666 }