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