1 /* *****************************************************************************
2 * Original Version Copyright (c) 2007-2014 Alexis Naveros.
4 * Ecere Corporation has unlimited/unrestricted rights.
5 * *****************************************************************************/
20 static inline uint32 decodeUTF8( uint32 b, uint32 *state, unichar *retCodePoint ) { return ccUtf8ToUnicode(b, state, (uint *)retCodePoint); }
24 // Based on Exponential blur, Jani Huhtanen, 2006
29 static void blurCols( byte *dst, int w, int h, int stride, int alpha )
32 for( y = 0 ; y < h ; y++ )
35 for( x = 1 ; x < w ; x++ )
37 z += ( alpha * ( ( (int)( dst[x] ) << ZPREC ) - z ) ) >> APREC;
38 dst[x] = (byte)( z >> ZPREC );
40 for( x = w-2 ; x >= 0 ; x-- )
42 z += ( alpha * ( ( (int)( dst[x] ) << ZPREC ) - z ) ) >> APREC;
43 dst[x] = (byte)( z >> ZPREC );
49 static void blurRows( byte *dst, int w, int h, int stride, int alpha )
52 for( x = 0 ; x < w ; x++ )
55 for( y = stride ; y < h*stride ; y += stride )
57 z += ( alpha * ( ( (int)( dst[y] ) << ZPREC ) - z ) ) >> APREC;
58 dst[y] = (byte)( z >> ZPREC );
60 for( y = (h-2)*stride ; y >= 0 ; y -= stride )
62 z += ( alpha * ( ( (int)( dst[y] ) << ZPREC ) - z ) ) >> APREC;
63 dst[y] = (byte)( z >> ZPREC );
69 static void blur( byte *dst, int width, int height, int stride, int radius )
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-- )
79 blurRows( dst, width, height, stride, alpha );
80 blurCols( dst, width, height, stride, alpha );
91 #define SPREC (31-(APREC+ZPREC+8))
93 static void blurCols32( int *dst, int w, int h, int stride, int alpha )
96 for( y = 0 ; y < h ; y++ )
99 for( x = 1 ; x < w ; x++ )
101 z += ( alpha * ( dst[x] - z ) ) >> APREC;
104 for( x = w - 2 ; x >= 0 ; x-- )
106 z += ( alpha * ( dst[x] - z ) ) >> APREC;
113 static void blurRows32( int *dst, int w, int h, int stride, int alpha )
116 for( x = 0 ; x < w ; x++ )
119 for( y = 1 * stride ; y < h * stride ; y += stride )
121 z += ( alpha * ( dst[y] - z ) ) >> APREC;
124 for( y = ( h - 2 ) * stride ; y >= 0 ; y -= stride )
126 z += ( alpha * ( dst[y] - z ) ) >> APREC;
133 static void blurScale( byte *dst, int width, int height, int stride, int radius, int scale )
140 int *buffer, *bufferrow;
143 if( scale > ( 1 << SPREC ) )
146 buffer = new int[ width * height ];
147 for( y = 0 ; y < height ; y++ )
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;
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-- )
162 blurRows32( buffer, width, height, width, alpha );
163 blurCols32( buffer, width, height, width, alpha );
166 for( y = 0 ; y < height ; y++ )
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);
184 static void buildCursorGlyph( byte *dst, int glyphwidth, int glyphheight, int dststride )
186 int x, y, hbarheight, hgap, hline, vline;
189 if( glyphwidth >= 3 )
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++ )
198 for( x = 0 ; x < glyphwidth ; x++ )
202 for( y = 0 ; y < vline ; y++ )
205 for( x = 0 ; x < hgap ; x++ )
207 for( ; x < hline ; x++ )
209 for( ; x < glyphwidth ; x++ )
213 for( y = 0 ; y < hbarheight ; y++ )
216 for( x = 0 ; x < glyphwidth ; x++ )
223 for( y = 0 ; y < glyphheight ; y++ )
226 for( x = 0 ; x < glyphwidth ; x++ )
235 static inline void addGlyphAdvance( int *x, int *subpixel, FMGlyph *glyph )
237 *subpixel += glyph->advance;
238 #if FM_SUBPIXEL_ROUNDING_RANGE
239 *subpixel = ( *subpixel + (FM_SUBPIXEL_ROUNDING_RANGE>>1) ) & ~(FM_SUBPIXEL_ROUNDING_RANGE-1);
241 *x += *subpixel >> 6;
245 public class FontManagerRenderer
250 virtual bool init(int channelCount);
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 );
257 // If the renderer does any buffering of drawImage(), flush it all right now (texture may change immediately after)
258 virtual void flush( );
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 );
271 // The renderer must flush all recorded images, registerImage() will be called for new images
272 virtual void resetImages( );
274 virtual void setLayer( uint32 layerIndex );
281 #include <ft2build.h>
283 #include FT_FREETYPE_H
284 #include FT_ADVANCES_H
290 #define FM_SUPPORT_GLYPH_ROTATION (1)
296 #define FM_ENABLE_HINTING (1)
297 #define FM_SUBPIXEL_ROUNDING_RANGE (16)
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 = (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;
377 static inline int getGlyphKernAdvance( int glyph1, int glyph2 )
380 FT_Get_Kerning( face, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning );
381 return (int)ftKerning.x;
384 static inline byte *getGlyphBitmap( int glyphindex )
386 FT_GlyphSlot glyphslot;
387 glyphslot = face->glyph;
388 return glyphslot->bitmap.buffer;
392 class FMDefBits : uint64
396 int size:11, subPixel:6, blurRadius:6, blurScale:6;
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;
404 static define FM_GLYPH_CODEPOINT_CURSOR = 0x1;
405 static define FM_GLYPH_CODEPOINT_REPLACEMENT = 0xfffd;
407 public enum FMVerticalAlignment { baseline, top, middle, bottom };
409 public class FMTextAlignment : uint16
412 Alignment horzAlignment:2;
413 FMVerticalAlignment vertAlignment:2;
416 public struct FMPathDraw
422 struct FMQuad { int x0, y0, x1, y1; };
428 short x0, y0, x1, y1;
429 short advance, offsetx, offsety;
433 static void getQuad( float x, float y, FMQuad q )
435 int rx = (int)(x + offsetx);
436 int ry = (int)(y + offsety);
437 q = { rx, ry, rx + x1 - x0, ry + y1 - y0 };
441 public class FMFont : struct
443 public LinkElement link;
444 FMFreeTypeFont ftFont;
450 float limitminy, limitmaxy;
454 int hashtable[FM_HASH_TABLE_SIZE];
455 int glyphPaddingWidth;
457 void (*processImage)( void *opaquecontext, byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingwidth, int pass);
458 void *processImageContext;
461 public void setFontImageProcessing(
462 void (*processImage)( byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingsize, void *opaquecontext ),
463 void *opaquecontext )
465 this.processImage = processImage;
466 this.processImageContext = opaquecontext;
471 float outlineAlphaFactor;
472 float outlineIntensityFactor;
474 static void ::outlineProcessGlyphImage( FMFont font, byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingwidth, int isOutline )
477 byte *src, *dst, *dstrow;
478 float intensityfactor, alphafactor, range, alpha, intensity, rangeinv, rangebase;
479 float *distancemap, *dmap;
481 distancemap = new float[width * height];
484 imgDistMapBuild( distancemap, src, width, height, bytesperpixel, bytesperline );
486 alphafactor = font.outlineAlphaFactor; //2.0f;
487 intensityfactor = font.outlineIntensityFactor; // 0.2f;
488 range = (float)font.outlineRadius;
489 rangeinv = 1.0f / range;
493 for( y = 0 ; y < height ; y++ )
496 for( x = 0 ; x < width ; x++ )
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)
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 ) ) );
511 dstrow[0] = (unsigned char)roundf( fmaxf( 0.0f, fminf( 255.0f, alpha * 255.0f ) ) );
513 dstrow[0] = (unsigned char)roundf( fmaxf( 0.0f, fminf( 255.0f, intensity * 255.0f ) ) );
515 dstrow += bytesperpixel;
523 public void setOutline(float intensityFactor, float alphaFactor, float radius) // TODO: Figure out radius?
525 outlineIntensityFactor = intensityFactor;
526 outlineAlphaFactor = alphaFactor;
527 outlineRadius = radius;
528 processImage = outlineProcessGlyphImage;
529 processImageContext = this;
539 static FMGlyph *allocGlyph( )
541 if( glyphcount >= glyphalloc )
544 if( !(glyphs = renew glyphs FMGlyph[glyphalloc]) )
547 return &glyphs[ glyphcount++ ];
552 static float getVertAlign( FMTextAlignment align, int 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;
564 static inline void addKerning( int prevGlyphIndex, FMGlyph *glyph, int *x, int *subpixel )
566 if( prevGlyphIndex != -1 )
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);
572 *x += *subpixel >> 6;
583 FMTextAlignment align;
591 static FT_Library ftLibrary2;
593 static void copyGlyphBitmap1( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride )
596 for( y = 0 ; y < glyphheight ; y++ )
598 byte *dstrow = &dst[ y * dststride ];
599 for( x = 0 ; x < glyphwidth ; x++ )
600 dstrow[ x ] = src[ x ];
605 static void copyGlyphBitmap2( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride )
608 for( y = 0 ; y < glyphheight ; y++ )
610 byte *dstrow = &dst[ y * dststride ];
611 for( x = 0 ; x < glyphwidth ; x++ )
612 dstrow[ x << 1 ] = src[ x ];
617 static void copyGlyphBitmap4( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride )
620 for( y = 0 ; y < glyphheight ; y++ )
622 byte *dstrow = &dst[ y * dststride ];
623 for( x = 0 ; x < glyphwidth ; x++ )
624 dstrow[ x << 2 ] = src[ x ];
629 public class FontManager
631 FontManagerRenderer renderer;
633 float widthinv, heightinv;
638 AtlasBuilder atlas { };
642 LinkList<FMFont, link = link> fontList { };
644 FMState states[FM_MAX_STATES];
647 void (*copyGlyphBitmap)( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride );
650 static FMGlyph *getGlyph( FMFont font, unichar codepoint, int size, int subpixel, int blurradius, int blurscale, bool outlinePass )
652 int i, glyphindex, advance, x0, y0, x1, y1, gx, gy;
653 int glyphwidth, glyphheight, glyphareawidth, glyphareaheight;
664 padding = blurradius + font.glyphPaddingWidth;
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];
672 if( glyphdef == font.glyphs[i].glyphdef )
673 return &font.glyphs[i];
674 i = font.glyphs[i].listnext;
677 /* Could not find glyph, create it */
678 if( codepoint == FM_GLYPH_CODEPOINT_CURSOR )
689 y0 = -(int)ceilf( font.limitmaxy * (float)size );
690 y1 = -(int)floorf( font.limitminy * (float)size );
691 i = ( (y1 - y0) - size ) / 3;
697 glyphindex = font.ftFont.getGlyphIndex( codepoint );
698 if( !( font.ftFont.buildGlyphBitmap( glyphindex, size, subpixel, &advance, &x0, &y0, &x1, &y1 ) ) )
700 if( codepoint != FM_GLYPH_CODEPOINT_REPLACEMENT )
701 return getGlyph(font, FM_GLYPH_CODEPOINT_REPLACEMENT, size, subpixel, blurradius, blurscale, outlinePass );
705 glyphwidth = ( x1 - x0 );
706 glyphheight = ( y1 - y0 );
707 glyphareawidth = glyphwidth + (padding << 1);
708 glyphareaheight = glyphheight + (padding << 1);
710 // Find free spot for the rect in the atlas
711 added = atlas.addRect( glyphareawidth, glyphareaheight, &gx, &gy );
712 if( !( added ) && ( onAtlasFull ) )
714 /* Atlas is full, let the user to resize the atlas (or not), and try again. */
716 added = atlas.addRect( glyphareawidth, glyphareaheight, &gx, &gy );
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 );
733 glyph->imageIndex = -1;
734 if( renderer.registerImage )
738 renderer.setLayer(3); //DM_LAYER_BELOW);
740 glyph->imageIndex = renderer.registerImage( gx, gy, glyphareawidth, glyphareaheight );
743 renderer.setLayer(6); //DM_LAYER_NORMAL);
747 // Add char to hash table
748 glyph->listnext = font.hashtable[hashindex];
749 font.hashtable[hashindex] = font.glyphcount - 1;
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 );
757 dst = &texdata[ ( glyph->x0 + padding ) * bytesperpixel + ( ( glyph->y0 + padding ) * bytesperline ) + channelindex];
758 if( codepoint == FM_GLYPH_CODEPOINT_CURSOR )
760 src = new byte[ glyphwidth * glyphheight ];
761 buildCursorGlyph( src, glyphwidth, glyphheight, glyphwidth );
762 copyGlyphBitmap( dst, src, glyphwidth, glyphheight, bytesperline );
767 src = font.ftFont.getGlyphBitmap(glyphindex);
768 copyGlyphBitmap( dst, src, glyphwidth, glyphheight, bytesperline );
775 printf( "Do NOT use the blur! This will be removed in favor of external image processing\n" );
777 bdst = &texdata[ glyph->x0 * bytesperpixel + ( glyph->y0 * bytesperline ) ];
779 blur(bdst, glyphareawidth, glyphareaheight, this.width, blurradius );
781 blurScale(bdst, glyphareawidth, glyphareaheight, this.width, blurradius, blurscale );
784 // User custom font image processing
785 if(font.processImage)
787 dst = &texdata[ ( glyph->x0 * bytesperpixel ) + ( glyph->y0 * bytesperline ) ];
788 font.processImage( font.processImageContext, dst, glyphareawidth, glyphareaheight, bytesperpixel, bytesperline, font.glyphPaddingWidth, outlinePass );
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 );
799 static inline void drawTextGlyph( FMFont font, FMGlyph *glyph, int x, int y, bool useExtColor )
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 );
809 static inline void drawTextCursorGlyph( FMFont font, FMGlyph *glyph, int x, int y )
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 );
820 static inline void drawTextGlyphFloat( FMFont font, FMGlyph *glyph, float x, float y, float vectorx, float vectory, float offsetx, float offsety, bool useExtColor )
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 );
831 property FontManagerRenderer renderer { set { renderer = value; } get { return renderer; } }
833 // When notified of a full atlas, you should call fmExpandAtlas() or fmResetAtlas() within the callback
834 virtual void (*onAtlasFull)();
836 // Create and destroy font manager
837 bool create( int width, int height, int channelCount, int channelIndex, FontManagerRenderer renderer)
841 if( ( channelCount != 1 ) && ( channelCount != 2 ) && ( channelCount != 4 ) )
843 if( ( width <= 0 ) || ( height <= 0 ) )
846 this.renderer = renderer;
848 renderer.init(channelCount);
850 // Initialize implementation library
851 if(FMFreeTypeFont::init() )
854 this.height = height;
855 bytesperpixel = channelCount;
856 bytesperline = width * bytesperpixel;
857 this.channelindex = channelIndex;
858 if(renderer.createTexture( width, height ))
860 if((atlas.create( this.width, this.height, FM_INIT_ATLAS_NODES )))
862 // Create texture for the cache.
863 widthinv = 1.0f / width;
864 heightinv = 1.0f / height;
865 texdata = new byte[width * height * bytesperpixel];
868 memset( texdata, 0, height * bytesperline );
870 dirtyrect[0] = this.width;
871 dirtyrect[1] = this.height;
875 if( bytesperpixel == 1 )
876 copyGlyphBitmap = copyGlyphBitmap1;
877 else if( bytesperpixel == 2 )
878 copyGlyphBitmap = copyGlyphBitmap2;
880 copyGlyphBitmap = copyGlyphBitmap4;
907 void setState( FMFont font, int size, int align, int blurradius, int blurscale )
910 if( size >= FM_SIZE_MAX )
912 if( blurradius >= FM_BLUR_RADIUS_MAX )
913 blurradius = FM_BLUR_RADIUS_MAX;
914 else if( blurradius < 0 )
916 if( blurscale >= FM_BLUR_SCALE_MAX )
917 blurscale = FM_BLUR_SCALE_MAX;
918 else if( blurscale < 1 )
920 state = &states[ nstates - 1 ];
922 state->size = (uint16)size;
923 state->align = (uint16)align;
924 state->blurradius = (uint16)blurradius;
925 state->blurscale = (uint16)blurscale;
928 void setFont( FMFont font )
930 states[ nstates - 1 ].font = font;
933 void setSize( int size )
935 if( size >= FM_SIZE_MAX )
938 states[ nstates - 1 ].size = (uint16)size;
941 void setAlign( int align )
943 states[ nstates - 1 ].align = (uint16)align;
946 void setBlur( int radius, int scale )
948 if( radius >= FM_BLUR_RADIUS_MAX )
949 radius = FM_BLUR_RADIUS_MAX;
950 else if( radius < 0 )
952 if( scale >= FM_BLUR_SCALE_MAX )
953 scale = FM_BLUR_SCALE_MAX;
956 states[ nstates - 1 ].blurradius = (uint16)radius;
957 states[ nstates - 1 ].blurscale = (uint16)scale;
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 )
969 if(nstates < FM_MAX_STATES)
972 memcpy( &states[nstates], &states[nstates-1], sizeof(FMState) );
985 FMState *state = &states[ nstates - 1 ];
988 state->blurradius = 0;
989 state->blurscale = 1;
990 state->align = { left, baseline };
996 static void freeFont(FMFont font)
1000 fontList.Remove((IteratorPointer)font);
1005 // Add font from file
1006 FMFont addFont(const String path, int glyphPaddingWidth )
1009 File f = FileOpen(path, read);
1012 // Read in the font data
1013 int dataSize = f.GetSize();
1014 byte *data = new byte[dataSize];
1017 f.Read(data, 1, dataSize);
1018 font = addFontData(data, dataSize, glyphPaddingWidth);
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 )
1032 glyphs = new FMGlyph[FM_INIT_GLYPHS];
1033 glyphalloc = FM_INIT_GLYPHS;
1034 glyphPaddingWidth = glyphPaddingWidth;
1039 float ascent, descent, fontHeight;
1044 for( i = 0 ; i < FM_HASH_TABLE_SIZE ; i++ )
1045 font.hashtable[i] = -1;
1048 if(!font.ftFont.loadFont(data, dataSize))
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;
1066 void removeFont( FMFont font )
1072 int drawText( int x, int y, const char *string, int stringlength )
1074 int subpixel, index;
1080 int blurradius, blurscale;
1083 state = &states[ nstates - 1 ];
1086 prevGlyphIndex = -1;
1087 blurradius = state->blurradius;
1088 blurscale = state->blurscale;
1089 if( !( state->font ) )
1092 if( !( stringlength ) )
1093 stringlength = strlen( string );
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;
1102 y += roundf( font.getVertAlign(state->align, state->size ) );
1105 for( index = 0 ; index < stringlength ; index++ )
1107 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1109 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale, false );
1112 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1113 #if !defined(SHADERS)
1114 if(font.processImage)
1116 FMGlyph *outlineGlyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale, true );
1118 drawTextGlyph(font, outlineGlyph, x, y, true );
1121 drawTextGlyph(font, glyph, x, y, false );
1122 addGlyphAdvance( &x, &subpixel, glyph );
1124 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1127 return x + ( subpixel >= 32 );
1130 int drawTextWithCursor( int x, int y, const char *string, int stringlength, int cursoroffset )
1132 int subpixel, index;
1138 int blurradius, blurscale;
1141 state = &states[ nstates - 1 ];
1144 prevGlyphIndex = -1;
1145 blurradius = state->blurradius;
1146 blurscale = state->blurscale;
1147 if( !( state->font ) )
1150 if( !( stringlength ) )
1151 stringlength = strlen( string );
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;
1160 y += roundf( font.getVertAlign(state->align, state->size ) );
1163 for( index = 0 ; ; index++ )
1165 if( index == cursoroffset )
1167 glyph = getGlyph(font, FM_GLYPH_CODEPOINT_CURSOR, state->size, subpixel, blurradius, blurscale, false );
1169 drawTextCursorGlyph(font, glyph, x, y );
1171 if( index >= stringlength )
1173 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1175 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale, false );
1178 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1179 #if !defined(SHADERS)
1180 if(font.processImage)
1182 FMGlyph *outlineGlyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale, true );
1184 drawTextGlyph(font, outlineGlyph, x, y, true );
1187 drawTextGlyph(font, glyph, x, y, false );
1188 addGlyphAdvance( &x, &subpixel, glyph );
1190 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1193 return x + ( subpixel >= 32 );
1196 int drawTextTruncate( int x, int y, int truncatewidth, const char *string, int stringlength, char *extstring, int extwidth )
1198 int subpixel, index, textwidth, truncatepoint;
1204 int blurradius, blurscale;
1207 state = &states[ nstates - 1 ];
1210 prevGlyphIndex = -1;
1211 blurradius = state->blurradius;
1212 blurscale = state->blurscale;
1213 if( !( state->font ) )
1216 if( !( stringlength ) )
1217 stringlength = strlen( string );
1218 textwidth = getTextWidthTruncate(string, stringlength, truncatewidth );
1220 truncatepoint = x + truncatewidth;
1221 if( textwidth <= truncatewidth )
1225 if( extwidth >= truncatewidth )
1227 truncatepoint -= extwidth;
1230 // Align horizontally
1231 if( state->align.horzAlignment == right )
1233 else if( state->align.horzAlignment == center )
1234 x -= textwidth >> 1;
1237 y += roundf( font.getVertAlign(state->align, state->size ) );
1240 for( index = 0 ; index < stringlength ; index++ )
1242 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1244 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale, false );
1247 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1248 #if !defined(SHADERS)
1249 if(font.processImage)
1251 FMGlyph *outlineGlyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale, true );
1253 drawTextGlyph(font, outlineGlyph, x, y, true );
1256 drawTextGlyph(font, glyph, x, y, false );
1257 addGlyphAdvance( &x, &subpixel, glyph );
1258 if( x > truncatepoint )
1261 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1263 x += ( subpixel >= 32 );
1265 drawText(x, y, extstring, 0 );
1275 int getTextWidth( const char *string, int stringlength )
1277 return getTextWidthTruncate(string, stringlength, ( (unsigned)1 << ( ( sizeof(int) * CHAR_BIT ) - 1 ) ) - 1 );
1281 int getTextWidthTruncate( const char *string, int stringlength, int truncatewidth )
1283 int subpixel, index, advance;
1289 int blurradius, blurscale;
1292 state = &states[ nstates - 1 ];
1295 prevGlyphIndex = -1;
1296 blurradius = state->blurradius;
1297 blurscale = state->blurscale;
1298 if( !( state->font ) )
1301 if( !( stringlength ) )
1302 stringlength = strlen( string );
1306 for( index = 0 ; index < stringlength ; index++ )
1308 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1310 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale, false );
1313 font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1314 addGlyphAdvance( &advance, &subpixel, glyph );
1315 if( advance > truncatewidth )
1318 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1321 return advance + ( subpixel >= 32 );
1324 int getTextBounds( int x, int y, const char *string, int stringlength, int *bounds )
1326 int subpixel, index;
1333 int blurradius, blurscale;
1335 int startx, advance;
1336 int minx, miny, maxx, maxy;
1338 state = &states[ nstates - 1 ];
1341 prevGlyphIndex = -1;
1342 blurradius = state->blurradius;
1343 blurscale = state->blurscale;
1344 if( !( state->font ) )
1347 if( !( stringlength ) )
1348 stringlength = strlen( string );
1351 y += font.getVertAlign(state->align, state->size );
1358 for( index = 0 ; index < stringlength ; index++ )
1360 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1362 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale, false );
1365 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1366 glyph->getQuad(x, y, q);
1375 addGlyphAdvance( &x, &subpixel, glyph );
1377 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1380 advance = x - startx;
1382 /* Align horizontally */
1383 if( state->align.horzAlignment == right )
1388 else if( state->align.horzAlignment == center )
1390 minx -= advance * 0.5f;
1391 maxx -= advance * 0.5f;
1402 return advance + ( subpixel >= 32 );
1405 // Find text offset up to truncatewidth
1406 int getTextTruncateOffset( int truncatewidth, const char *string, int stringlength, int extwidth, int *retextflag, int *retfullwidth )
1408 int subpixel, index, advance, truncatewidthext, truncateindex, extflag, fullwidth = 0;
1414 int blurradius, blurscale;
1417 state = &states[ nstates - 1 ];
1422 if( extwidth >= truncatewidth )
1426 prevGlyphIndex = -1;
1427 blurradius = state->blurradius;
1428 blurscale = state->blurscale;
1429 if( !( state->font ) )
1432 if( stringlength <= 0 )
1433 stringlength = strlen( string );
1435 truncatewidthext = truncatewidth - extwidth;
1441 for( index = 0 ; ; index++ )
1443 if( index >= stringlength )
1445 truncateindex = index;
1446 fullwidth = advance + ( subpixel >= 32 );
1449 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1451 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale, false );
1454 font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1455 addGlyphAdvance( &advance, &subpixel, glyph );
1456 if( advance > truncatewidth )
1461 if( advance <= truncatewidthext )
1463 truncateindex = index + 1;
1464 fullwidth = advance + ( subpixel >= 32 );
1467 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1473 *retfullwidth = fullwidth;
1475 *retfullwidth = fullwidth;
1478 *retextflag = extflag;
1480 return truncateindex;
1483 // Find text offset nearest to the given width
1484 int getTextNearestOffset( int targetwidth, const char *string, int stringlength )
1486 int subpixel, index, advance, truncateindex, distance, bestdistance;
1492 int blurradius, blurscale;
1495 state = &states[ nstates - 1 ];
1498 prevGlyphIndex = -1;
1499 blurradius = state->blurradius;
1500 blurscale = state->blurscale;
1501 if( !( state->font ) )
1504 if( stringlength <= 0 )
1505 stringlength = strlen( string );
1510 bestdistance = abs( targetwidth );
1511 for( index = 0 ; index < stringlength ; index++ )
1513 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1515 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale, false );
1518 font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1519 addGlyphAdvance( &advance, &subpixel, glyph );
1520 distance = abs( targetwidth - ( advance + ( subpixel >= 32 ) ) );
1521 if( distance > bestdistance )
1523 bestdistance = distance;
1524 truncateindex = index + 1;
1526 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1529 return truncateindex;
1534 static void flush( bool rendererFlush )
1536 // Flush texture updates
1537 if( ( dirtyrect[0] < dirtyrect[2] ) && ( dirtyrect[1] < dirtyrect[3] ) )
1539 renderer.updateTexture( dirtyrect, texdata );
1541 dirtyrect[0] = width;
1542 dirtyrect[1] = height;
1546 // Flush buffered glyphs
1547 if( rendererFlush ) renderer.flush( );
1550 // Flush buffered texture updates, renderer->updateTexture()
1560 void getFontExtent( int *retascent, int *retdescent )
1563 FMState *state = &states[ nstates - 1 ];
1569 *retascent = -(int)ceilf( font.ascender * (float)state->size );
1571 *retdescent = -(int)floorf( font.descender * (float)state->size );
1575 void getFontLimits( int *retlimitminy, int *retlimitmaxy )
1580 state = &states[ nstates - 1 ];
1581 if( !( state->font ) )
1586 *retlimitminy = -(int)ceilf( font.limitmaxy * state->size );
1588 *retlimitmaxy = -(int)floorf( font.limitminy * state->size );
1593 int getFontLineHeight( )
1598 state = &states[ nstates - 1 ];
1599 if( !( state->font ) )
1602 return (int)ceilf( font.lineHeight * state->size );
1608 // Pull texture changes
1609 const byte *getTextureData( int *width, int *height )
1612 *width = this.width;
1614 *height = this.height;
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 )
1621 if( ( dirtyrect[0] < dirtyrect[2] ) && ( dirtyrect[1] < dirtyrect[3] ) )
1623 retdirtyrect[0] = dirtyrect[0];
1624 retdirtyrect[1] = dirtyrect[1];
1625 retdirtyrect[2] = dirtyrect[2];
1626 retdirtyrect[3] = dirtyrect[3];
1628 dirtyrect[0] = width;
1629 dirtyrect[1] = height;
1637 // Returns current atlas size
1638 void getAtlasSize( int *retwidth, int *retheight )
1640 *retwidth = this.width;
1641 *retheight = this.height;
1645 // Expands the atlas size
1646 bool expandAtlas( int width, int height )
1648 width = Max( width, this.width );
1649 height = Max( height, this.height );
1651 if( ( width == this.width ) && ( height == this.height ) )
1654 // Flush all pending glyphs
1657 // Create new texture
1658 if( renderer.resizeTexture( width, height ) )
1663 // Copy old texture data over.
1664 if( !( data = new byte[width * bytesperline] ) )
1666 for( i = 0 ; i < this.height ; i++ )
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 );
1674 if( height > this.height )
1675 memset( &data[ width * this.height * bytesperpixel], 0, ( height - this.height ) *bytesperline );
1677 delete this.texdata;
1680 // Increase atlas size
1681 atlas.expand( width, height );
1683 // Add existing data as dirty.
1686 dirtyrect[2] = this.width;
1687 dirtyrect[3] = atlas.getAtlasMaxHeight();
1690 this.height = height;
1691 this.bytesperline = this.width * bytesperpixel;
1692 widthinv = 1.0f / this.width;
1693 heightinv = 1.0f / this.height;
1700 // Reset the whole fm
1701 bool resetAtlas( int width, int height )
1703 // Flush all pending glyphs
1706 // Create new texture
1707 if(renderer.resizeTexture( width, height ) )
1710 atlas.reset( width, height );
1712 // Clear texture data.
1713 texdata = renew texdata byte[width * height * bytesperpixel];
1714 if(!texdata) return 0;
1715 memset( this.texdata, 0, width * height );
1718 dirtyrect[0] = width;
1719 dirtyrect[1] = height;
1723 // Reset cached glyphs
1724 for(font : fontList)
1727 font.glyphcount = 0;
1728 for( i = 0 ; i < FM_HASH_TABLE_SIZE ; i++ )
1729 font.hashtable[i] = -1;
1731 renderer.resetImages( );
1734 this.height = height;
1735 this.bytesperline = width * bytesperpixel;
1736 this.widthinv = 1.0f / this.width;
1737 this.heightinv = 1.0f / this.height;
1745 bool initPathDraw( FMPathDraw pathdraw )
1747 FMState *state = &states[ nstates - 1 ];
1748 FMFont font = state->font;
1751 pathdraw.prevGlyphIndex = -1;
1752 pathdraw.middleAlign = font.middleAlign * (float)state->size;
1758 float pathDrawCharacter( FMPathDraw pathdraw, float x, float y, float vectorx, float vectory, int unicode )
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 );
1768 subpixel = font.ftFont.getGlyphKernAdvance( pathdraw.prevGlyphIndex, glyph->glyphindex );
1769 #if !defined(SHADERS)
1770 if(font.processImage)
1772 FMGlyph *outlineGlyph = getGlyph(font, unicode, state->size, 0, blurradius, blurscale, true );
1774 drawTextGlyphFloat(font, outlineGlyph, x, y, vectorx, vectory, (float)subpixel * (1.0f/64.0f), pathdraw.middleAlign, true );
1777 drawTextGlyphFloat(font, glyph, x, y, vectorx, vectory, (float)subpixel * (1.0f/64.0f), pathdraw.middleAlign, false );
1779 subpixel += glyph->advance;
1780 pathdraw.prevGlyphIndex = glyph->glyphindex;
1785 pathdraw.prevGlyphIndex = -1;
1788 return (float)subpixel * (1.0f/64.0f);
1791 float pathDrawPredictAdvance( FMPathDraw pathdraw, unichar unicode )
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 );
1801 subpixel = font.ftFont.getGlyphKernAdvance( pathdraw.prevGlyphIndex, glyph->glyphindex );
1802 subpixel += glyph->advance;
1804 return (float)subpixel * (1.0f/64.0f);