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