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 VerticalAlignment { baseline, top, middle, bottom };
409 public class FMTextAlignment : uint16
412 Alignment horzAlignment:2;
413 VerticalAlignment 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;
909 void setFont( FMFont font )
911 states[ nstates - 1 ].font = font;
914 void setSize( int size )
916 if( size >= FM_SIZE_MAX )
919 states[ nstates - 1 ].size = (uint16)size;
922 void setAlign( int align )
924 states[ nstates - 1 ].align = (uint16)align;
927 void setBlur( int radius, int scale )
929 if( radius >= FM_BLUR_RADIUS_MAX )
930 radius = FM_BLUR_RADIUS_MAX;
931 else if( radius < 0 )
933 if( scale >= FM_BLUR_SCALE_MAX )
934 scale = FM_BLUR_SCALE_MAX;
937 states[ nstates - 1 ].blurradius = (uint16)radius;
938 states[ nstates - 1 ].blurscale = (uint16)scale;
941 // Set image manipuation callback
942 void setFontImageProcessing( FMFont font, void (*processImage)( byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingwidth, void *opaquecontext ), void *opaquecontext )
950 if(nstates < FM_MAX_STATES)
953 memcpy( &states[nstates], &states[nstates-1], sizeof(FMState) );
966 FMState *state = &states[ nstates - 1 ];
969 state->blurradius = 0;
970 state->blurscale = 1;
971 state->align = { left, baseline };
977 static void freeFont(FMFont font)
981 fontList.Remove((IteratorPointer)font);
986 // Add font from file
987 FMFont addFont(const String path, int glyphPaddingWidth )
990 File f = FileOpen(path, read);
993 // Read in the font data
994 int dataSize = f.GetSize();
995 byte *data = new byte[dataSize];
998 f.Read(data, 1, dataSize);
999 font = addFontData(data, dataSize, glyphPaddingWidth);
1008 // Add font from data ; do not free( data ), the font manager will do that when removing the font
1009 FMFont addFontData( byte *data, int dataSize, int glyphPaddingWidth )
1013 glyphs = new FMGlyph[FM_INIT_GLYPHS];
1014 glyphalloc = FM_INIT_GLYPHS;
1015 glyphPaddingWidth = glyphPaddingWidth;
1020 float ascent, descent, fontHeight;
1025 for( i = 0 ; i < FM_HASH_TABLE_SIZE ; i++ )
1026 font.hashtable[i] = -1;
1029 if(!font.ftFont.loadFont(data, dataSize))
1035 // Store normalized line height. The real line height is got by multiplying by font size.
1036 font.ftFont.getFontVMetrics( &ascent, &descent, &font.lineHeight, &font.limitminy, &font.limitmaxy );
1037 fontHeight = ascent - descent;
1038 font.ascender = ascent / fontHeight;
1039 font.descender = descent / fontHeight;
1040 font.middleAlign = 0.5f * ( font.ascender + font.descender );
1041 font.fontdata = data;
1047 void removeFont( FMFont font )
1053 int drawText( int x, int y, const char *string, int stringlength )
1055 int subpixel, index;
1061 int blurradius, blurscale;
1064 state = &states[ nstates - 1 ];
1067 prevGlyphIndex = -1;
1068 blurradius = state->blurradius;
1069 blurscale = state->blurscale;
1070 if( !( state->font ) )
1073 if( !( stringlength ) )
1074 stringlength = strlen( string );
1076 // Align horizontally
1077 if( state->align.horzAlignment == right )
1078 x -= getTextWidth(string, stringlength );
1079 else if( state->align.horzAlignment == center )
1080 x -= getTextWidth(string, stringlength ) >> 1;
1083 y += roundf( font.getVertAlign(state->align, state->size ) );
1086 for( index = 0 ; index < stringlength ; index++ )
1088 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1090 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1093 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1094 drawTextGlyph(font, glyph, x, y );
1095 addGlyphAdvance( &x, &subpixel, glyph );
1097 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1100 return x + ( subpixel >= 32 );
1103 int drawTextWithCursor( int x, int y, const char *string, int stringlength, int cursoroffset )
1105 int subpixel, index;
1111 int blurradius, blurscale;
1114 state = &states[ nstates - 1 ];
1117 prevGlyphIndex = -1;
1118 blurradius = state->blurradius;
1119 blurscale = state->blurscale;
1120 if( !( state->font ) )
1123 if( !( stringlength ) )
1124 stringlength = strlen( string );
1126 // Align horizontally
1127 if( state->align.horzAlignment == right )
1128 x -= getTextWidth(string, stringlength );
1129 else if( state->align.horzAlignment == center )
1130 x -= getTextWidth(string, stringlength ) >> 1;
1133 y += roundf( font.getVertAlign(state->align, state->size ) );
1136 for( index = 0 ; ; index++ )
1138 if( index == cursoroffset )
1140 glyph = getGlyph(font, FM_GLYPH_CODEPOINT_CURSOR, state->size, subpixel, blurradius, blurscale );
1142 drawTextCursorGlyph(font, glyph, x, y );
1144 if( index >= stringlength )
1146 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1148 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1151 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1152 drawTextGlyph(font, glyph, x, y );
1153 addGlyphAdvance( &x, &subpixel, glyph );
1155 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1158 return x + ( subpixel >= 32 );
1161 int drawTextTruncate( int x, int y, int truncatewidth, const char *string, int stringlength, char *extstring, int extwidth )
1163 int subpixel, index, textwidth, truncatepoint;
1169 int blurradius, blurscale;
1172 state = &states[ nstates - 1 ];
1175 prevGlyphIndex = -1;
1176 blurradius = state->blurradius;
1177 blurscale = state->blurscale;
1178 if( !( state->font ) )
1181 if( !( stringlength ) )
1182 stringlength = strlen( string );
1183 textwidth = getTextWidthTruncate(string, stringlength, truncatewidth );
1185 truncatepoint = x + truncatewidth;
1186 if( textwidth <= truncatewidth )
1190 if( extwidth >= truncatewidth )
1192 truncatepoint -= extwidth;
1195 // Align horizontally
1196 if( state->align.horzAlignment == right )
1198 else if( state->align.horzAlignment == center )
1199 x -= textwidth >> 1;
1202 y += roundf( font.getVertAlign(state->align, state->size ) );
1205 for( index = 0 ; index < stringlength ; index++ )
1207 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1209 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1212 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1213 drawTextGlyph(font, glyph, x, y );
1214 addGlyphAdvance( &x, &subpixel, glyph );
1215 if( x > truncatepoint )
1218 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1220 x += ( subpixel >= 32 );
1222 drawText(x, y, extstring, 0 );
1232 int getTextWidth( const char *string, int stringlength )
1234 return getTextWidthTruncate(string, stringlength, ( (unsigned)1 << ( ( sizeof(int) * CHAR_BIT ) - 1 ) ) - 1 );
1238 int getTextWidthTruncate( const char *string, int stringlength, int truncatewidth )
1240 int subpixel, index, advance;
1246 int blurradius, blurscale;
1249 state = &states[ nstates - 1 ];
1252 prevGlyphIndex = -1;
1253 blurradius = state->blurradius;
1254 blurscale = state->blurscale;
1255 if( !( state->font ) )
1258 if( !( stringlength ) )
1259 stringlength = strlen( string );
1263 for( index = 0 ; index < stringlength ; index++ )
1265 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1267 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1270 font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1271 addGlyphAdvance( &advance, &subpixel, glyph );
1272 if( advance > truncatewidth )
1275 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1278 return advance + ( subpixel >= 32 );
1281 int getTextBounds( int x, int y, const char *string, int stringlength, int *bounds )
1283 int subpixel, index;
1290 int blurradius, blurscale;
1292 int startx, advance;
1293 int minx, miny, maxx, maxy;
1295 state = &states[ nstates - 1 ];
1298 prevGlyphIndex = -1;
1299 blurradius = state->blurradius;
1300 blurscale = state->blurscale;
1301 if( !( state->font ) )
1304 if( !( stringlength ) )
1305 stringlength = strlen( string );
1308 y += font.getVertAlign(state->align, state->size );
1315 for( index = 0 ; index < stringlength ; index++ )
1317 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1319 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1322 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1323 glyph->getQuad(x, y, q);
1332 addGlyphAdvance( &x, &subpixel, glyph );
1334 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1337 advance = x - startx;
1339 /* Align horizontally */
1340 if( state->align.horzAlignment == right )
1345 else if( state->align.horzAlignment == center )
1347 minx -= advance * 0.5f;
1348 maxx -= advance * 0.5f;
1359 return advance + ( subpixel >= 32 );
1362 // Find text offset up to truncatewidth
1363 int getTextTruncateOffset( int truncatewidth, const char *string, int stringlength, int extwidth, int *retextflag, int *retfullwidth )
1365 int subpixel, index, advance, truncatewidthext, truncateindex, extflag, fullwidth = 0;
1371 int blurradius, blurscale;
1374 state = &states[ nstates - 1 ];
1379 if( extwidth >= truncatewidth )
1383 prevGlyphIndex = -1;
1384 blurradius = state->blurradius;
1385 blurscale = state->blurscale;
1386 if( !( state->font ) )
1389 if( stringlength <= 0 )
1390 stringlength = strlen( string );
1392 truncatewidthext = truncatewidth - extwidth;
1398 for( index = 0 ; ; index++ )
1400 if( index >= stringlength )
1402 truncateindex = index;
1403 fullwidth = advance + ( subpixel >= 32 );
1406 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1408 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1411 font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1412 addGlyphAdvance( &advance, &subpixel, glyph );
1413 if( advance > truncatewidth )
1418 if( advance <= truncatewidthext )
1420 truncateindex = index + 1;
1421 fullwidth = advance + ( subpixel >= 32 );
1424 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1430 *retfullwidth = fullwidth;
1432 *retfullwidth = fullwidth;
1435 *retextflag = extflag;
1437 return truncateindex;
1440 // Find text offset nearest to the given width
1441 int getTextNearestOffset( int targetwidth, const char *string, int stringlength )
1443 int subpixel, index, advance, truncateindex, distance, bestdistance;
1449 int blurradius, blurscale;
1452 state = &states[ nstates - 1 ];
1455 prevGlyphIndex = -1;
1456 blurradius = state->blurradius;
1457 blurscale = state->blurscale;
1458 if( !( state->font ) )
1461 if( stringlength <= 0 )
1462 stringlength = strlen( string );
1467 bestdistance = abs( targetwidth );
1468 for( index = 0 ; index < stringlength ; index++ )
1470 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1472 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1475 font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1476 addGlyphAdvance( &advance, &subpixel, glyph );
1477 distance = abs( targetwidth - ( advance + ( subpixel >= 32 ) ) );
1478 if( distance > bestdistance )
1480 bestdistance = distance;
1481 truncateindex = index + 1;
1483 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1486 return truncateindex;
1491 static void flush( bool rendererFlush )
1493 // Flush texture updates
1494 if( ( dirtyrect[0] < dirtyrect[2] ) && ( dirtyrect[1] < dirtyrect[3] ) )
1496 renderer.updateTexture( dirtyrect, texdata );
1498 dirtyrect[0] = width;
1499 dirtyrect[1] = height;
1503 // Flush buffered glyphs
1504 if( rendererFlush ) renderer.flush( );
1507 // Flush buffered texture updates, renderer->updateTexture()
1517 void getFontExtent( int *retascent, int *retdescent )
1520 FMState *state = &states[ nstates - 1 ];
1526 *retascent = -(int)ceilf( font.ascender * (float)state->size );
1528 *retdescent = -(int)floorf( font.descender * (float)state->size );
1532 void getFontLimits( int *retlimitminy, int *retlimitmaxy )
1537 state = &states[ nstates - 1 ];
1538 if( !( state->font ) )
1543 *retlimitminy = -(int)ceilf( font.limitmaxy * state->size );
1545 *retlimitmaxy = -(int)floorf( font.limitminy * state->size );
1550 int getFontLineHeight( )
1555 state = &states[ nstates - 1 ];
1556 if( !( state->font ) )
1559 return (int)ceilf( font.lineHeight * state->size );
1565 // Pull texture changes
1566 const byte *getTextureData( int *width, int *height )
1569 *width = this.width;
1571 *height = this.height;
1575 // Retrieve the dirty rectangle, telling fontmanager you will manually perform the update, returns 0 if no part of the texture requires an update
1576 bool validateTexture( int *retdirtyrect )
1578 if( ( dirtyrect[0] < dirtyrect[2] ) && ( dirtyrect[1] < dirtyrect[3] ) )
1580 retdirtyrect[0] = dirtyrect[0];
1581 retdirtyrect[1] = dirtyrect[1];
1582 retdirtyrect[2] = dirtyrect[2];
1583 retdirtyrect[3] = dirtyrect[3];
1585 dirtyrect[0] = width;
1586 dirtyrect[1] = height;
1594 // Returns current atlas size
1595 void getAtlasSize( int *retwidth, int *retheight )
1597 *retwidth = this.width;
1598 *retheight = this.height;
1602 // Expands the atlas size
1603 bool expandAtlas( int width, int height )
1605 width = Max( width, this.width );
1606 height = Max( height, this.height );
1608 if( ( width == this.width ) && ( height == this.height ) )
1611 // Flush all pending glyphs
1614 // Create new texture
1615 if( renderer.resizeTexture( width, height ) )
1620 // Copy old texture data over.
1621 if( !( data = new byte[width * bytesperline] ) )
1623 for( i = 0 ; i < this.height ; i++ )
1625 byte * dst = &data[ (i * width) * bytesperpixel ];
1626 byte * src = &this.texdata[ i * this.bytesperline ];
1627 memcpy( dst, src, bytesperline);
1628 if( width > this.width )
1629 memset( dst+bytesperline, 0, (width - this.width) * bytesperpixel );
1631 if( height > this.height )
1632 memset( &data[ width * this.height * bytesperpixel], 0, ( height - this.height ) *bytesperline );
1634 delete this.texdata;
1637 // Increase atlas size
1638 atlas.expand( width, height );
1640 // Add existing data as dirty.
1643 dirtyrect[2] = this.width;
1644 dirtyrect[3] = atlas.getAtlasMaxHeight();
1647 this.height = height;
1648 this.bytesperline = this.width * bytesperpixel;
1649 widthinv = 1.0f / this.width;
1650 heightinv = 1.0f / this.height;
1657 // Reset the whole fm
1658 bool resetAtlas( int width, int height )
1660 // Flush all pending glyphs
1663 // Create new texture
1664 if(renderer.resizeTexture( width, height ) )
1667 atlas.reset( width, height );
1669 // Clear texture data.
1670 texdata = renew texdata byte[width * height * bytesperpixel];
1671 if(!texdata) return 0;
1672 memset( this.texdata, 0, width * height );
1675 dirtyrect[0] = width;
1676 dirtyrect[1] = height;
1680 // Reset cached glyphs
1681 for(font : fontList)
1684 font.glyphcount = 0;
1685 for( i = 0 ; i < FM_HASH_TABLE_SIZE ; i++ )
1686 font.hashtable[i] = -1;
1688 renderer.resetImages( );
1691 this.height = height;
1692 this.bytesperline = width * bytesperpixel;
1693 this.widthinv = 1.0f / this.width;
1694 this.heightinv = 1.0f / this.height;
1702 bool initPathDraw( FMPathDraw pathdraw )
1704 FMState *state = &states[ nstates - 1 ];
1705 FMFont font = state->font;
1708 pathdraw.prevGlyphIndex = -1;
1709 pathdraw.middleAlign = font.middleAlign * (float)state->size;
1715 float pathDrawCharacter( FMPathDraw pathdraw, float x, float y, float vectorx, float vectory, int unicode )
1718 FMState *state = &states[ nstates - 1 ];
1719 int blurradius = state->blurradius;
1720 int blurscale = state->blurscale;
1721 FMFont font = state->font;
1722 FMGlyph *glyph = getGlyph(font, unicode, state->size, 0, blurradius, blurscale );
1725 subpixel = font.ftFont.getGlyphKernAdvance( pathdraw.prevGlyphIndex, glyph->glyphindex );
1726 drawTextGlyphFloat(font, glyph, x, y, vectorx, vectory, (float)subpixel * (1.0f/64.0f), pathdraw.middleAlign );
1727 subpixel += glyph->advance;
1728 pathdraw.prevGlyphIndex = glyph->glyphindex;
1733 pathdraw.prevGlyphIndex = -1;
1736 return (float)subpixel * (1.0f/64.0f);
1739 float pathDrawPredictAdvance( FMPathDraw pathdraw, unichar unicode )
1742 FMState *state = &states[ nstates - 1 ];
1743 FMFont font = state->font;
1744 int blurradius = state->blurradius;
1745 int blurscale = state->blurscale;
1746 FMGlyph *glyph = getGlyph(font, unicode, state->size, 0, blurradius, blurscale );
1749 subpixel = font.ftFont.getGlyphKernAdvance( pathdraw.prevGlyphIndex, glyph->glyphindex );
1750 subpixel += glyph->advance;
1752 return (float)subpixel * (1.0f/64.0f);