1 /* *****************************************************************************
2 * Original Version Copyright (c) 2007-2014 Alexis Naveros.
4 * Ecere Corporation has unlimited/unrestricted rights.
5 * *****************************************************************************/
16 static inline uint32 decodeUTF8( uint32 b, uint32 *state, unichar *retCodePoint ) { return ccUtf8ToUnicode(b, state, (uint *)retCodePoint); }
20 // Based on Exponential blur, Jani Huhtanen, 2006
25 static void blurCols( byte *dst, int w, int h, int stride, int alpha )
28 for( y = 0 ; y < h ; y++ )
31 for( x = 1 ; x < w ; x++ )
33 z += ( alpha * ( ( (int)( dst[x] ) << ZPREC ) - z ) ) >> APREC;
34 dst[x] = (byte)( z >> ZPREC );
36 for( x = w-2 ; x >= 0 ; x-- )
38 z += ( alpha * ( ( (int)( dst[x] ) << ZPREC ) - z ) ) >> APREC;
39 dst[x] = (byte)( z >> ZPREC );
45 static void blurRows( byte *dst, int w, int h, int stride, int alpha )
48 for( x = 0 ; x < w ; x++ )
51 for( y = stride ; y < h*stride ; y += stride )
53 z += ( alpha * ( ( (int)( dst[y] ) << ZPREC ) - z ) ) >> APREC;
54 dst[y] = (byte)( z >> ZPREC );
56 for( y = (h-2)*stride ; y >= 0 ; y -= stride )
58 z += ( alpha * ( ( (int)( dst[y] ) << ZPREC ) - z ) ) >> APREC;
59 dst[y] = (byte)( z >> ZPREC );
65 static void blur( byte *dst, int width, int height, int stride, int radius )
70 // Calculate the alpha such that 90% of the kernel is within the radius. (Kernel extends to infinity)
71 float radiusrange = (float)radius * ( 1.0f / sqrtf( 3 ) );
72 uint32 alpha = (uint32)roundf( ( 1 << APREC ) * ( 1.0f - expf( -2.3f / ( radiusrange + 1.0f ) ) ) );
73 for( ; boxcount ; boxcount-- )
75 blurRows( dst, width, height, stride, alpha );
76 blurCols( dst, width, height, stride, alpha );
87 #define SPREC (31-(APREC+ZPREC+8))
89 static void blurCols32( int *dst, int w, int h, int stride, int alpha )
92 for( y = 0 ; y < h ; y++ )
95 for( x = 1 ; x < w ; x++ )
97 z += ( alpha * ( dst[x] - z ) ) >> APREC;
100 for( x = w - 2 ; x >= 0 ; x-- )
102 z += ( alpha * ( dst[x] - z ) ) >> APREC;
109 static void blurRows32( int *dst, int w, int h, int stride, int alpha )
112 for( x = 0 ; x < w ; x++ )
115 for( y = 1 * stride ; y < h * stride ; y += stride )
117 z += ( alpha * ( dst[y] - z ) ) >> APREC;
120 for( y = ( h - 2 ) * stride ; y >= 0 ; y -= stride )
122 z += ( alpha * ( dst[y] - z ) ) >> APREC;
129 static void blurScale( byte *dst, int width, int height, int stride, int radius, int scale )
136 int *buffer, *bufferrow;
139 if( scale > ( 1 << SPREC ) )
142 buffer = malloc( width * height * sizeof(int) );
143 for( y = 0 ; y < height ; y++ )
145 dstrow = &dst[y*stride];
146 bufferrow = &buffer[y*width];
147 for( x = 0 ; x < width ; x++ )
148 bufferrow[x] = ( (int)dstrow[x] << ZPREC ) * scale;
153 /* Calculate the alpha such that 90% of the kernel is within the radius. (Kernel extends to infinity) */
154 radiusrange = (float)radius * 0.3f;
155 alpha = (uint32)roundf( ( 1 << APREC ) * ( 1.0f - expf( -2.3f / ( radiusrange + 1.0f ) ) ) );
156 for( ; boxcount ; boxcount-- )
158 blurRows32( buffer, width, height, width, alpha );
159 blurCols32( buffer, width, height, width, alpha );
162 for( y = 0 ; y < height ; y++ )
164 dstrow = &dst[y*stride];
165 bufferrow = &buffer[y*width];
166 for( x = 0 ; x < width ; x++ )
167 dstrow[x] = (byte)Min(255, bufferrow[x] >> ZPREC);
180 static void buildCursorGlyph( byte *dst, int glyphwidth, int glyphheight, int dststride )
182 int x, y, hbarheight, hgap, hline, vline;
185 if( glyphwidth >= 3 )
187 hbarheight = 1 + ( glyphheight >> 6 );
188 hgap = ( glyphwidth - ( 1 + ( glyphheight >> 6 ) ) ) >> 1;
189 hline = hgap + glyphwidth - ( hgap << 1 );
190 vline = glyphheight - ( hbarheight << 1 );
191 for( y = 0 ; y < hbarheight ; y++ )
194 for( x = 0 ; x < glyphwidth ; x++ )
198 for( y = 0 ; y < vline ; y++ )
201 for( x = 0 ; x < hgap ; x++ )
203 for( ; x < hline ; x++ )
205 for( ; x < glyphwidth ; x++ )
209 for( y = 0 ; y < hbarheight ; y++ )
212 for( x = 0 ; x < glyphwidth ; x++ )
219 for( y = 0 ; y < glyphheight ; y++ )
222 for( x = 0 ; x < glyphwidth ; x++ )
231 static inline void addGlyphAdvance( int *x, int *subpixel, FMGlyph *glyph )
233 *subpixel += glyph->advance;
234 #if FM_SUBPIXEL_ROUNDING_RANGE
235 *subpixel = ( *subpixel + (FM_SUBPIXEL_ROUNDING_RANGE>>1) ) & ~(FM_SUBPIXEL_ROUNDING_RANGE-1);
237 *x += *subpixel >> 6;
241 public class FontManagerRenderer
248 // Create, resize or update texture data ; rect is [minx,maxx,miny,maxy]
249 virtual int createTexture( int width, int height );
250 virtual int resizeTexture( int width, int height );
251 virtual void updateTexture( int *rect, const byte *data );
253 // If the renderer does any buffering of drawImage(), flush it all right now (texture may change immediately after)
254 virtual void flush( );
256 // If the renderer records per-image data, return an imageIndex passed to drawImage()
257 virtual int registerImage( int offsetx, int offsety, int width, int height );
258 // Draw an image, imageIndex passed as the value previously returned by registerImage()
259 virtual void drawImage( int targetx, int targety, int imageIndex );
260 // Draw an image, called instead of drawImage() for text cursors, can point to exactly the same function
261 virtual void drawImageCursor( int targetx, int targety, int imageIndex );
262 // If drawImage is zero, then this alternate function is called, passing everything required to render the glyph
263 virtual void drawImageAlt( byte *texdata, int targetx, int targety, int offsetx, int offsety, int width, int height );
264 // Draw a non-aligned image, imageIndex passed as the value previously returned by registerImage()
265 virtual void drawImageFloat( float targetx, float targety, float angsin, float angcos, int imageIndex );
267 // The renderer must flush all recorded images, registerImage() will be called for new images
268 virtual void resetImages( );
275 #include <ft2build.h>
277 #include FT_FREETYPE_H
278 #include FT_ADVANCES_H
284 #define FM_SUPPORT_GLYPH_ROTATION (1)
290 #define FM_ENABLE_HINTING (1)
291 #define FM_SUBPIXEL_ROUNDING_RANGE (16)
292 #if FM_SUPPORT_GLYPH_ROTATION
293 #define FM_TEXTURE_PADDING (1)
295 #define FM_TEXTURE_PADDING (0)
297 #define FM_DEBUG_WHITERECT (0)
300 #define FM_HASH_TABLE_SIZE (4096)
301 #define FM_INIT_GLYPHS (1024)
302 #define FM_INIT_ATLAS_NODES (512)
304 #define FM_MAX_STATES (16)
309 struct FMFreeTypeFont
313 static inline int ::init()
316 ftError = FT_Init_FreeType( &ftLibrary2 );
320 static inline int loadFont( byte *data, int dataSize )
322 FT_Error ftError = FT_New_Memory_Face( ftLibrary2, (const FT_Byte*)data, dataSize, 0, &face );
328 FT_Done_Face( face );
331 static inline void getFontVMetrics( float *ascent, float *descent, float *lineHeight, float *limitminy, float *limitmaxy )
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;
340 static inline int getGlyphIndex( unichar codepoint )
342 return FT_Get_Char_Index( this.face, codepoint );
345 static int buildGlyphBitmap( int glyph, int size, int subpixel, int *advance, int *x0, int *y0, int *x1, int *y1 )
348 FT_GlyphSlot glyphslot;
351 ftError = FT_Set_Pixel_Sizes( this.face, 0, (FT_UInt)size );
355 subvector.x = subpixel;
357 FT_Set_Transform( face, 0, &subvector );
359 #if FM_ENABLE_HINTING
360 ftError = FT_Load_Glyph( face, glyph, FT_LOAD_RENDER );
362 ftError = FT_Load_Glyph( face, glyph, FT_LOAD_RENDER | FT_LOAD_NO_HINTING );
367 glyphslot = face->glyph;
368 *advance = 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;
377 static void copyGlyphBitmap( byte *dst, int glyphwidth, int glyphheight, int dststride, int glyphindex )
381 FT_GlyphSlot glyphslot;
383 glyphslot = face->glyph;
384 src = glyphslot->bitmap.buffer;
385 for( y = 0 ; y < glyphslot->bitmap.rows ; y++ )
387 dstrow = &dst[ y * dststride ];
388 for( x = 0 ; x < glyphslot->bitmap.width ; x++ )
389 dstrow[ x ] = src[ x ];
390 src += glyphslot->bitmap.width;
395 static inline int getGlyphKernAdvance( int glyph1, int glyph2 )
398 FT_Get_Kerning( face, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning );
403 #define FM_DEF_CODEPOINT_BITS (32)
404 #define FM_DEF_SIZE_BITS (12)
405 #define FM_DEF_SUBPIXEL_BITS (6)
406 #define FM_DEF_BLURRADIUS_BITS (8)
407 #define FM_DEF_BLURSCALE_BITS (6)
409 #define FM_DEF_CODEPOINT_SHIFT (0)
410 #define FM_DEF_SIZE_SHIFT (FM_DEF_CODEPOINT_BITS)
411 #define FM_DEF_SUBPIXEL_SHIFT (FM_DEF_CODEPOINT_BITS+FM_DEF_SIZE_BITS)
412 #define FM_DEF_BLURRADIUS_SHIFT (FM_DEF_CODEPOINT_BITS+FM_DEF_SIZE_BITS+FM_DEF_SUBPIXEL_BITS)
413 #define FM_DEF_BLURSCALE_SHIFT (FM_DEF_CODEPOINT_BITS+FM_DEF_SIZE_BITS+FM_DEF_SUBPIXEL_BITS+FM_DEF_BLURRADIUS_BITS)
415 #define FM_GLYPH_COMPUTE_DEF(codepoint,size,subpixel,blurradius,blurscale) ( (((uint64)(codepoint))<<FM_DEF_CODEPOINT_SHIFT) | (((uint64)(size))<<FM_DEF_SIZE_SHIFT) | (((uint64)(subpixel))<<FM_DEF_SUBPIXEL_SHIFT) | (((uint64)(blurradius))<<FM_DEF_BLURRADIUS_SHIFT) | (((uint64)(blurscale))<<FM_DEF_BLURSCALE_SHIFT) )
417 #define FM_SIZE_MAX ((1<<FM_DEF_SIZE_BITS)-1)
418 #define FM_BLUR_RADIUS_MAX ((1<<FM_DEF_BLURRADIUS_BITS)-1)
419 #define FM_BLUR_SCALE_MAX ((1<<FM_DEF_BLURSCALE_BITS)-1)
421 #define FM_GLYPH_CODEPOINT_CURSOR (0x1)
422 #define FM_GLYPH_CODEPOINT_REPLACEMENT (0xfffd)
424 #define FM_ALIGN_LEFT (0x0)
425 #define FM_ALIGN_CENTER (0x1)
426 #define FM_ALIGN_RIGHT (0x2)
428 #define FM_ALIGN_BASELINE (0x0)
429 #define FM_ALIGN_TOP (0x4)
430 #define FM_ALIGN_MIDDLE (0x8)
431 #define FM_ALIGN_BOTTOM (0x10)
434 public struct FMPathDraw
440 struct FMQuad { int x0, y0, x1, y1; };
446 short x0, y0, x1, y1;
447 short advance, offsetx, offsety;
451 static void getQuad( float x, float y, FMQuad q )
453 int rx = (int)(x + offsetx);
454 int ry = (int)(y + offsety);
455 q = { rx, ry, rx + x1 - x0, ry + y1 - y0 };
459 public class FMFont : struct
461 public LinkElement link;
462 FMFreeTypeFont ftFont;
468 float limitminy, limitmaxy;
472 int hashtable[FM_HASH_TABLE_SIZE];
481 static FMGlyph *allocGlyph( )
483 if( glyphcount >= glyphalloc )
486 if( !(glyphs = renew glyphs FMGlyph[glyphalloc]) )
489 return &glyphs[ glyphcount++ ];
494 static float getVertAlign( int align, int size )
496 if( align & FM_ALIGN_TOP )
497 return ascender * size;
498 else if( align & FM_ALIGN_MIDDLE )
499 return middleAlign * size;
500 else if( align & FM_ALIGN_BOTTOM )
501 return descender * (float)size;
505 static inline void addKerning( int prevGlyphIndex, FMGlyph *glyph, int *x, int *subpixel )
507 if( prevGlyphIndex != -1 )
509 *subpixel += ftFont.getGlyphKernAdvance( prevGlyphIndex, glyph->glyphindex );
510 #if FM_SUBPIXEL_ROUNDING_RANGE
511 *subpixel = ( *subpixel + (FM_SUBPIXEL_ROUNDING_RANGE>>1) ) & ~(FM_SUBPIXEL_ROUNDING_RANGE-1);
513 *x += *subpixel >> 6;
532 static FT_Library ftLibrary2;
534 public class FontManager
536 FontManagerRenderer renderer;
538 float widthinv, heightinv;
540 AtlasBuilder atlas { };
544 LinkList<FMFont, link = link> fontList { };
546 FMState states[FM_MAX_STATES];
549 #if FM_DEBUG_WHITERECT
550 static void addWhiteRect( int w, int h )
553 if( atlas.addRect( w, h, &gx, &gy ) )
556 byte * dst = &texdata[ gx + ( gy * width ) ];
558 for( y = 0 ; y < h ; y++)
560 for( x = 0 ; x < w ; x++ )
565 dirtyrect[0] = Min( dirtyrect[0], gx );
566 dirtyrect[1] = Min( dirtyrect[1], gy );
567 dirtyrect[2] = Max( dirtyrect[2], gx + w );
568 dirtyrect[3] = Max( dirtyrect[3], gy + h );
573 static FMGlyph *getGlyph( FMFont font, unichar codepoint, int size, int subpixel, int blurradius, int blurscale )
575 int i, glyphindex, advance, x0, y0, x1, y1, glyphwidth, glyphheight, gx, gy;
576 #if FM_TEXTURE_PADDING >= 1
589 padding = blurradius + FM_TEXTURE_PADDING;
591 /* Find code point and size. */
592 glyphdef = FM_GLYPH_COMPUTE_DEF( codepoint, size, subpixel, blurradius, blurscale );
593 hashindex = ccHash32Int64Inline( glyphdef ) & ( FM_HASH_TABLE_SIZE - 1 );
594 i = font.hashtable[hashindex];
597 if( glyphdef == font.glyphs[i].glyphdef )
598 return &font.glyphs[i];
599 i = font.glyphs[i].listnext;
602 /* Could not find glyph, create it. */
603 if( codepoint == FM_GLYPH_CODEPOINT_CURSOR )
614 y0 = -(int)ceilf( font.limitmaxy * (float)size );
615 y1 = -(int)floorf( font.limitminy * (float)size );
616 glyphheight = y1 - y0;
617 i = ( glyphheight - size ) / 3;
623 glyphindex = font.ftFont.getGlyphIndex( codepoint );
624 if( !( font.ftFont.buildGlyphBitmap( glyphindex, size, subpixel, &advance, &x0, &y0, &x1, &y1 ) ) )
626 if( codepoint != FM_GLYPH_CODEPOINT_REPLACEMENT )
627 return getGlyph(font, FM_GLYPH_CODEPOINT_REPLACEMENT, size, subpixel, blurradius, blurscale );
631 glyphwidth = ( x1 - x0 ) + ( padding << 1 );
632 glyphheight = ( y1 - y0 ) + ( padding << 1 );
634 // Find free spot for the rect in the atlas
635 added = atlas.addRect( glyphwidth, glyphheight, &gx, &gy );
636 if( !( added ) && ( onAtlasFull ) )
638 /* Atlas is full, let the user to resize the atlas (or not), and try again. */
640 added = atlas.addRect( glyphwidth, glyphheight, &gx, &gy );
645 /* Build the glyph */
646 glyph = font.allocGlyph();
647 glyph->glyphdef = glyphdef;
648 glyph->glyphindex = glyphindex;
649 glyph->x0 = (short)gx;
650 glyph->y0 = (short)gy;
651 glyph->x1 = (short)( glyph->x0 + glyphwidth );
652 glyph->y1 = (short)( glyph->y0 + glyphheight );
653 glyph->advance = (short)advance;
654 glyph->offsetx = (short)( x0 - padding );
655 glyph->offsety = (short)( y0 - padding );
657 glyph->imageIndex = -1;
658 if( renderer.registerImage )
659 glyph->imageIndex = renderer.registerImage( gx, gy, glyphwidth, glyphheight );
661 /* Add char to hash table */
662 glyph->listnext = font.hashtable[hashindex];
663 font.hashtable[hashindex] = font.glyphcount - 1;
666 dst = &texdata[ ( glyph->x0 + padding ) + ( ( glyph->y0 + padding ) * this.width ) ];
667 if( codepoint == FM_GLYPH_CODEPOINT_CURSOR )
668 buildCursorGlyph( dst, glyphwidth - ( padding << 1 ), glyphheight - ( padding << 1 ), this.width );
670 font.ftFont.copyGlyphBitmap( dst, glyphwidth - ( padding << 1 ), glyphheight - ( padding << 1 ), this.width, glyphindex );
675 bdst = &texdata[ glyph->x0 + ( glyph->y0 * this.width ) ];
677 blur(bdst, glyphwidth, glyphheight, this.width, blurradius );
679 blurScale(bdst, glyphwidth, glyphheight, this.width, blurradius, blurscale );
682 #if FM_TEXTURE_PADDING >= 1
683 dst = &texdata[ glyph->x0 + ( glyph->y0 * this.width ) ];
684 for( y = 0 ; y < glyphheight ; y++ )
686 dst[ y * this.width] = 0;
687 dst[ ( glyphwidth - 1 ) + ( y * this.width ) ] = 0;
689 for( x = 0 ; x < glyphwidth ; x++ )
692 dst[ x + ( ( glyphheight - 1 ) * this.width ) ] = 0;
696 dirtyrect[0] = Min( dirtyrect[0], glyph->x0 );
697 dirtyrect[1] = Min( dirtyrect[1], glyph->y0 );
698 dirtyrect[2] = Max( dirtyrect[2], glyph->x1 );
699 dirtyrect[3] = Max( dirtyrect[3], glyph->y1 );
704 static inline void drawTextGlyph( FMFont font, FMGlyph *glyph, int x, int y )
706 int ptx = x + glyph->offsetx;
707 int pty = y + glyph->offsety;
708 if( renderer.drawImage )
709 renderer.drawImage( ptx, pty, glyph->imageIndex );
710 else if( renderer.drawImageAlt )
711 renderer.drawImageAlt( texdata, ptx, pty, glyph->x0, glyph->y0, glyph->x1 - glyph->x0, glyph->y1 - glyph->y0 );
714 static inline void drawTextCursorGlyph( FMFont font, FMGlyph *glyph, int x, int y )
717 ptx = x + glyph->offsetx;
718 pty = y + glyph->offsety;
719 if( renderer.drawImageCursor )
720 renderer.drawImageCursor( ptx, pty, glyph->imageIndex );
721 else if( renderer.drawImageAlt )
722 renderer.drawImageAlt( texdata, ptx, pty, glyph->x0, glyph->y0, glyph->x1 - glyph->x0, glyph->y1 - glyph->y0 );
725 static inline void drawTextGlyphFloat( FMFont font, FMGlyph *glyph, float x, float y, float vectorx, float vectory, float offsetx, float offsety )
727 float vectx = (float)glyph->offsetx + offsetx;
728 float vecty = (float)glyph->offsety + offsety;
729 float ptx = x + ( vectorx * vectx ) - ( vectory * vecty );
730 float pty = y + ( vectorx * vecty ) + ( vectory * vectx );
731 renderer.drawImageFloat( ptx, pty, vectory, vectorx, glyph->imageIndex );
736 property FontManagerRenderer renderer { set { renderer = value; } get { return renderer; } }\
738 // When notified of a full atlas, you should call fmExpandAtlas() or fmResetAtlas() within the callback
739 virtual void (*onAtlasFull)();
741 // Create and destroy font manager
742 bool create( int width, int height, FontManagerRenderer renderer)
746 this.renderer = renderer;
750 // Initialize implementation library
751 if(FMFreeTypeFont::init() )
754 this.height = height;
755 if(renderer.createTexture( width, height ))
757 if((atlas.create( this.width, this.height, FM_INIT_ATLAS_NODES )))
759 // Create texture for the cache.
760 widthinv = 1.0f / width;
761 heightinv = 1.0f / height;
762 texdata = new byte[width * height];
765 memset( texdata, 0, width * height );
767 dirtyrect[0] = this.width;
768 dirtyrect[1] = this.height;
772 // Add white rect at 0,0 for debug drawing.
773 #if FM_DEBUG_WHITERECT
774 fmAddWhiteRect(2, 2 );
802 void setState( FMFont font, int size, int align, int blurradius, int blurscale )
805 if( size >= FM_SIZE_MAX )
807 if( blurradius >= FM_BLUR_RADIUS_MAX )
808 blurradius = FM_BLUR_RADIUS_MAX;
809 else if( blurradius < 0 )
811 if( blurscale >= FM_BLUR_SCALE_MAX )
812 blurscale = FM_BLUR_SCALE_MAX;
813 else if( blurscale < 1 )
815 state = &states[ nstates - 1 ];
817 state->size = (uint16)size;
818 state->align = (uint16)align;
819 state->blurradius = (uint16)blurradius;
820 state->blurscale = (uint16)blurscale;
824 void setFont( FMFont font )
826 states[ nstates - 1 ].font = font;
829 void setSize( int size )
831 if( size >= FM_SIZE_MAX )
834 states[ nstates - 1 ].size = (uint16)size;
837 void setAlign( int align )
839 states[ nstates - 1 ].align = (uint16)align;
842 void setBlur( int radius, int scale )
844 if( radius >= FM_BLUR_RADIUS_MAX )
845 radius = FM_BLUR_RADIUS_MAX;
846 else if( radius < 0 )
848 if( scale >= FM_BLUR_SCALE_MAX )
849 scale = FM_BLUR_SCALE_MAX;
852 states[ nstates - 1 ].blurradius = (uint16)radius;
853 states[ nstates - 1 ].blurscale = (uint16)scale;
860 if(nstates < FM_MAX_STATES)
863 memcpy( &states[nstates], &states[nstates-1], sizeof(FMState) );
876 FMState *state = &states[ nstates - 1 ];
879 state->blurradius = 0;
880 state->blurscale = 1;
881 state->align = FM_ALIGN_LEFT | FM_ALIGN_BASELINE;
887 static void freeFont(FMFont font)
891 fontList.Remove((IteratorPointer)font);
896 // Add font from file
897 FMFont addFont(const String path )
900 File f = FileOpen(path, read);
903 // Read in the font data
904 int dataSize = f.GetSize();
905 byte *data = new byte[dataSize];
908 f.Read(data, 1, dataSize);
909 font = addFontData(data, dataSize );
918 // Add font from data ; do not free( data ), the font manager will do that when removing the font
919 FMFont addFontData( byte *data, int dataSize )
923 glyphs = new FMGlyph[FM_INIT_GLYPHS];
924 glyphalloc = FM_INIT_GLYPHS;
929 float ascent, descent, fontHeight;
934 for( i = 0 ; i < FM_HASH_TABLE_SIZE ; i++ )
935 font.hashtable[i] = -1;
938 if(!font.ftFont.loadFont(data, dataSize))
944 // Store normalized line height. The real line height is got by multiplying by font size.
945 font.ftFont.getFontVMetrics( &ascent, &descent, &font.lineHeight, &font.limitminy, &font.limitmaxy );
946 fontHeight = ascent - descent;
947 font.ascender = ascent / fontHeight;
948 font.descender = descent / fontHeight;
949 font.middleAlign = 0.5f * ( font.ascender + font.descender );
950 font.fontdata = data;
956 void removeFont( FMFont font )
962 int drawText( int x, int y, const char *string, int stringlength )
970 int blurradius, blurscale;
973 state = &states[ nstates - 1 ];
977 blurradius = state->blurradius;
978 blurscale = state->blurscale;
979 if( !( state->font ) )
982 if( !( stringlength ) )
983 stringlength = strlen( string );
985 // Align horizontally
986 if( state->align & FM_ALIGN_RIGHT )
987 x -= getTextWidth(string, stringlength );
988 else if( state->align & FM_ALIGN_CENTER )
989 x -= getTextWidth(string, stringlength ) >> 1;
992 y += roundf( font.getVertAlign(state->align, state->size ) );
995 for( index = 0 ; index < stringlength ; index++ )
997 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
999 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1002 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1003 drawTextGlyph(font, glyph, x, y );
1004 addGlyphAdvance( &x, &subpixel, glyph );
1006 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1009 return x + ( subpixel >= 32 );
1012 int drawTextWithCursor( int x, int y, const char *string, int stringlength, int cursoroffset )
1014 int subpixel, index;
1020 int blurradius, blurscale;
1023 state = &states[ nstates - 1 ];
1026 prevGlyphIndex = -1;
1027 blurradius = state->blurradius;
1028 blurscale = state->blurscale;
1029 if( !( state->font ) )
1032 if( !( stringlength ) )
1033 stringlength = strlen( string );
1035 // Align horizontally
1036 if( state->align & FM_ALIGN_RIGHT )
1037 x -= getTextWidth(string, stringlength );
1038 else if( state->align & FM_ALIGN_CENTER )
1039 x -= getTextWidth(string, stringlength ) >> 1;
1042 y += roundf( font.getVertAlign(state->align, state->size ) );
1045 for( index = 0 ; ; index++ )
1047 if( index == cursoroffset )
1049 glyph = getGlyph(font, FM_GLYPH_CODEPOINT_CURSOR, state->size, subpixel, blurradius, blurscale );
1051 drawTextCursorGlyph(font, glyph, x, y );
1053 if( index >= stringlength )
1055 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1057 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1060 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1061 drawTextGlyph(font, glyph, x, y );
1062 addGlyphAdvance( &x, &subpixel, glyph );
1064 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1067 return x + ( subpixel >= 32 );
1070 int drawTextTruncate( int x, int y, int truncatewidth, const char *string, int stringlength, char *extstring, int extwidth )
1072 int subpixel, index, textwidth, truncatepoint;
1078 int blurradius, blurscale;
1081 state = &states[ nstates - 1 ];
1084 prevGlyphIndex = -1;
1085 blurradius = state->blurradius;
1086 blurscale = state->blurscale;
1087 if( !( state->font ) )
1090 if( !( stringlength ) )
1091 stringlength = strlen( string );
1092 textwidth = getTextWidthTruncate(string, stringlength, truncatewidth );
1094 truncatepoint = x + truncatewidth;
1095 if( textwidth <= truncatewidth )
1099 if( extwidth >= truncatewidth )
1101 truncatepoint -= extwidth;
1104 // Align horizontally
1105 if( state->align & FM_ALIGN_RIGHT )
1107 else if( state->align & FM_ALIGN_CENTER )
1108 x -= textwidth >> 1;
1111 y += roundf( font.getVertAlign(state->align, state->size ) );
1114 for( index = 0 ; index < stringlength ; index++ )
1116 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1118 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1121 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1122 drawTextGlyph(font, glyph, x, y );
1123 addGlyphAdvance( &x, &subpixel, glyph );
1124 if( x > truncatepoint )
1127 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1129 x += ( subpixel >= 32 );
1131 drawText(x, y, extstring, 0 );
1141 int getTextWidth( const char *string, int stringlength )
1143 return getTextWidthTruncate(string, stringlength, ( (unsigned)1 << ( ( sizeof(int) * CHAR_BIT ) - 1 ) ) - 1 );
1147 int getTextWidthTruncate( const char *string, int stringlength, int truncatewidth )
1149 int subpixel, index, advance;
1155 int blurradius, blurscale;
1158 state = &states[ nstates - 1 ];
1161 prevGlyphIndex = -1;
1162 blurradius = state->blurradius;
1163 blurscale = state->blurscale;
1164 if( !( state->font ) )
1167 if( !( stringlength ) )
1168 stringlength = strlen( string );
1172 for( index = 0 ; index < stringlength ; index++ )
1174 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1176 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1179 font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1180 addGlyphAdvance( &advance, &subpixel, glyph );
1181 if( advance > truncatewidth )
1184 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1187 return advance + ( subpixel >= 32 );
1190 int getTextBounds( int x, int y, const char *string, int stringlength, int *bounds )
1192 int subpixel, index;
1199 int blurradius, blurscale;
1201 int startx, advance;
1202 int minx, miny, maxx, maxy;
1204 state = &states[ nstates - 1 ];
1207 prevGlyphIndex = -1;
1208 blurradius = state->blurradius;
1209 blurscale = state->blurscale;
1210 if( !( state->font ) )
1213 if( !( stringlength ) )
1214 stringlength = strlen( string );
1217 y += font.getVertAlign(state->align, state->size );
1224 for( index = 0 ; index < stringlength ; index++ )
1226 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1228 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1231 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1232 glyph->getQuad(x, y, q);
1241 addGlyphAdvance( &x, &subpixel, glyph );
1243 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1246 advance = x - startx;
1248 /* Align horizontally */
1249 if( state->align & FM_ALIGN_RIGHT )
1254 else if( state->align & FM_ALIGN_CENTER )
1256 minx -= advance * 0.5f;
1257 maxx -= advance * 0.5f;
1268 return advance + ( subpixel >= 32 );
1271 // Find text offset up to truncatewidth
1272 int getTextTruncateOffset( int truncatewidth, const char *string, int stringlength, int extwidth, int *retextflag, int *retfullwidth )
1274 int subpixel, index, advance, truncatewidthext, truncateindex, extflag, fullwidth = 0;
1280 int blurradius, blurscale;
1283 state = &states[ nstates - 1 ];
1288 if( extwidth >= truncatewidth )
1292 prevGlyphIndex = -1;
1293 blurradius = state->blurradius;
1294 blurscale = state->blurscale;
1295 if( !( state->font ) )
1298 if( stringlength <= 0 )
1299 stringlength = strlen( string );
1301 truncatewidthext = truncatewidth - extwidth;
1307 for( index = 0 ; ; index++ )
1309 if( index >= stringlength )
1311 truncateindex = index;
1312 fullwidth = advance + ( subpixel >= 32 );
1315 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1317 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1320 font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1321 addGlyphAdvance( &advance, &subpixel, glyph );
1322 if( advance > truncatewidth )
1327 if( advance <= truncatewidthext )
1329 truncateindex = index + 1;
1330 fullwidth = advance + ( subpixel >= 32 );
1333 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1339 *retfullwidth = fullwidth;
1341 *retfullwidth = fullwidth;
1344 *retextflag = extflag;
1346 return truncateindex;
1349 // Find text offset nearest to the given width
1350 int getTextNearestOffset( int targetwidth, const char *string, int stringlength )
1352 int subpixel, index, advance, truncateindex, distance, bestdistance;
1358 int blurradius, blurscale;
1361 state = &states[ nstates - 1 ];
1364 prevGlyphIndex = -1;
1365 blurradius = state->blurradius;
1366 blurscale = state->blurscale;
1367 if( !( state->font ) )
1370 if( stringlength <= 0 )
1371 stringlength = strlen( string );
1376 bestdistance = abs( targetwidth );
1377 for( index = 0 ; index < stringlength ; index++ )
1379 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1381 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1384 font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1385 addGlyphAdvance( &advance, &subpixel, glyph );
1386 distance = abs( targetwidth - ( advance + ( subpixel >= 32 ) ) );
1387 if( distance > bestdistance )
1389 bestdistance = distance;
1390 truncateindex = index + 1;
1392 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1395 return truncateindex;
1400 static void flush( bool rendererFlush )
1402 // Flush texture updates
1403 if( ( dirtyrect[0] < dirtyrect[2] ) && ( dirtyrect[1] < dirtyrect[3] ) )
1405 renderer.updateTexture( dirtyrect, texdata );
1407 dirtyrect[0] = width;
1408 dirtyrect[1] = height;
1412 // Flush buffered glyphs
1413 if( rendererFlush ) renderer.flush( );
1416 // Flush buffered texture updates, renderer->updateTexture()
1426 void getFontExtent( int *retascent, int *retdescent )
1429 FMState *state = &states[ nstates - 1 ];
1435 *retascent = -(int)ceilf( font.ascender * (float)state->size );
1437 *retdescent = -(int)floorf( font.descender * (float)state->size );
1441 void fmGetFontLimits( int *retlimitminy, int *retlimitmaxy )
1446 state = &states[ nstates - 1 ];
1447 if( !( state->font ) )
1452 *retlimitminy = -(int)ceilf( font.limitmaxy * state->size );
1454 *retlimitmaxy = -(int)floorf( font.limitminy * state->size );
1459 int getFontLineHeight( )
1464 state = &states[ nstates - 1 ];
1465 if( !( state->font ) )
1468 return (int)ceilf( font.lineHeight * state->size );
1474 // Pull texture changes
1475 const byte *getTextureData( int *width, int *height )
1478 *width = this.width;
1480 *height = this.height;
1484 // Retrieve the dirty rectangle, telling fontmanager you will manually perform the update, returns 0 if no part of the texture requires an update
1485 bool validateTexture( int *retdirtyrect )
1487 if( ( dirtyrect[0] < dirtyrect[2] ) && ( dirtyrect[1] < dirtyrect[3] ) )
1489 retdirtyrect[0] = dirtyrect[0];
1490 retdirtyrect[1] = dirtyrect[1];
1491 retdirtyrect[2] = dirtyrect[2];
1492 retdirtyrect[3] = dirtyrect[3];
1494 dirtyrect[0] = width;
1495 dirtyrect[1] = height;
1503 // Returns current atlas size
1504 void getAtlasSize( int *retwidth, int *retheight )
1506 *retwidth = this.width;
1507 *retheight = this.height;
1511 // Expands the atlas size
1512 bool expandAtlas( int width, int height )
1514 width = Max( width, this.width );
1515 height = Max( height, this.height );
1517 if( ( width == this.width ) && ( height == this.height ) )
1520 // Flush all pending glyphs
1523 // Create new texture
1524 if( renderer.resizeTexture( width, height ) )
1529 // Copy old texture data over.
1530 if( !( data = (byte *)malloc( width * height ) ) )
1532 for( i = 0 ; i < this.height ; i++ )
1534 byte * dst = &data[ i * width ];
1535 byte * src = &this.texdata[ i * this.width ];
1536 memcpy( dst, src, this.width );
1537 if( width > this.width )
1538 memset( dst+this.width, 0, width - this.width );
1540 if( height > this.height )
1541 memset( &data[ this.height * width ], 0, ( height - this.height ) * width );
1543 delete this.texdata;
1546 // Increase atlas size
1547 atlas.expand( width, height );
1549 // Add existing data as dirty.
1552 dirtyrect[2] = this.width;
1553 dirtyrect[3] = atlas.getAtlasMaxHeight();
1556 this.height = height;
1557 widthinv = 1.0f / this.width;
1558 heightinv = 1.0f / this.height;
1565 // Reset the whole fm
1566 bool resetAtlas( int width, int height )
1568 // Flush all pending glyphs
1571 // Create new texture
1572 if(renderer.resizeTexture( width, height ) )
1575 atlas.reset( width, height );
1577 // Clear texture data.
1578 texdata = renew texdata byte[width * height];
1579 if(!texdata) return 0;
1580 memset( this.texdata, 0, width * height );
1583 dirtyrect[0] = width;
1584 dirtyrect[1] = height;
1588 // Reset cached glyphs
1589 for(font : fontList)
1592 font.glyphcount = 0;
1593 for( i = 0 ; i < FM_HASH_TABLE_SIZE ; i++ )
1594 font.hashtable[i] = -1;
1596 renderer.resetImages( );
1599 this.height = height;
1600 this.widthinv = 1.0f / this.width;
1601 this.heightinv = 1.0f / this.height;
1603 // Add white rect at 0,0 for debug drawing.
1604 #if FM_DEBUG_WHITERECT
1605 fmAddWhiteRect(2, 2 );
1614 bool initPathDraw( FMPathDraw pathdraw )
1616 FMState *state = &states[ nstates - 1 ];
1617 FMFont font = state->font;
1620 pathdraw.prevGlyphIndex = -1;
1621 pathdraw.middleAlign = font.middleAlign * (float)state->size;
1627 float pathDrawCharacter( FMPathDraw pathdraw, float x, float y, float vectorx, float vectory, int unicode )
1630 FMState *state = &states[ nstates - 1 ];
1631 int blurradius = state->blurradius;
1632 int blurscale = state->blurscale;
1633 FMFont font = state->font;
1634 FMGlyph *glyph = getGlyph(font, unicode, state->size, 0, blurradius, blurscale );
1637 subpixel = font.ftFont.getGlyphKernAdvance( pathdraw.prevGlyphIndex, glyph->glyphindex );
1638 drawTextGlyphFloat(font, glyph, x, y, vectorx, vectory, (float)subpixel * (1.0f/64.0f), pathdraw.middleAlign );
1639 subpixel += glyph->advance;
1640 pathdraw.prevGlyphIndex = glyph->glyphindex;
1645 pathdraw.prevGlyphIndex = -1;
1648 return (float)subpixel * (1.0f/64.0f);
1651 float pathDrawPredictAdvance( FMPathDraw pathdraw, unichar unicode )
1654 FMState *state = &states[ nstates - 1 ];
1655 FMFont font = state->font;
1656 int blurradius = state->blurradius;
1657 int blurscale = state->blurscale;
1658 FMGlyph *glyph = getGlyph(font, unicode, state->size, 0, blurradius, blurscale );
1661 subpixel = font.ftFont.getGlyphKernAdvance( pathdraw.prevGlyphIndex, glyph->glyphindex );
1662 subpixel += glyph->advance;
1664 return (float)subpixel * (1.0f/64.0f);