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 = new int[ width * height ];
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
246 virtual bool init(int channelCount);
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)
294 #define FM_HASH_TABLE_SIZE (4096)
295 #define FM_INIT_GLYPHS (1024)
296 #define FM_INIT_ATLAS_NODES (512)
298 #define FM_MAX_STATES (16)
303 struct FMFreeTypeFont
307 static inline int ::init()
310 ftError = FT_Init_FreeType( &ftLibrary2 );
314 static inline int loadFont( byte *data, int dataSize )
316 FT_Error ftError = FT_New_Memory_Face( ftLibrary2, (const FT_Byte*)data, dataSize, 0, &face );
322 FT_Done_Face( face );
325 static inline void getFontVMetrics( float *ascent, float *descent, float *lineHeight, float *limitminy, float *limitmaxy )
327 *ascent = (float)face->ascender;
328 *descent = (float)face->descender;
329 *lineHeight = (float)face->height / (float)face->units_per_EM;
330 *limitminy = (float)face->bbox.yMin / (float)face->units_per_EM;
331 *limitmaxy = (float)face->bbox.yMax / (float)face->units_per_EM;
334 static inline int getGlyphIndex( unichar codepoint )
336 return FT_Get_Char_Index( this.face, codepoint );
339 static int buildGlyphBitmap( int glyph, int size, int subpixel, int *advance, int *x0, int *y0, int *x1, int *y1 )
342 FT_GlyphSlot glyphslot;
345 ftError = FT_Set_Pixel_Sizes( this.face, 0, (FT_UInt)size );
349 subvector.x = subpixel;
351 FT_Set_Transform( face, 0, &subvector );
353 #if FM_ENABLE_HINTING
354 ftError = FT_Load_Glyph( face, glyph, FT_LOAD_RENDER );
356 ftError = FT_Load_Glyph( face, glyph, FT_LOAD_RENDER | FT_LOAD_NO_HINTING );
361 glyphslot = face->glyph;
362 *advance = glyphslot->metrics.horiAdvance;
363 *x0 = glyphslot->bitmap_left;
364 *x1 = glyphslot->bitmap_left + glyphslot->bitmap.width;
365 *y0 = -glyphslot->bitmap_top;
366 *y1 = -glyphslot->bitmap_top + glyphslot->bitmap.rows;
371 static inline int getGlyphKernAdvance( int glyph1, int glyph2 )
374 FT_Get_Kerning( face, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning );
378 static inline byte *getGlyphBitmap( int glyphindex )
380 FT_GlyphSlot glyphslot;
381 glyphslot = face->glyph;
382 return glyphslot->bitmap.buffer;
386 #define FM_DEF_CODEPOINT_BITS (32)
387 #define FM_DEF_SIZE_BITS (12)
388 #define FM_DEF_SUBPIXEL_BITS (6)
389 #define FM_DEF_BLURRADIUS_BITS (8)
390 #define FM_DEF_BLURSCALE_BITS (6)
392 #define FM_DEF_CODEPOINT_SHIFT (0)
393 #define FM_DEF_SIZE_SHIFT (FM_DEF_CODEPOINT_BITS)
394 #define FM_DEF_SUBPIXEL_SHIFT (FM_DEF_CODEPOINT_BITS+FM_DEF_SIZE_BITS)
395 #define FM_DEF_BLURRADIUS_SHIFT (FM_DEF_CODEPOINT_BITS+FM_DEF_SIZE_BITS+FM_DEF_SUBPIXEL_BITS)
396 #define FM_DEF_BLURSCALE_SHIFT (FM_DEF_CODEPOINT_BITS+FM_DEF_SIZE_BITS+FM_DEF_SUBPIXEL_BITS+FM_DEF_BLURRADIUS_BITS)
398 #define FM_GLYPH_COMPUTE_DEF(codepoint,size,subpixel,blurradius,blurscale) ( (((uint64)(codepoint))<<FM_DEF_CODEPOINT_SHIFT) | (((uint64)(size))<<FM_DEF_SIZE_SHIFT) | (((uint64)(subpixel))<<FM_DEF_SUBPIXEL_SHIFT) | (((uint64)(blurradius))<<FM_DEF_BLURRADIUS_SHIFT) | (((uint64)(blurscale))<<FM_DEF_BLURSCALE_SHIFT) )
400 #define FM_SIZE_MAX ((1<<FM_DEF_SIZE_BITS)-1)
401 #define FM_BLUR_RADIUS_MAX ((1<<FM_DEF_BLURRADIUS_BITS)-1)
402 #define FM_BLUR_SCALE_MAX ((1<<FM_DEF_BLURSCALE_BITS)-1)
404 #define FM_GLYPH_CODEPOINT_CURSOR (0x1)
405 #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 );
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 )
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 );
502 dstrow[0] = (unsigned char)roundf( fmaxf( 0.0f, fminf( 255.0f, alpha * 255.0f ) ) );
503 /* Intensity channel */
504 dstrow[1] = (unsigned char)roundf( fmaxf( 0.0f, fminf( 255.0f, intensity * 255.0f ) ) );
505 dstrow += bytesperpixel;
513 public void setOutline(float intensityFactor, float alphaFactor, float radius) // TODO: Figure out radius?
515 outlineIntensityFactor = intensityFactor;
516 outlineAlphaFactor = alphaFactor;
517 outlineRadius = radius;
518 processImage = outlineProcessGlyphImage;
519 processImageContext = this;
529 static FMGlyph *allocGlyph( )
531 if( glyphcount >= glyphalloc )
534 if( !(glyphs = renew glyphs FMGlyph[glyphalloc]) )
537 return &glyphs[ glyphcount++ ];
542 static float getVertAlign( FMTextAlignment align, int size )
545 if( align.vertAlignment == top )
546 return ascender * sizef;
547 else if( align.vertAlignment == middle )
548 return middleAlign * sizef;
549 else if( align.vertAlignment == bottom )
550 return descender * sizef;
554 static inline void addKerning( int prevGlyphIndex, FMGlyph *glyph, int *x, int *subpixel )
556 if( prevGlyphIndex != -1 )
558 *subpixel += ftFont.getGlyphKernAdvance( prevGlyphIndex, glyph->glyphindex );
559 #if FM_SUBPIXEL_ROUNDING_RANGE
560 *subpixel = ( *subpixel + (FM_SUBPIXEL_ROUNDING_RANGE>>1) ) & ~(FM_SUBPIXEL_ROUNDING_RANGE-1);
562 *x += *subpixel >> 6;
573 FMTextAlignment align;
581 static FT_Library ftLibrary2;
583 static void copyGlyphBitmap1( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride )
586 for( y = 0 ; y < glyphheight ; y++ )
588 byte *dstrow = &dst[ y * dststride ];
589 for( x = 0 ; x < glyphwidth ; x++ )
590 dstrow[ x ] = src[ x ];
595 static void copyGlyphBitmap2( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride )
598 for( y = 0 ; y < glyphheight ; y++ )
600 byte *dstrow = &dst[ y * dststride ];
601 for( x = 0 ; x < glyphwidth ; x++ )
602 dstrow[ x << 1 ] = src[ x ];
607 static void copyGlyphBitmap4( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride )
610 for( y = 0 ; y < glyphheight ; y++ )
612 byte *dstrow = &dst[ y * dststride ];
613 for( x = 0 ; x < glyphwidth ; x++ )
614 dstrow[ x << 2 ] = src[ x ];
619 public class FontManager
621 FontManagerRenderer renderer;
623 float widthinv, heightinv;
628 AtlasBuilder atlas { };
632 LinkList<FMFont, link = link> fontList { };
634 FMState states[FM_MAX_STATES];
637 void (*copyGlyphBitmap)( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride );
640 static FMGlyph *getGlyph( FMFont font, unichar codepoint, int size, int subpixel, int blurradius, int blurscale )
642 int i, glyphindex, advance, x0, y0, x1, y1, gx, gy;
643 int glyphwidth, glyphheight, glyphareawidth, glyphareaheight;
654 padding = blurradius + font.glyphPaddingWidth;
656 /* Find code point and size. */
657 glyphdef = FM_GLYPH_COMPUTE_DEF( codepoint, size, subpixel, blurradius, blurscale );
658 hashindex = ccHash32Int64Inline( glyphdef ) & ( FM_HASH_TABLE_SIZE - 1 );
659 i = font.hashtable[hashindex];
662 if( glyphdef == font.glyphs[i].glyphdef )
663 return &font.glyphs[i];
664 i = font.glyphs[i].listnext;
667 /* Could not find glyph, create it */
668 if( codepoint == FM_GLYPH_CODEPOINT_CURSOR )
679 y0 = -(int)ceilf( font.limitmaxy * (float)size );
680 y1 = -(int)floorf( font.limitminy * (float)size );
681 i = ( (y1 - y0) - size ) / 3;
687 glyphindex = font.ftFont.getGlyphIndex( codepoint );
688 if( !( font.ftFont.buildGlyphBitmap( glyphindex, size, subpixel, &advance, &x0, &y0, &x1, &y1 ) ) )
690 if( codepoint != FM_GLYPH_CODEPOINT_REPLACEMENT )
691 return getGlyph(font, FM_GLYPH_CODEPOINT_REPLACEMENT, size, subpixel, blurradius, blurscale );
695 glyphwidth = ( x1 - x0 );
696 glyphheight = ( y1 - y0 );
697 glyphareawidth = glyphwidth + (padding << 1);
698 glyphareaheight = glyphheight + (padding << 1);
700 // Find free spot for the rect in the atlas
701 added = atlas.addRect( glyphareawidth, glyphareaheight, &gx, &gy );
702 if( !( added ) && ( onAtlasFull ) )
704 /* Atlas is full, let the user to resize the atlas (or not), and try again. */
706 added = atlas.addRect( glyphareawidth, glyphareaheight, &gx, &gy );
711 /* Build the glyph */
712 glyph = font.allocGlyph();
713 glyph->glyphdef = glyphdef;
714 glyph->glyphindex = glyphindex;
715 glyph->x0 = (short)gx;
716 glyph->y0 = (short)gy;
717 glyph->x1 = (short)( glyph->x0 + glyphareawidth );
718 glyph->y1 = (short)( glyph->y0 + glyphareaheight );
719 glyph->advance = (short)advance;
720 glyph->offsetx = (short)( x0 - padding );
721 glyph->offsety = (short)( y0 - padding );
723 glyph->imageIndex = -1;
724 if( renderer.registerImage )
725 glyph->imageIndex = renderer.registerImage( gx, gy, glyphareawidth, glyphareaheight );
727 // Add char to hash table
728 glyph->listnext = font.hashtable[hashindex];
729 font.hashtable[hashindex] = font.glyphcount - 1;
731 // Clear glyph image area (TODO: wasteful when single channel without prepare callback?)
732 dst = &texdata[ ( glyph->x0 * bytesperpixel ) + ( glyph->y0 * bytesperline ) ];
733 for( i = 0 ; i < glyphareaheight ; i++, dst += bytesperline )
734 memset( dst, 0, glyphareawidth * bytesperpixel );
737 dst = &texdata[ ( glyph->x0 + padding ) * bytesperpixel + ( ( glyph->y0 + padding ) * bytesperline ) + channelindex];
738 if( codepoint == FM_GLYPH_CODEPOINT_CURSOR )
740 src = new byte[ glyphwidth * glyphheight ];
741 buildCursorGlyph( src, glyphwidth, glyphheight, glyphwidth );
742 copyGlyphBitmap( dst, src, glyphwidth, glyphheight, bytesperline );
747 src = font.ftFont.getGlyphBitmap(glyphindex);
748 copyGlyphBitmap( dst, src, glyphwidth, glyphheight, bytesperline );
755 printf( "Do NOT use the blur! This will be removed in favor of external image processing\n" );
757 bdst = &texdata[ glyph->x0 * bytesperpixel + ( glyph->y0 * bytesperline ) ];
759 blur(bdst, glyphareawidth, glyphareaheight, this.width, blurradius );
761 blurScale(bdst, glyphareawidth, glyphareaheight, this.width, blurradius, blurscale );
764 // User custom font image processing
765 if(font.processImage)
767 dst = &texdata[ ( glyph->x0 * bytesperpixel ) + ( glyph->y0 * bytesperline ) ];
768 font.processImage( font.processImageContext, dst, glyphareawidth, glyphareaheight, bytesperpixel, bytesperline, font.glyphPaddingWidth );
771 dirtyrect[0] = Min( dirtyrect[0], glyph->x0 );
772 dirtyrect[1] = Min( dirtyrect[1], glyph->y0 );
773 dirtyrect[2] = Max( dirtyrect[2], glyph->x1 );
774 dirtyrect[3] = Max( dirtyrect[3], glyph->y1 );
779 static inline void drawTextGlyph( FMFont font, FMGlyph *glyph, int x, int y )
781 int ptx = x + glyph->offsetx;
782 int pty = y + glyph->offsety;
783 if( renderer.drawImage )
784 renderer.drawImage( ptx, pty, glyph->imageIndex );
785 else if( renderer.drawImageAlt )
786 renderer.drawImageAlt( texdata, ptx, pty, glyph->x0, glyph->y0, glyph->x1 - glyph->x0, glyph->y1 - glyph->y0 );
789 static inline void drawTextCursorGlyph( FMFont font, FMGlyph *glyph, int x, int y )
792 ptx = x + glyph->offsetx;
793 pty = y + glyph->offsety;
794 if( renderer.drawImageCursor )
795 renderer.drawImageCursor( ptx, pty, glyph->imageIndex );
796 else if( renderer.drawImageAlt )
797 renderer.drawImageAlt( texdata, ptx, pty, glyph->x0, glyph->y0, glyph->x1 - glyph->x0, glyph->y1 - glyph->y0 );
800 static inline void drawTextGlyphFloat( FMFont font, FMGlyph *glyph, float x, float y, float vectorx, float vectory, float offsetx, float offsety )
802 float vectx = (float)glyph->offsetx + offsetx;
803 float vecty = (float)glyph->offsety + offsety;
804 float ptx = x + ( vectorx * vectx ) - ( vectory * vecty );
805 float pty = y + ( vectorx * vecty ) + ( vectory * vectx );
806 renderer.drawImageFloat( ptx, pty, vectory, vectorx, glyph->imageIndex );
811 property FontManagerRenderer renderer { set { renderer = value; } get { return renderer; } }
813 // When notified of a full atlas, you should call fmExpandAtlas() or fmResetAtlas() within the callback
814 virtual void (*onAtlasFull)();
816 // Create and destroy font manager
817 bool create( int width, int height, int channelCount, int channelIndex, FontManagerRenderer renderer)
821 if( ( channelCount != 1 ) && ( channelCount != 2 ) && ( channelCount != 4 ) )
823 if( ( width <= 0 ) || ( height <= 0 ) )
826 this.renderer = renderer;
828 renderer.init(channelCount);
830 // Initialize implementation library
831 if(FMFreeTypeFont::init() )
834 this.height = height;
835 bytesperpixel = channelCount;
836 bytesperline = width * bytesperpixel;
837 this.channelindex = channelIndex;
838 if(renderer.createTexture( width, height ))
840 if((atlas.create( this.width, this.height, FM_INIT_ATLAS_NODES )))
842 // Create texture for the cache.
843 widthinv = 1.0f / width;
844 heightinv = 1.0f / height;
845 texdata = new byte[width * height * bytesperpixel];
848 memset( texdata, 0, height * bytesperline );
850 dirtyrect[0] = this.width;
851 dirtyrect[1] = this.height;
855 if( bytesperpixel == 1 )
856 copyGlyphBitmap = copyGlyphBitmap1;
857 else if( bytesperpixel == 2 )
858 copyGlyphBitmap = copyGlyphBitmap2;
860 copyGlyphBitmap = copyGlyphBitmap4;
887 void setState( FMFont font, int size, int align, int blurradius, int blurscale )
890 if( size >= FM_SIZE_MAX )
892 if( blurradius >= FM_BLUR_RADIUS_MAX )
893 blurradius = FM_BLUR_RADIUS_MAX;
894 else if( blurradius < 0 )
896 if( blurscale >= FM_BLUR_SCALE_MAX )
897 blurscale = FM_BLUR_SCALE_MAX;
898 else if( blurscale < 1 )
900 state = &states[ nstates - 1 ];
902 state->size = (uint16)size;
903 state->align = (uint16)align;
904 state->blurradius = (uint16)blurradius;
905 state->blurscale = (uint16)blurscale;
908 void setFont( FMFont font )
910 states[ nstates - 1 ].font = font;
913 void setSize( int size )
915 if( size >= FM_SIZE_MAX )
918 states[ nstates - 1 ].size = (uint16)size;
921 void setAlign( int align )
923 states[ nstates - 1 ].align = (uint16)align;
926 void setBlur( int radius, int scale )
928 if( radius >= FM_BLUR_RADIUS_MAX )
929 radius = FM_BLUR_RADIUS_MAX;
930 else if( radius < 0 )
932 if( scale >= FM_BLUR_SCALE_MAX )
933 scale = FM_BLUR_SCALE_MAX;
936 states[ nstates - 1 ].blurradius = (uint16)radius;
937 states[ nstates - 1 ].blurscale = (uint16)scale;
940 // Set image manipuation callback
941 void setFontImageProcessing( FMFont font, void (*processImage)( byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingwidth, void *opaquecontext ), void *opaquecontext )
949 if(nstates < FM_MAX_STATES)
952 memcpy( &states[nstates], &states[nstates-1], sizeof(FMState) );
965 FMState *state = &states[ nstates - 1 ];
968 state->blurradius = 0;
969 state->blurscale = 1;
970 state->align = { left, baseline };
976 static void freeFont(FMFont font)
980 fontList.Remove((IteratorPointer)font);
985 // Add font from file
986 FMFont addFont(const String path, int glyphPaddingWidth )
989 File f = FileOpen(path, read);
992 // Read in the font data
993 int dataSize = f.GetSize();
994 byte *data = new byte[dataSize];
997 f.Read(data, 1, dataSize);
998 font = addFontData(data, dataSize, glyphPaddingWidth);
1007 // Add font from data ; do not free( data ), the font manager will do that when removing the font
1008 FMFont addFontData( byte *data, int dataSize, int glyphPaddingWidth )
1012 glyphs = new FMGlyph[FM_INIT_GLYPHS];
1013 glyphalloc = FM_INIT_GLYPHS;
1014 glyphPaddingWidth = glyphPaddingWidth;
1019 float ascent, descent, fontHeight;
1024 for( i = 0 ; i < FM_HASH_TABLE_SIZE ; i++ )
1025 font.hashtable[i] = -1;
1028 if(!font.ftFont.loadFont(data, dataSize))
1034 // Store normalized line height. The real line height is got by multiplying by font size.
1035 font.ftFont.getFontVMetrics( &ascent, &descent, &font.lineHeight, &font.limitminy, &font.limitmaxy );
1036 fontHeight = ascent - descent;
1037 font.ascender = ascent / fontHeight;
1038 font.descender = descent / fontHeight;
1039 font.middleAlign = 0.5f * ( font.ascender + font.descender );
1040 font.fontdata = data;
1046 void removeFont( FMFont font )
1052 int drawText( int x, int y, const char *string, int stringlength )
1054 int subpixel, index;
1060 int blurradius, blurscale;
1063 state = &states[ nstates - 1 ];
1066 prevGlyphIndex = -1;
1067 blurradius = state->blurradius;
1068 blurscale = state->blurscale;
1069 if( !( state->font ) )
1072 if( !( stringlength ) )
1073 stringlength = strlen( string );
1075 // Align horizontally
1076 if( state->align.horzAlignment == right )
1077 x -= getTextWidth(string, stringlength );
1078 else if( state->align.horzAlignment == center )
1079 x -= getTextWidth(string, stringlength ) >> 1;
1082 y += roundf( font.getVertAlign(state->align, state->size ) );
1085 for( index = 0 ; index < stringlength ; index++ )
1087 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1089 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1092 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1093 drawTextGlyph(font, glyph, x, y );
1094 addGlyphAdvance( &x, &subpixel, glyph );
1096 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1099 return x + ( subpixel >= 32 );
1102 int drawTextWithCursor( int x, int y, const char *string, int stringlength, int cursoroffset )
1104 int subpixel, index;
1110 int blurradius, blurscale;
1113 state = &states[ nstates - 1 ];
1116 prevGlyphIndex = -1;
1117 blurradius = state->blurradius;
1118 blurscale = state->blurscale;
1119 if( !( state->font ) )
1122 if( !( stringlength ) )
1123 stringlength = strlen( string );
1125 // Align horizontally
1126 if( state->align.horzAlignment == right )
1127 x -= getTextWidth(string, stringlength );
1128 else if( state->align.horzAlignment == center )
1129 x -= getTextWidth(string, stringlength ) >> 1;
1132 y += roundf( font.getVertAlign(state->align, state->size ) );
1135 for( index = 0 ; ; index++ )
1137 if( index == cursoroffset )
1139 glyph = getGlyph(font, FM_GLYPH_CODEPOINT_CURSOR, state->size, subpixel, blurradius, blurscale );
1141 drawTextCursorGlyph(font, glyph, x, y );
1143 if( index >= stringlength )
1145 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1147 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1150 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1151 drawTextGlyph(font, glyph, x, y );
1152 addGlyphAdvance( &x, &subpixel, glyph );
1154 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1157 return x + ( subpixel >= 32 );
1160 int drawTextTruncate( int x, int y, int truncatewidth, const char *string, int stringlength, char *extstring, int extwidth )
1162 int subpixel, index, textwidth, truncatepoint;
1168 int blurradius, blurscale;
1171 state = &states[ nstates - 1 ];
1174 prevGlyphIndex = -1;
1175 blurradius = state->blurradius;
1176 blurscale = state->blurscale;
1177 if( !( state->font ) )
1180 if( !( stringlength ) )
1181 stringlength = strlen( string );
1182 textwidth = getTextWidthTruncate(string, stringlength, truncatewidth );
1184 truncatepoint = x + truncatewidth;
1185 if( textwidth <= truncatewidth )
1189 if( extwidth >= truncatewidth )
1191 truncatepoint -= extwidth;
1194 // Align horizontally
1195 if( state->align.horzAlignment == right )
1197 else if( state->align.horzAlignment == center )
1198 x -= textwidth >> 1;
1201 y += roundf( font.getVertAlign(state->align, state->size ) );
1204 for( index = 0 ; index < stringlength ; index++ )
1206 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1208 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1211 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1212 drawTextGlyph(font, glyph, x, y );
1213 addGlyphAdvance( &x, &subpixel, glyph );
1214 if( x > truncatepoint )
1217 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1219 x += ( subpixel >= 32 );
1221 drawText(x, y, extstring, 0 );
1231 int getTextWidth( const char *string, int stringlength )
1233 return getTextWidthTruncate(string, stringlength, ( (unsigned)1 << ( ( sizeof(int) * CHAR_BIT ) - 1 ) ) - 1 );
1237 int getTextWidthTruncate( const char *string, int stringlength, int truncatewidth )
1239 int subpixel, index, advance;
1245 int blurradius, blurscale;
1248 state = &states[ nstates - 1 ];
1251 prevGlyphIndex = -1;
1252 blurradius = state->blurradius;
1253 blurscale = state->blurscale;
1254 if( !( state->font ) )
1257 if( !( stringlength ) )
1258 stringlength = strlen( string );
1262 for( index = 0 ; index < stringlength ; index++ )
1264 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1266 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1269 font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1270 addGlyphAdvance( &advance, &subpixel, glyph );
1271 if( advance > truncatewidth )
1274 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1277 return advance + ( subpixel >= 32 );
1280 int getTextBounds( int x, int y, const char *string, int stringlength, int *bounds )
1282 int subpixel, index;
1289 int blurradius, blurscale;
1291 int startx, advance;
1292 int minx, miny, maxx, maxy;
1294 state = &states[ nstates - 1 ];
1297 prevGlyphIndex = -1;
1298 blurradius = state->blurradius;
1299 blurscale = state->blurscale;
1300 if( !( state->font ) )
1303 if( !( stringlength ) )
1304 stringlength = strlen( string );
1307 y += font.getVertAlign(state->align, state->size );
1314 for( index = 0 ; index < stringlength ; index++ )
1316 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1318 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1321 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1322 glyph->getQuad(x, y, q);
1331 addGlyphAdvance( &x, &subpixel, glyph );
1333 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1336 advance = x - startx;
1338 /* Align horizontally */
1339 if( state->align.horzAlignment == right )
1344 else if( state->align.horzAlignment == center )
1346 minx -= advance * 0.5f;
1347 maxx -= advance * 0.5f;
1358 return advance + ( subpixel >= 32 );
1361 // Find text offset up to truncatewidth
1362 int getTextTruncateOffset( int truncatewidth, const char *string, int stringlength, int extwidth, int *retextflag, int *retfullwidth )
1364 int subpixel, index, advance, truncatewidthext, truncateindex, extflag, fullwidth = 0;
1370 int blurradius, blurscale;
1373 state = &states[ nstates - 1 ];
1378 if( extwidth >= truncatewidth )
1382 prevGlyphIndex = -1;
1383 blurradius = state->blurradius;
1384 blurscale = state->blurscale;
1385 if( !( state->font ) )
1388 if( stringlength <= 0 )
1389 stringlength = strlen( string );
1391 truncatewidthext = truncatewidth - extwidth;
1397 for( index = 0 ; ; index++ )
1399 if( index >= stringlength )
1401 truncateindex = index;
1402 fullwidth = advance + ( subpixel >= 32 );
1405 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1407 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1410 font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1411 addGlyphAdvance( &advance, &subpixel, glyph );
1412 if( advance > truncatewidth )
1417 if( advance <= truncatewidthext )
1419 truncateindex = index + 1;
1420 fullwidth = advance + ( subpixel >= 32 );
1423 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1429 *retfullwidth = fullwidth;
1431 *retfullwidth = fullwidth;
1434 *retextflag = extflag;
1436 return truncateindex;
1439 // Find text offset nearest to the given width
1440 int getTextNearestOffset( int targetwidth, const char *string, int stringlength )
1442 int subpixel, index, advance, truncateindex, distance, bestdistance;
1448 int blurradius, blurscale;
1451 state = &states[ nstates - 1 ];
1454 prevGlyphIndex = -1;
1455 blurradius = state->blurradius;
1456 blurscale = state->blurscale;
1457 if( !( state->font ) )
1460 if( stringlength <= 0 )
1461 stringlength = strlen( string );
1466 bestdistance = abs( targetwidth );
1467 for( index = 0 ; index < stringlength ; index++ )
1469 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1471 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1474 font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1475 addGlyphAdvance( &advance, &subpixel, glyph );
1476 distance = abs( targetwidth - ( advance + ( subpixel >= 32 ) ) );
1477 if( distance > bestdistance )
1479 bestdistance = distance;
1480 truncateindex = index + 1;
1482 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1485 return truncateindex;
1490 static void flush( bool rendererFlush )
1492 // Flush texture updates
1493 if( ( dirtyrect[0] < dirtyrect[2] ) && ( dirtyrect[1] < dirtyrect[3] ) )
1495 renderer.updateTexture( dirtyrect, texdata );
1497 dirtyrect[0] = width;
1498 dirtyrect[1] = height;
1502 // Flush buffered glyphs
1503 if( rendererFlush ) renderer.flush( );
1506 // Flush buffered texture updates, renderer->updateTexture()
1516 void getFontExtent( int *retascent, int *retdescent )
1519 FMState *state = &states[ nstates - 1 ];
1525 *retascent = -(int)ceilf( font.ascender * (float)state->size );
1527 *retdescent = -(int)floorf( font.descender * (float)state->size );
1531 void getFontLimits( int *retlimitminy, int *retlimitmaxy )
1536 state = &states[ nstates - 1 ];
1537 if( !( state->font ) )
1542 *retlimitminy = -(int)ceilf( font.limitmaxy * state->size );
1544 *retlimitmaxy = -(int)floorf( font.limitminy * state->size );
1549 int getFontLineHeight( )
1554 state = &states[ nstates - 1 ];
1555 if( !( state->font ) )
1558 return (int)ceilf( font.lineHeight * state->size );
1564 // Pull texture changes
1565 const byte *getTextureData( int *width, int *height )
1568 *width = this.width;
1570 *height = this.height;
1574 // Retrieve the dirty rectangle, telling fontmanager you will manually perform the update, returns 0 if no part of the texture requires an update
1575 bool validateTexture( int *retdirtyrect )
1577 if( ( dirtyrect[0] < dirtyrect[2] ) && ( dirtyrect[1] < dirtyrect[3] ) )
1579 retdirtyrect[0] = dirtyrect[0];
1580 retdirtyrect[1] = dirtyrect[1];
1581 retdirtyrect[2] = dirtyrect[2];
1582 retdirtyrect[3] = dirtyrect[3];
1584 dirtyrect[0] = width;
1585 dirtyrect[1] = height;
1593 // Returns current atlas size
1594 void getAtlasSize( int *retwidth, int *retheight )
1596 *retwidth = this.width;
1597 *retheight = this.height;
1601 // Expands the atlas size
1602 bool expandAtlas( int width, int height )
1604 width = Max( width, this.width );
1605 height = Max( height, this.height );
1607 if( ( width == this.width ) && ( height == this.height ) )
1610 // Flush all pending glyphs
1613 // Create new texture
1614 if( renderer.resizeTexture( width, height ) )
1619 // Copy old texture data over.
1620 if( !( data = new byte[width * bytesperline] ) )
1622 for( i = 0 ; i < this.height ; i++ )
1624 byte * dst = &data[ (i * width) * bytesperpixel ];
1625 byte * src = &this.texdata[ i * this.bytesperline ];
1626 memcpy( dst, src, bytesperline);
1627 if( width > this.width )
1628 memset( dst+bytesperline, 0, (width - this.width) * bytesperpixel );
1630 if( height > this.height )
1631 memset( &data[ width * this.height * bytesperpixel], 0, ( height - this.height ) *bytesperline );
1633 delete this.texdata;
1636 // Increase atlas size
1637 atlas.expand( width, height );
1639 // Add existing data as dirty.
1642 dirtyrect[2] = this.width;
1643 dirtyrect[3] = atlas.getAtlasMaxHeight();
1646 this.height = height;
1647 this.bytesperline = this.width * bytesperpixel;
1648 widthinv = 1.0f / this.width;
1649 heightinv = 1.0f / this.height;
1656 // Reset the whole fm
1657 bool resetAtlas( int width, int height )
1659 // Flush all pending glyphs
1662 // Create new texture
1663 if(renderer.resizeTexture( width, height ) )
1666 atlas.reset( width, height );
1668 // Clear texture data.
1669 texdata = renew texdata byte[width * height * bytesperpixel];
1670 if(!texdata) return 0;
1671 memset( this.texdata, 0, width * height );
1674 dirtyrect[0] = width;
1675 dirtyrect[1] = height;
1679 // Reset cached glyphs
1680 for(font : fontList)
1683 font.glyphcount = 0;
1684 for( i = 0 ; i < FM_HASH_TABLE_SIZE ; i++ )
1685 font.hashtable[i] = -1;
1687 renderer.resetImages( );
1690 this.height = height;
1691 this.bytesperline = width * bytesperpixel;
1692 this.widthinv = 1.0f / this.width;
1693 this.heightinv = 1.0f / this.height;
1701 bool initPathDraw( FMPathDraw pathdraw )
1703 FMState *state = &states[ nstates - 1 ];
1704 FMFont font = state->font;
1707 pathdraw.prevGlyphIndex = -1;
1708 pathdraw.middleAlign = font.middleAlign * (float)state->size;
1714 float pathDrawCharacter( FMPathDraw pathdraw, float x, float y, float vectorx, float vectory, int unicode )
1717 FMState *state = &states[ nstates - 1 ];
1718 int blurradius = state->blurradius;
1719 int blurscale = state->blurscale;
1720 FMFont font = state->font;
1721 FMGlyph *glyph = getGlyph(font, unicode, state->size, 0, blurradius, blurscale );
1724 subpixel = font.ftFont.getGlyphKernAdvance( pathdraw.prevGlyphIndex, glyph->glyphindex );
1725 drawTextGlyphFloat(font, glyph, x, y, vectorx, vectory, (float)subpixel * (1.0f/64.0f), pathdraw.middleAlign );
1726 subpixel += glyph->advance;
1727 pathdraw.prevGlyphIndex = glyph->glyphindex;
1732 pathdraw.prevGlyphIndex = -1;
1735 return (float)subpixel * (1.0f/64.0f);
1738 float pathDrawPredictAdvance( FMPathDraw pathdraw, unichar unicode )
1741 FMState *state = &states[ nstates - 1 ];
1742 FMFont font = state->font;
1743 int blurradius = state->blurradius;
1744 int blurscale = state->blurscale;
1745 FMGlyph *glyph = getGlyph(font, unicode, state->size, 0, blurradius, blurscale );
1748 subpixel = font.ftFont.getGlyphKernAdvance( pathdraw.prevGlyphIndex, glyph->glyphindex );
1749 subpixel += glyph->advance;
1751 return (float)subpixel * (1.0f/64.0f);