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