f84f689395c29c37d9e27d5b7bca662484275b97
[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 #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 FMVerticalAlignment { baseline, top, middle, bottom };
408
409 public class FMTextAlignment : uint16
410 {
411 public:
412    Alignment horzAlignment:2;
413    FMVerticalAlignment 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    }
907
908    void setFont( FMFont font )
909    {
910      states[ nstates - 1 ].font = font;
911    }
912
913    void setSize( int size )
914    {
915      if( size >= FM_SIZE_MAX )
916        size = FM_SIZE_MAX;
917
918      states[ nstates - 1 ].size = (uint16)size;
919    }
920
921    void setAlign( int align )
922    {
923      states[ nstates - 1 ].align = (uint16)align;
924    }
925
926    void setBlur( int radius, int scale )
927    {
928      if( radius >= FM_BLUR_RADIUS_MAX )
929        radius = FM_BLUR_RADIUS_MAX;
930      else if( radius < 0 )
931        radius = 0;
932      if( scale >= FM_BLUR_SCALE_MAX )
933        scale = FM_BLUR_SCALE_MAX;
934      else if( scale < 1 )
935        scale = 1;
936      states[ nstates - 1 ].blurradius = (uint16)radius;
937      states[ nstates - 1 ].blurscale = (uint16)scale;
938    }
939
940    // Set image manipuation callback
941    void setFontImageProcessing( FMFont font, void (*processImage)( byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingwidth, void *opaquecontext ), void *opaquecontext )
942    {
943
944    }
945
946    // State handling
947    void pushState( )
948    {
949       if(nstates < FM_MAX_STATES)
950       {
951          if( nstates > 0 )
952             memcpy( &states[nstates], &states[nstates-1], sizeof(FMState) );
953          nstates++;
954       }
955    }
956
957    void popState()
958    {
959       if(nstates > 1)
960          nstates--;
961    }
962
963    void clearState()
964    {
965      FMState *state = &states[ nstates - 1 ];
966      state->size = 12;
967      state->font = 0;
968      state->blurradius = 0;
969      state->blurscale = 1;
970      state->align = { left, baseline };
971    }
972
973
974    ////
975
976    static void freeFont(FMFont font)
977    {
978      if(font)
979      {
980         fontList.Remove((IteratorPointer)font);
981         delete font;
982      }
983    }
984
985    // Add font from file
986    FMFont addFont(const String path, int glyphPaddingWidth )
987    {
988       FMFont font = null;
989       File f = FileOpen(path, read);
990       if(f)
991       {
992          // Read in the font data
993          int dataSize = f.GetSize();
994          byte *data = new byte[dataSize];
995          if(data)
996          {
997             f.Read(data, 1, dataSize);
998             font = addFontData(data, dataSize, glyphPaddingWidth);
999             if(!font)
1000                delete data;
1001          }
1002          delete f;
1003       }
1004       return font;
1005    }
1006
1007    // Add font from data ; do not free( data ), the font manager will do that when removing the font
1008    FMFont addFontData( byte *data, int dataSize, int glyphPaddingWidth )
1009    {
1010      FMFont font
1011      {
1012         glyphs = new FMGlyph[FM_INIT_GLYPHS];
1013         glyphalloc = FM_INIT_GLYPHS;
1014         glyphPaddingWidth = glyphPaddingWidth;
1015      };
1016      if(font)
1017      {
1018          int i;
1019          float ascent, descent, fontHeight;
1020
1021          fontList.Add(font);
1022
1023          // Init hash lookup
1024          for( i = 0 ; i < FM_HASH_TABLE_SIZE ; i++ )
1025             font.hashtable[i] = -1;
1026
1027          // Init font
1028          if(!font.ftFont.loadFont(data, dataSize))
1029          {
1030             freeFont(font );
1031             return 0;
1032          }
1033
1034          // Store normalized line height. The real line height is got by multiplying by font size.
1035          font.ftFont.getFontVMetrics( &ascent, &descent, &font.lineHeight, &font.limitminy, &font.limitmaxy );
1036          fontHeight = ascent - descent;
1037          font.ascender = ascent / fontHeight;
1038          font.descender = descent / fontHeight;
1039          font.middleAlign = 0.5f * ( font.ascender + font.descender );
1040          font.fontdata = data;
1041       }
1042       return font;
1043    }
1044
1045    // Free font
1046    void removeFont( FMFont font )
1047    {
1048       freeFont(font );
1049    }
1050
1051    // Draw text
1052    int drawText( int x, int y, const char *string, int stringlength )
1053    {
1054      int subpixel, index;
1055      FMState *state;
1056      unichar codepoint;
1057      uint32 utf8state;
1058      FMGlyph *glyph;
1059      int prevGlyphIndex;
1060      int blurradius, blurscale;
1061      FMFont font;
1062
1063      state = &states[ nstates - 1 ];
1064      glyph = 0;
1065      utf8state = 0;
1066      prevGlyphIndex = -1;
1067      blurradius = state->blurradius;
1068      blurscale = state->blurscale;
1069      if( !( state->font ) )
1070        return x;
1071      font = state->font;
1072      if( !( stringlength ) )
1073        stringlength = strlen( string );
1074
1075      // Align horizontally
1076      if( state->align.horzAlignment == right )
1077        x -= getTextWidth(string, stringlength );
1078      else if( state->align.horzAlignment == center )
1079        x -= getTextWidth(string, stringlength ) >> 1;
1080
1081      // Align vertically
1082      y += roundf( font.getVertAlign(state->align, state->size ) );
1083
1084      subpixel = 0;
1085      for( index = 0 ; index < stringlength ; index++ )
1086      {
1087        if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1088          continue;
1089        glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1090        if( glyph )
1091        {
1092          font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1093          drawTextGlyph(font, glyph, x, y );
1094          addGlyphAdvance( &x, &subpixel, glyph );
1095        }
1096        prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1097      }
1098
1099      return x + ( subpixel >= 32 );
1100    }
1101
1102    int drawTextWithCursor( int x, int y, const char *string, int stringlength, int cursoroffset )
1103    {
1104      int subpixel, index;
1105      FMState *state;
1106      unichar codepoint;
1107      uint32 utf8state;
1108      FMGlyph *glyph;
1109      int prevGlyphIndex;
1110      int blurradius, blurscale;
1111      FMFont font;
1112
1113      state = &states[ nstates - 1 ];
1114      glyph = 0;
1115      utf8state = 0;
1116      prevGlyphIndex = -1;
1117      blurradius = state->blurradius;
1118      blurscale = state->blurscale;
1119      if( !( state->font ) )
1120        return x;
1121      font = state->font;
1122      if( !( stringlength ) )
1123        stringlength = strlen( string );
1124
1125      // Align horizontally
1126      if( state->align.horzAlignment == right )
1127        x -= getTextWidth(string, stringlength );
1128      else if( state->align.horzAlignment == center )
1129        x -= getTextWidth(string, stringlength ) >> 1;
1130
1131      // Align vertically
1132      y += roundf( font.getVertAlign(state->align, state->size ) );
1133
1134      subpixel = 0;
1135      for( index = 0 ; ; index++ )
1136      {
1137        if( index == cursoroffset )
1138        {
1139          glyph = getGlyph(font, FM_GLYPH_CODEPOINT_CURSOR, state->size, subpixel, blurradius, blurscale );
1140          if( glyph )
1141            drawTextCursorGlyph(font, glyph, x, y );
1142        }
1143        if( index >= stringlength )
1144          break;
1145        if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1146          continue;
1147        glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1148        if( glyph )
1149        {
1150          font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1151          drawTextGlyph(font, glyph, x, y );
1152          addGlyphAdvance( &x, &subpixel, glyph );
1153        }
1154        prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1155      }
1156
1157      return x + ( subpixel >= 32 );
1158    }
1159
1160    int drawTextTruncate( int x, int y, int truncatewidth, const char *string, int stringlength, char *extstring, int extwidth )
1161    {
1162      int subpixel, index, textwidth, truncatepoint;
1163      FMState *state;
1164      unichar codepoint;
1165      uint32 utf8state;
1166      FMGlyph *glyph;
1167      int prevGlyphIndex;
1168      int blurradius, blurscale;
1169      FMFont font;
1170
1171      state = &states[ nstates - 1 ];
1172      glyph = 0;
1173      utf8state = 0;
1174      prevGlyphIndex = -1;
1175      blurradius = state->blurradius;
1176      blurscale = state->blurscale;
1177      if( !( state->font ) )
1178        return x;
1179      font = state->font;
1180      if( !( stringlength ) )
1181        stringlength = strlen( string );
1182      textwidth = getTextWidthTruncate(string, stringlength, truncatewidth );
1183
1184      truncatepoint = x + truncatewidth;
1185      if( textwidth <= truncatewidth )
1186        extstring = 0;
1187      else
1188      {
1189        if( extwidth >= truncatewidth )
1190          return x;
1191        truncatepoint -= extwidth;
1192      }
1193
1194      // Align horizontally
1195      if( state->align.horzAlignment == right )
1196        x -= textwidth;
1197      else if( state->align.horzAlignment == center )
1198        x -= textwidth >> 1;
1199
1200      // Align vertically
1201      y += roundf( font.getVertAlign(state->align, state->size ) );
1202
1203      subpixel = 0;
1204      for( index = 0 ; index < stringlength ; index++ )
1205      {
1206        if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1207          continue;
1208        glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1209        if( glyph )
1210        {
1211          font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1212          drawTextGlyph(font, glyph, x, y );
1213          addGlyphAdvance( &x, &subpixel, glyph );
1214          if( x > truncatepoint )
1215            break;
1216        }
1217        prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1218      }
1219      x += ( subpixel >= 32 );
1220      if( extstring )
1221        drawText(x, y, extstring, 0 );
1222
1223      return x;
1224    }
1225
1226
1227    ////
1228
1229
1230    // Measure text
1231    int getTextWidth( const char *string, int stringlength )
1232    {
1233      return getTextWidthTruncate(string, stringlength, ( (unsigned)1 << ( ( sizeof(int) * CHAR_BIT ) - 1 ) ) - 1 );
1234    }
1235
1236
1237    int getTextWidthTruncate( const char *string, int stringlength, int truncatewidth )
1238    {
1239      int subpixel, index, advance;
1240      FMState *state;
1241      unichar codepoint;
1242      uint32 utf8state;
1243      FMGlyph *glyph;
1244      int prevGlyphIndex;
1245      int blurradius, blurscale;
1246      FMFont font;
1247
1248      state = &states[ nstates - 1 ];
1249      glyph = 0;
1250      utf8state = 0;
1251      prevGlyphIndex = -1;
1252      blurradius = state->blurradius;
1253      blurscale = state->blurscale;
1254      if( !( state->font ) )
1255        return 0;
1256      font = state->font;
1257      if( !( stringlength ) )
1258        stringlength = strlen( string );
1259
1260      advance = 0;
1261      subpixel = 0;
1262      for( index = 0 ; index < stringlength ; index++ )
1263      {
1264        if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1265          continue;
1266        glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1267        if( glyph )
1268        {
1269          font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1270          addGlyphAdvance( &advance, &subpixel, glyph );
1271          if( advance > truncatewidth )
1272            break;
1273        }
1274        prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1275      }
1276
1277      return advance + ( subpixel >= 32 );
1278    }
1279
1280    int getTextBounds( int x, int y, const char *string, int stringlength, int *bounds )
1281    {
1282      int subpixel, index;
1283      FMState *state;
1284      unichar codepoint;
1285      uint32 utf8state;
1286      FMQuad q;
1287      FMGlyph *glyph;
1288      int prevGlyphIndex;
1289      int blurradius, blurscale;
1290      FMFont font;
1291      int startx, advance;
1292      int minx, miny, maxx, maxy;
1293
1294      state = &states[ nstates - 1 ];
1295      glyph = 0;
1296      utf8state = 0;
1297      prevGlyphIndex = -1;
1298      blurradius = state->blurradius;
1299      blurscale = state->blurscale;
1300      if( !( state->font ) )
1301        return x;
1302      font = state->font;
1303      if( !( stringlength ) )
1304        stringlength = strlen( string );
1305
1306      // Align vertically
1307      y += font.getVertAlign(state->align, state->size );
1308
1309      minx = maxx = x;
1310      miny = maxy = y;
1311      startx = x;
1312
1313      subpixel = 0;
1314      for( index = 0 ; index < stringlength ; index++ )
1315      {
1316        if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1317          continue;
1318        glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1319        if( glyph )
1320        {
1321          font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1322          glyph->getQuad(x, y, q);
1323          if( q.x0 < minx )
1324            minx = q.x0;
1325          if( q.x1 > maxx )
1326            maxx = q.x1;
1327          if( q.y0 < miny )
1328            miny = q.y0;
1329          if( q.y1 > maxy )
1330            maxy = q.y1;
1331          addGlyphAdvance( &x, &subpixel, glyph );
1332        }
1333        prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1334      }
1335
1336      advance = x - startx;
1337
1338      /* Align horizontally */
1339      if( state->align.horzAlignment == right )
1340      {
1341        minx -= advance;
1342        maxx -= advance;
1343      }
1344      else if( state->align.horzAlignment == center )
1345      {
1346        minx -= advance * 0.5f;
1347        maxx -= advance * 0.5f;
1348      }
1349
1350      if( bounds )
1351      {
1352        bounds[0] = minx;
1353        bounds[1] = maxx;
1354        bounds[2] = miny;
1355        bounds[3] = maxy;
1356      }
1357
1358      return advance + ( subpixel >= 32 );
1359    }
1360
1361    // Find text offset up to truncatewidth
1362    int getTextTruncateOffset( int truncatewidth, const char *string, int stringlength, int extwidth, int *retextflag, int *retfullwidth )
1363    {
1364      int subpixel, index, advance, truncatewidthext, truncateindex, extflag, fullwidth = 0;
1365      FMState *state;
1366      unichar codepoint;
1367      uint32 utf8state;
1368      FMGlyph *glyph;
1369      int prevGlyphIndex;
1370      int blurradius, blurscale;
1371      FMFont font;
1372
1373      state = &states[ nstates - 1 ];
1374      if( retextflag )
1375        *retextflag = 0;
1376      if( retfullwidth )
1377        *retfullwidth = 0;
1378      if( extwidth >= truncatewidth )
1379        return 0;
1380      glyph = 0;
1381      utf8state = 0;
1382      prevGlyphIndex = -1;
1383      blurradius = state->blurradius;
1384      blurscale = state->blurscale;
1385      if( !( state->font ) )
1386        return 0;
1387      font = state->font;
1388      if( stringlength <= 0 )
1389        stringlength = strlen( string );
1390
1391      truncatewidthext = truncatewidth - extwidth;
1392
1393      advance = 0;
1394      subpixel = 0;
1395      truncateindex = 0;
1396      extflag = 0;
1397      for( index = 0 ; ; index++ )
1398      {
1399        if( index >= stringlength )
1400        {
1401          truncateindex = index;
1402          fullwidth = advance + ( subpixel >= 32 );
1403          break;
1404        }
1405        if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1406          continue;
1407        glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1408        if( glyph )
1409        {
1410          font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1411          addGlyphAdvance( &advance, &subpixel, glyph );
1412          if( advance > truncatewidth )
1413          {
1414            extflag = 1;
1415            break;
1416          }
1417          if( advance <= truncatewidthext )
1418          {
1419            truncateindex = index + 1;
1420            fullwidth = advance + ( subpixel >= 32 );
1421          }
1422        }
1423        prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1424      }
1425
1426      if( retfullwidth )
1427      {
1428        if( extflag )
1429          *retfullwidth = fullwidth;
1430        else
1431          *retfullwidth = fullwidth;
1432      }
1433      if( retextflag )
1434        *retextflag = extflag;
1435
1436      return truncateindex;
1437    }
1438
1439    // Find text offset nearest to the given width
1440    int getTextNearestOffset( int targetwidth, const char *string, int stringlength )
1441    {
1442      int subpixel, index, advance, truncateindex, distance, bestdistance;
1443      FMState *state;
1444      unichar codepoint;
1445      uint32 utf8state;
1446      FMGlyph *glyph;
1447      int prevGlyphIndex;
1448      int blurradius, blurscale;
1449      FMFont font;
1450
1451      state = &states[ nstates - 1 ];
1452      glyph = 0;
1453      utf8state = 0;
1454      prevGlyphIndex = -1;
1455      blurradius = state->blurradius;
1456      blurscale = state->blurscale;
1457      if( !( state->font ) )
1458        return 0;
1459      font = state->font;
1460      if( stringlength <= 0 )
1461        stringlength = strlen( string );
1462
1463      advance = 0;
1464      subpixel = 0;
1465      truncateindex = 0;
1466      bestdistance = abs( targetwidth );
1467      for( index = 0 ; index < stringlength ; index++ )
1468      {
1469        if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1470          continue;
1471        glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1472        if( glyph )
1473        {
1474          font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1475          addGlyphAdvance( &advance, &subpixel, glyph );
1476          distance = abs( targetwidth - ( advance + ( subpixel >= 32 ) ) );
1477          if( distance > bestdistance )
1478            break;
1479          bestdistance = distance;
1480          truncateindex = index + 1;
1481        }
1482        prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1483      }
1484
1485      return truncateindex;
1486    }
1487
1488    ////
1489
1490    static void flush( bool rendererFlush )
1491    {
1492       // Flush texture updates
1493       if( ( dirtyrect[0] < dirtyrect[2] ) && ( dirtyrect[1] < dirtyrect[3] ) )
1494       {
1495          renderer.updateTexture( dirtyrect, texdata );
1496          // Reset dirty rect
1497          dirtyrect[0] = width;
1498          dirtyrect[1] = height;
1499          dirtyrect[2] = 0;
1500          dirtyrect[3] = 0;
1501       }
1502       // Flush buffered glyphs
1503       if( rendererFlush ) renderer.flush( );
1504    }
1505
1506    // Flush buffered texture updates, renderer->updateTexture()
1507    void flushUpdate( )
1508    {
1509       flush(false );
1510    }
1511
1512    ////
1513
1514    // Text metrics
1515
1516    void getFontExtent( int *retascent, int *retdescent )
1517    {
1518      FMFont font;
1519      FMState *state = &states[ nstates - 1 ];
1520      if(state->font)
1521      {
1522          font = state->font;
1523
1524          if( retascent )
1525             *retascent = -(int)ceilf( font.ascender * (float)state->size );
1526          if( retdescent )
1527             *retdescent = -(int)floorf( font.descender * (float)state->size );
1528       }
1529    }
1530
1531    void getFontLimits( int *retlimitminy, int *retlimitmaxy )
1532    {
1533      FMFont font;
1534      FMState *state;
1535
1536      state = &states[ nstates - 1 ];
1537      if( !( state->font ) )
1538        return;
1539      font = state->font;
1540
1541      if( retlimitminy )
1542        *retlimitminy = -(int)ceilf( font.limitmaxy * state->size );
1543      if( retlimitmaxy )
1544        *retlimitmaxy = -(int)floorf( font.limitminy * state->size );
1545
1546      return;
1547    }
1548
1549    int getFontLineHeight( )
1550    {
1551      FMFont font;
1552      FMState *state;
1553
1554      state = &states[ nstates - 1 ];
1555      if( !( state->font ) )
1556        return 0 ;
1557      font = state->font;
1558      return (int)ceilf( font.lineHeight * state->size );
1559    }
1560
1561
1562    ////
1563
1564    // Pull texture changes
1565    const byte *getTextureData( int *width, int *height )
1566    {
1567       if( width )
1568          *width = this.width;
1569       if( height )
1570          *height = this.height;
1571       return texdata;
1572    }
1573
1574    // Retrieve the dirty rectangle, telling fontmanager you will manually perform the update, returns 0 if no part of the texture requires an update
1575    bool validateTexture( int *retdirtyrect )
1576    {
1577       if( ( dirtyrect[0] < dirtyrect[2] ) && ( dirtyrect[1] < dirtyrect[3] ) )
1578       {
1579          retdirtyrect[0] = dirtyrect[0];
1580          retdirtyrect[1] = dirtyrect[1];
1581          retdirtyrect[2] = dirtyrect[2];
1582          retdirtyrect[3] = dirtyrect[3];
1583          // Reset dirty rect
1584          dirtyrect[0] = width;
1585          dirtyrect[1] = height;
1586          dirtyrect[2] = 0;
1587          dirtyrect[3] = 0;
1588          return true;
1589       }
1590       return false;
1591    }
1592
1593    // Returns current atlas size
1594    void getAtlasSize( int *retwidth, int *retheight )
1595    {
1596      *retwidth = this.width;
1597      *retheight = this.height;
1598      return;
1599    }
1600
1601    // Expands the atlas size
1602    bool expandAtlas( int width, int height )
1603    {
1604       width = Max( width, this.width );
1605       height = Max( height, this.height );
1606
1607       if( ( width == this.width ) && ( height == this.height ) )
1608          return true;
1609
1610       // Flush all pending glyphs
1611       flush(true);
1612
1613       // Create new texture
1614       if( renderer.resizeTexture( width, height ) )
1615       {
1616          byte *data;
1617          int i;
1618
1619          // Copy old texture data over.
1620          if( !( data = new byte[width * bytesperline] ) )
1621             return false;
1622          for( i = 0 ; i < this.height ; i++ )
1623          {
1624             byte * dst = &data[ (i * width) * bytesperpixel ];
1625             byte * src = &this.texdata[ i * this.bytesperline ];
1626             memcpy( dst, src, bytesperline);
1627             if( width > this.width )
1628                memset( dst+bytesperline, 0, (width - this.width) * bytesperpixel );
1629          }
1630          if( height > this.height )
1631             memset( &data[ width * this.height * bytesperpixel], 0, ( height - this.height ) *bytesperline );
1632
1633          delete this.texdata;
1634          texdata = data;
1635
1636          // Increase atlas size
1637          atlas.expand( width, height );
1638
1639          // Add existing data as dirty.
1640          dirtyrect[0] = 0;
1641          dirtyrect[1] = 0;
1642          dirtyrect[2] = this.width;
1643          dirtyrect[3] = atlas.getAtlasMaxHeight();
1644
1645          this.width = width;
1646          this.height = height;
1647          this.bytesperline = this.width * bytesperpixel;
1648          widthinv = 1.0f / this.width;
1649          heightinv = 1.0f / this.height;
1650
1651          return true;
1652       }
1653       return false;
1654    }
1655
1656    // Reset the whole fm
1657    bool resetAtlas( int width, int height )
1658    {
1659       // Flush all pending glyphs
1660       flush(true);
1661
1662       // Create new texture
1663       if(renderer.resizeTexture( width, height ) )
1664       {
1665          // Reset atlas
1666          atlas.reset( width, height );
1667
1668          // Clear texture data.
1669          texdata = renew texdata byte[width * height * bytesperpixel];
1670          if(!texdata) return 0;
1671          memset( this.texdata, 0, width * height );
1672
1673          // Reset dirty rect
1674          dirtyrect[0] = width;
1675          dirtyrect[1] = height;
1676          dirtyrect[2] = 0;
1677          dirtyrect[3] = 0;
1678
1679          // Reset cached glyphs
1680          for(font : fontList)
1681          {
1682             int i;
1683             font.glyphcount = 0;
1684             for( i = 0 ; i < FM_HASH_TABLE_SIZE ; i++ )
1685                font.hashtable[i] = -1;
1686          }
1687          renderer.resetImages( );
1688
1689          this.width = width;
1690          this.height = height;
1691          this.bytesperline = width * bytesperpixel;
1692          this.widthinv = 1.0f / this.width;
1693          this.heightinv = 1.0f / this.height;
1694
1695          return true;
1696       }
1697       return false;
1698    }
1699
1700    ////
1701    bool initPathDraw( FMPathDraw pathdraw )
1702    {
1703      FMState *state = &states[ nstates - 1 ];
1704      FMFont font = state->font;
1705      if(font)
1706      {
1707         pathdraw.prevGlyphIndex = -1;
1708         pathdraw.middleAlign = font.middleAlign * (float)state->size;
1709         return true;
1710      }
1711      return false;
1712    }
1713
1714    float pathDrawCharacter( FMPathDraw pathdraw, float x, float y, float vectorx, float vectory, int unicode )
1715    {
1716       int subpixel;
1717       FMState *state = &states[ nstates - 1 ];
1718       int blurradius = state->blurradius;
1719       int blurscale = state->blurscale;
1720       FMFont font = state->font;
1721       FMGlyph *glyph = getGlyph(font, unicode, state->size, 0, blurradius, blurscale );
1722       if( glyph )
1723       {
1724          subpixel = font.ftFont.getGlyphKernAdvance( pathdraw.prevGlyphIndex, glyph->glyphindex );
1725          drawTextGlyphFloat(font, glyph, x, y, vectorx, vectory, (float)subpixel * (1.0f/64.0f), pathdraw.middleAlign );
1726          subpixel += glyph->advance;
1727          pathdraw.prevGlyphIndex = glyph->glyphindex;
1728       }
1729       else
1730       {
1731          subpixel = 0;
1732          pathdraw.prevGlyphIndex = -1;
1733       }
1734
1735       return (float)subpixel * (1.0f/64.0f);
1736    }
1737
1738    float pathDrawPredictAdvance( FMPathDraw pathdraw, unichar unicode )
1739    {
1740       int subpixel = 0;
1741       FMState *state = &states[ nstates - 1 ];
1742       FMFont font = state->font;
1743       int blurradius = state->blurradius;
1744       int blurscale = state->blurscale;
1745       FMGlyph *glyph = getGlyph(font, unicode, state->size, 0, blurradius, blurscale );
1746       if( glyph )
1747       {
1748          subpixel = font.ftFont.getGlyphKernAdvance( pathdraw.prevGlyphIndex, glyph->glyphindex );
1749          subpixel += glyph->advance;
1750       }
1751       return (float)subpixel * (1.0f/64.0f);
1752    }
1753 }