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