1 /* *****************************************************************************
2 * Original Version Copyright (c) 2007-2014 Alexis Naveros.
4 * Ecere Corporation has unlimited/unrestricted rights.
5 * *****************************************************************************/
18 static inline uint32 decodeUTF8( uint32 b, uint32 *state, unichar *retCodePoint ) { return ccUtf8ToUnicode(b, state, (uint *)retCodePoint); }
22 // Based on Exponential blur, Jani Huhtanen, 2006
27 static void blurCols( byte *dst, int w, int h, int stride, int alpha )
30 for( y = 0 ; y < h ; y++ )
33 for( x = 1 ; x < w ; x++ )
35 z += ( alpha * ( ( (int)( dst[x] ) << ZPREC ) - z ) ) >> APREC;
36 dst[x] = (byte)( z >> ZPREC );
38 for( x = w-2 ; x >= 0 ; x-- )
40 z += ( alpha * ( ( (int)( dst[x] ) << ZPREC ) - z ) ) >> APREC;
41 dst[x] = (byte)( z >> ZPREC );
47 static void blurRows( byte *dst, int w, int h, int stride, int alpha )
50 for( x = 0 ; x < w ; x++ )
53 for( y = stride ; y < h*stride ; y += stride )
55 z += ( alpha * ( ( (int)( dst[y] ) << ZPREC ) - z ) ) >> APREC;
56 dst[y] = (byte)( z >> ZPREC );
58 for( y = (h-2)*stride ; y >= 0 ; y -= stride )
60 z += ( alpha * ( ( (int)( dst[y] ) << ZPREC ) - z ) ) >> APREC;
61 dst[y] = (byte)( z >> ZPREC );
67 static void blur( byte *dst, int width, int height, int stride, int radius )
72 // Calculate the alpha such that 90% of the kernel is within the radius. (Kernel extends to infinity)
73 float radiusrange = (float)radius * ( 1.0f / sqrtf( 3 ) );
74 uint32 alpha = (uint32)roundf( ( 1 << APREC ) * ( 1.0f - expf( -2.3f / ( radiusrange + 1.0f ) ) ) );
75 for( ; boxcount ; boxcount-- )
77 blurRows( dst, width, height, stride, alpha );
78 blurCols( dst, width, height, stride, alpha );
89 #define SPREC (31-(APREC+ZPREC+8))
91 static void blurCols32( int *dst, int w, int h, int stride, int alpha )
94 for( y = 0 ; y < h ; y++ )
97 for( x = 1 ; x < w ; x++ )
99 z += ( alpha * ( dst[x] - z ) ) >> APREC;
102 for( x = w - 2 ; x >= 0 ; x-- )
104 z += ( alpha * ( dst[x] - z ) ) >> APREC;
111 static void blurRows32( int *dst, int w, int h, int stride, int alpha )
114 for( x = 0 ; x < w ; x++ )
117 for( y = 1 * stride ; y < h * stride ; y += stride )
119 z += ( alpha * ( dst[y] - z ) ) >> APREC;
122 for( y = ( h - 2 ) * stride ; y >= 0 ; y -= stride )
124 z += ( alpha * ( dst[y] - z ) ) >> APREC;
131 static void blurScale( byte *dst, int width, int height, int stride, int radius, int scale )
138 int *buffer, *bufferrow;
141 if( scale > ( 1 << SPREC ) )
144 buffer = new int[ width * height ];
145 for( y = 0 ; y < height ; y++ )
147 dstrow = &dst[y*stride];
148 bufferrow = &buffer[y*width];
149 for( x = 0 ; x < width ; x++ )
150 bufferrow[x] = ( (int)dstrow[x] << ZPREC ) * scale;
155 /* Calculate the alpha such that 90% of the kernel is within the radius. (Kernel extends to infinity) */
156 radiusrange = (float)radius * 0.3f;
157 alpha = (uint32)roundf( ( 1 << APREC ) * ( 1.0f - expf( -2.3f / ( radiusrange + 1.0f ) ) ) );
158 for( ; boxcount ; boxcount-- )
160 blurRows32( buffer, width, height, width, alpha );
161 blurCols32( buffer, width, height, width, alpha );
164 for( y = 0 ; y < height ; y++ )
166 dstrow = &dst[y*stride];
167 bufferrow = &buffer[y*width];
168 for( x = 0 ; x < width ; x++ )
169 dstrow[x] = (byte)Min(255, bufferrow[x] >> ZPREC);
182 static void buildCursorGlyph( byte *dst, int glyphwidth, int glyphheight, int dststride )
184 int x, y, hbarheight, hgap, hline, vline;
187 if( glyphwidth >= 3 )
189 hbarheight = 1 + ( glyphheight >> 6 );
190 hgap = ( glyphwidth - ( 1 + ( glyphheight >> 6 ) ) ) >> 1;
191 hline = hgap + glyphwidth - ( hgap << 1 );
192 vline = glyphheight - ( hbarheight << 1 );
193 for( y = 0 ; y < hbarheight ; y++ )
196 for( x = 0 ; x < glyphwidth ; x++ )
200 for( y = 0 ; y < vline ; y++ )
203 for( x = 0 ; x < hgap ; x++ )
205 for( ; x < hline ; x++ )
207 for( ; x < glyphwidth ; x++ )
211 for( y = 0 ; y < hbarheight ; y++ )
214 for( x = 0 ; x < glyphwidth ; x++ )
221 for( y = 0 ; y < glyphheight ; y++ )
224 for( x = 0 ; x < glyphwidth ; x++ )
233 static inline void addGlyphAdvance( int *x, int *subpixel, FMGlyph *glyph )
235 *subpixel += glyph->advance;
236 #if FM_SUBPIXEL_ROUNDING_RANGE
237 *subpixel = ( *subpixel + (FM_SUBPIXEL_ROUNDING_RANGE>>1) ) & ~(FM_SUBPIXEL_ROUNDING_RANGE-1);
239 *x += *subpixel >> 6;
243 public class FontManagerRenderer
248 virtual bool init(int channelCount);
250 // Create, resize or update texture data ; rect is [minx,maxx,miny,maxy]
251 virtual int createTexture( int width, int height );
252 virtual int resizeTexture( int width, int height );
253 virtual void updateTexture( int *rect, const byte *data );
255 // If the renderer does any buffering of drawImage(), flush it all right now (texture may change immediately after)
256 virtual void flush( );
258 // If the renderer records per-image data, return an imageIndex passed to drawImage()
259 virtual int registerImage( int offsetx, int offsety, int width, int height );
260 // Draw an image, imageIndex passed as the value previously returned by registerImage()
261 virtual void drawImage( int targetx, int targety, int imageIndex );
262 // Draw an image, called instead of drawImage() for text cursors, can point to exactly the same function
263 virtual void drawImageCursor( int targetx, int targety, int imageIndex );
264 // If drawImage is zero, then this alternate function is called, passing everything required to render the glyph
265 virtual void drawImageAlt( byte *texdata, int targetx, int targety, int offsetx, int offsety, int width, int height );
266 // Draw a non-aligned image, imageIndex passed as the value previously returned by registerImage()
267 virtual void drawImageFloat( float targetx, float targety, float angsin, float angcos, int imageIndex );
269 // The renderer must flush all recorded images, registerImage() will be called for new images
270 virtual void resetImages( );
277 #include <ft2build.h>
279 #include FT_FREETYPE_H
280 #include FT_ADVANCES_H
286 #define FM_SUPPORT_GLYPH_ROTATION (1)
292 #define FM_ENABLE_HINTING (1)
293 #define FM_SUBPIXEL_ROUNDING_RANGE (16)
296 #define FM_HASH_TABLE_SIZE (4096)
297 #define FM_INIT_GLYPHS (1024)
298 #define FM_INIT_ATLAS_NODES (512)
300 #define FM_MAX_STATES (16)
305 struct FMFreeTypeFont
309 static inline int ::init()
312 ftError = FT_Init_FreeType( &ftLibrary2 );
316 static inline int loadFont( byte *data, int dataSize )
318 FT_Error ftError = FT_New_Memory_Face( ftLibrary2, (const FT_Byte*)data, dataSize, 0, &face );
324 FT_Done_Face( face );
327 static inline void getFontVMetrics( float *ascent, float *descent, float *lineHeight, float *limitminy, float *limitmaxy )
329 *ascent = (float)face->ascender;
330 *descent = (float)face->descender;
331 *lineHeight = (float)face->height / (float)face->units_per_EM;
332 *limitminy = (float)face->bbox.yMin / (float)face->units_per_EM;
333 *limitmaxy = (float)face->bbox.yMax / (float)face->units_per_EM;
336 static inline int getGlyphIndex( unichar codepoint )
338 return FT_Get_Char_Index( this.face, codepoint );
341 static int buildGlyphBitmap( int glyph, int size, int subpixel, int *advance, int *x0, int *y0, int *x1, int *y1 )
344 FT_GlyphSlot glyphslot;
347 ftError = FT_Set_Pixel_Sizes( this.face, 0, (FT_UInt)size );
351 subvector.x = subpixel;
353 FT_Set_Transform( face, 0, &subvector );
355 #if FM_ENABLE_HINTING
356 ftError = FT_Load_Glyph( face, glyph, FT_LOAD_RENDER );
358 ftError = FT_Load_Glyph( face, glyph, FT_LOAD_RENDER | FT_LOAD_NO_HINTING );
363 glyphslot = face->glyph;
364 *advance = (int)glyphslot->metrics.horiAdvance;
365 *x0 = glyphslot->bitmap_left;
366 *x1 = glyphslot->bitmap_left + glyphslot->bitmap.width;
367 *y0 = -glyphslot->bitmap_top;
368 *y1 = -glyphslot->bitmap_top + glyphslot->bitmap.rows;
373 static inline int getGlyphKernAdvance( int glyph1, int glyph2 )
376 FT_Get_Kerning( face, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning );
377 return (int)ftKerning.x;
380 static inline byte *getGlyphBitmap( int glyphindex )
382 FT_GlyphSlot glyphslot;
383 glyphslot = face->glyph;
384 return glyphslot->bitmap.buffer;
388 #define FM_DEF_CODEPOINT_BITS (32)
389 #define FM_DEF_SIZE_BITS (12)
390 #define FM_DEF_SUBPIXEL_BITS (6)
391 #define FM_DEF_BLURRADIUS_BITS (8)
392 #define FM_DEF_BLURSCALE_BITS (6)
394 #define FM_DEF_CODEPOINT_SHIFT (0)
395 #define FM_DEF_SIZE_SHIFT (FM_DEF_CODEPOINT_BITS)
396 #define FM_DEF_SUBPIXEL_SHIFT (FM_DEF_CODEPOINT_BITS+FM_DEF_SIZE_BITS)
397 #define FM_DEF_BLURRADIUS_SHIFT (FM_DEF_CODEPOINT_BITS+FM_DEF_SIZE_BITS+FM_DEF_SUBPIXEL_BITS)
398 #define FM_DEF_BLURSCALE_SHIFT (FM_DEF_CODEPOINT_BITS+FM_DEF_SIZE_BITS+FM_DEF_SUBPIXEL_BITS+FM_DEF_BLURRADIUS_BITS)
400 #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) )
402 #define FM_SIZE_MAX ((1<<FM_DEF_SIZE_BITS)-1)
403 #define FM_BLUR_RADIUS_MAX ((1<<FM_DEF_BLURRADIUS_BITS)-1)
404 #define FM_BLUR_SCALE_MAX ((1<<FM_DEF_BLURSCALE_BITS)-1)
406 #define FM_GLYPH_CODEPOINT_CURSOR (0x1)
407 #define FM_GLYPH_CODEPOINT_REPLACEMENT (0xfffd)
409 public enum FMVerticalAlignment { baseline, top, middle, bottom };
411 public class FMTextAlignment : uint16
414 Alignment horzAlignment:2;
415 FMVerticalAlignment vertAlignment:2;
418 public struct FMPathDraw
424 struct FMQuad { int x0, y0, x1, y1; };
430 short x0, y0, x1, y1;
431 short advance, offsetx, offsety;
435 static void getQuad( float x, float y, FMQuad q )
437 int rx = (int)(x + offsetx);
438 int ry = (int)(y + offsety);
439 q = { rx, ry, rx + x1 - x0, ry + y1 - y0 };
443 public class FMFont : struct
445 public LinkElement link;
446 FMFreeTypeFont ftFont;
452 float limitminy, limitmaxy;
456 int hashtable[FM_HASH_TABLE_SIZE];
457 int glyphPaddingWidth;
459 void (*processImage)( void *opaquecontext, byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingwidth );
460 void *processImageContext;
463 public void setFontImageProcessing(
464 void (*processImage)( byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingsize, void *opaquecontext ),
465 void *opaquecontext )
467 this.processImage = processImage;
468 this.processImageContext = opaquecontext;
473 float outlineAlphaFactor;
474 float outlineIntensityFactor;
476 static void ::outlineProcessGlyphImage( FMFont font, byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingwidth )
479 byte *src, *dst, *dstrow;
480 float intensityfactor, alphafactor, range, alpha, intensity, rangeinv, rangebase;
481 float *distancemap, *dmap;
483 distancemap = new float[width * height];
486 imgDistMapBuild( distancemap, src, width, height, bytesperpixel, bytesperline );
488 alphafactor = font.outlineAlphaFactor; //2.0f;
489 intensityfactor = font.outlineIntensityFactor; // 0.2f;
490 range = (float)font.outlineRadius;
491 rangeinv = 1.0f / range;
495 for( y = 0 ; y < height ; y++ )
498 for( x = 0 ; x < width ; x++ )
500 rangebase = ( range - dmap[ x ] ) * rangeinv;
501 alpha = alphafactor * rangebase;
502 intensity = fmaxf( (float)dstrow[0] * (1.0f/255.0f), intensityfactor * rangebase );
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 ) ) );
507 dstrow += bytesperpixel;
515 public void setOutline(float intensityFactor, float alphaFactor, float radius) // TODO: Figure out radius?
517 outlineIntensityFactor = intensityFactor;
518 outlineAlphaFactor = alphaFactor;
519 outlineRadius = radius;
520 processImage = outlineProcessGlyphImage;
521 processImageContext = this;
531 static FMGlyph *allocGlyph( )
533 if( glyphcount >= glyphalloc )
536 if( !(glyphs = renew glyphs FMGlyph[glyphalloc]) )
539 return &glyphs[ glyphcount++ ];
544 static float getVertAlign( FMTextAlignment align, int size )
547 if( align.vertAlignment == top )
548 return ascender * sizef;
549 else if( align.vertAlignment == middle )
550 return middleAlign * sizef;
551 else if( align.vertAlignment == bottom )
552 return descender * sizef;
556 static inline void addKerning( int prevGlyphIndex, FMGlyph *glyph, int *x, int *subpixel )
558 if( prevGlyphIndex != -1 )
560 *subpixel += ftFont.getGlyphKernAdvance( prevGlyphIndex, glyph->glyphindex );
561 #if FM_SUBPIXEL_ROUNDING_RANGE
562 *subpixel = ( *subpixel + (FM_SUBPIXEL_ROUNDING_RANGE>>1) ) & ~(FM_SUBPIXEL_ROUNDING_RANGE-1);
564 *x += *subpixel >> 6;
575 FMTextAlignment align;
583 static FT_Library ftLibrary2;
585 static void copyGlyphBitmap1( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride )
588 for( y = 0 ; y < glyphheight ; y++ )
590 byte *dstrow = &dst[ y * dststride ];
591 for( x = 0 ; x < glyphwidth ; x++ )
592 dstrow[ x ] = src[ x ];
597 static void copyGlyphBitmap2( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride )
600 for( y = 0 ; y < glyphheight ; y++ )
602 byte *dstrow = &dst[ y * dststride ];
603 for( x = 0 ; x < glyphwidth ; x++ )
604 dstrow[ x << 1 ] = src[ x ];
609 static void copyGlyphBitmap4( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride )
612 for( y = 0 ; y < glyphheight ; y++ )
614 byte *dstrow = &dst[ y * dststride ];
615 for( x = 0 ; x < glyphwidth ; x++ )
616 dstrow[ x << 2 ] = src[ x ];
621 public class FontManager
623 FontManagerRenderer renderer;
625 float widthinv, heightinv;
630 AtlasBuilder atlas { };
634 LinkList<FMFont, link = link> fontList { };
636 FMState states[FM_MAX_STATES];
639 void (*copyGlyphBitmap)( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride );
642 static FMGlyph *getGlyph( FMFont font, unichar codepoint, int size, int subpixel, int blurradius, int blurscale )
644 int i, glyphindex, advance, x0, y0, x1, y1, gx, gy;
645 int glyphwidth, glyphheight, glyphareawidth, glyphareaheight;
656 padding = blurradius + font.glyphPaddingWidth;
658 /* Find code point and size. */
659 glyphdef = FM_GLYPH_COMPUTE_DEF( codepoint, size, subpixel, blurradius, blurscale );
660 hashindex = ccHash32Int64Inline( glyphdef ) & ( FM_HASH_TABLE_SIZE - 1 );
661 i = font.hashtable[hashindex];
664 if( glyphdef == font.glyphs[i].glyphdef )
665 return &font.glyphs[i];
666 i = font.glyphs[i].listnext;
669 /* Could not find glyph, create it */
670 if( codepoint == FM_GLYPH_CODEPOINT_CURSOR )
681 y0 = -(int)ceilf( font.limitmaxy * (float)size );
682 y1 = -(int)floorf( font.limitminy * (float)size );
683 i = ( (y1 - y0) - size ) / 3;
689 glyphindex = font.ftFont.getGlyphIndex( codepoint );
690 if( !( font.ftFont.buildGlyphBitmap( glyphindex, size, subpixel, &advance, &x0, &y0, &x1, &y1 ) ) )
692 if( codepoint != FM_GLYPH_CODEPOINT_REPLACEMENT )
693 return getGlyph(font, FM_GLYPH_CODEPOINT_REPLACEMENT, size, subpixel, blurradius, blurscale );
697 glyphwidth = ( x1 - x0 );
698 glyphheight = ( y1 - y0 );
699 glyphareawidth = glyphwidth + (padding << 1);
700 glyphareaheight = glyphheight + (padding << 1);
702 // Find free spot for the rect in the atlas
703 added = atlas.addRect( glyphareawidth, glyphareaheight, &gx, &gy );
704 if( !( added ) && ( onAtlasFull ) )
706 /* Atlas is full, let the user to resize the atlas (or not), and try again. */
708 added = atlas.addRect( glyphareawidth, glyphareaheight, &gx, &gy );
713 /* Build the glyph */
714 glyph = font.allocGlyph();
715 glyph->glyphdef = glyphdef;
716 glyph->glyphindex = glyphindex;
717 glyph->x0 = (short)gx;
718 glyph->y0 = (short)gy;
719 glyph->x1 = (short)( glyph->x0 + glyphareawidth );
720 glyph->y1 = (short)( glyph->y0 + glyphareaheight );
721 glyph->advance = (short)advance;
722 glyph->offsetx = (short)( x0 - padding );
723 glyph->offsety = (short)( y0 - padding );
725 glyph->imageIndex = -1;
726 if( renderer.registerImage )
727 glyph->imageIndex = renderer.registerImage( gx, gy, glyphareawidth, glyphareaheight );
729 // Add char to hash table
730 glyph->listnext = font.hashtable[hashindex];
731 font.hashtable[hashindex] = font.glyphcount - 1;
733 // Clear glyph image area (TODO: wasteful when single channel without prepare callback?)
734 dst = &texdata[ ( glyph->x0 * bytesperpixel ) + ( glyph->y0 * bytesperline ) ];
735 for( i = 0 ; i < glyphareaheight ; i++, dst += bytesperline )
736 memset( dst, 0, glyphareawidth * bytesperpixel );
739 dst = &texdata[ ( glyph->x0 + padding ) * bytesperpixel + ( ( glyph->y0 + padding ) * bytesperline ) + channelindex];
740 if( codepoint == FM_GLYPH_CODEPOINT_CURSOR )
742 src = new byte[ glyphwidth * glyphheight ];
743 buildCursorGlyph( src, glyphwidth, glyphheight, glyphwidth );
744 copyGlyphBitmap( dst, src, glyphwidth, glyphheight, bytesperline );
749 src = font.ftFont.getGlyphBitmap(glyphindex);
750 copyGlyphBitmap( dst, src, glyphwidth, glyphheight, bytesperline );
757 printf( "Do NOT use the blur! This will be removed in favor of external image processing\n" );
759 bdst = &texdata[ glyph->x0 * bytesperpixel + ( glyph->y0 * bytesperline ) ];
761 blur(bdst, glyphareawidth, glyphareaheight, this.width, blurradius );
763 blurScale(bdst, glyphareawidth, glyphareaheight, this.width, blurradius, blurscale );
766 // User custom font image processing
767 if(font.processImage)
769 dst = &texdata[ ( glyph->x0 * bytesperpixel ) + ( glyph->y0 * bytesperline ) ];
770 font.processImage( font.processImageContext, dst, glyphareawidth, glyphareaheight, bytesperpixel, bytesperline, font.glyphPaddingWidth );
773 dirtyrect[0] = Min( dirtyrect[0], glyph->x0 );
774 dirtyrect[1] = Min( dirtyrect[1], glyph->y0 );
775 dirtyrect[2] = Max( dirtyrect[2], glyph->x1 );
776 dirtyrect[3] = Max( dirtyrect[3], glyph->y1 );
781 static inline void drawTextGlyph( FMFont font, FMGlyph *glyph, int x, int y )
783 int ptx = x + glyph->offsetx;
784 int pty = y + glyph->offsety;
785 if( renderer.drawImage )
786 renderer.drawImage( ptx, pty, glyph->imageIndex );
787 else if( renderer.drawImageAlt )
788 renderer.drawImageAlt( texdata, ptx, pty, glyph->x0, glyph->y0, glyph->x1 - glyph->x0, glyph->y1 - glyph->y0 );
791 static inline void drawTextCursorGlyph( FMFont font, FMGlyph *glyph, int x, int y )
794 ptx = x + glyph->offsetx;
795 pty = y + glyph->offsety;
796 if( renderer.drawImageCursor )
797 renderer.drawImageCursor( ptx, pty, glyph->imageIndex );
798 else if( renderer.drawImageAlt )
799 renderer.drawImageAlt( texdata, ptx, pty, glyph->x0, glyph->y0, glyph->x1 - glyph->x0, glyph->y1 - glyph->y0 );
802 static inline void drawTextGlyphFloat( FMFont font, FMGlyph *glyph, float x, float y, float vectorx, float vectory, float offsetx, float offsety )
804 float vectx = (float)glyph->offsetx + offsetx;
805 float vecty = (float)glyph->offsety + offsety;
806 float ptx = x + ( vectorx * vectx ) - ( vectory * vecty );
807 float pty = y + ( vectorx * vecty ) + ( vectory * vectx );
808 renderer.drawImageFloat( ptx, pty, vectory, vectorx, glyph->imageIndex );
813 property FontManagerRenderer renderer { set { renderer = value; } get { return renderer; } }
815 // When notified of a full atlas, you should call fmExpandAtlas() or fmResetAtlas() within the callback
816 virtual void (*onAtlasFull)();
818 // Create and destroy font manager
819 bool create( int width, int height, int channelCount, int channelIndex, FontManagerRenderer renderer)
823 if( ( channelCount != 1 ) && ( channelCount != 2 ) && ( channelCount != 4 ) )
825 if( ( width <= 0 ) || ( height <= 0 ) )
828 this.renderer = renderer;
830 renderer.init(channelCount);
832 // Initialize implementation library
833 if(FMFreeTypeFont::init() )
836 this.height = height;
837 bytesperpixel = channelCount;
838 bytesperline = width * bytesperpixel;
839 this.channelindex = channelIndex;
840 if(renderer.createTexture( width, height ))
842 if((atlas.create( this.width, this.height, FM_INIT_ATLAS_NODES )))
844 // Create texture for the cache.
845 widthinv = 1.0f / width;
846 heightinv = 1.0f / height;
847 texdata = new byte[width * height * bytesperpixel];
850 memset( texdata, 0, height * bytesperline );
852 dirtyrect[0] = this.width;
853 dirtyrect[1] = this.height;
857 if( bytesperpixel == 1 )
858 copyGlyphBitmap = copyGlyphBitmap1;
859 else if( bytesperpixel == 2 )
860 copyGlyphBitmap = copyGlyphBitmap2;
862 copyGlyphBitmap = copyGlyphBitmap4;
889 void setState( FMFont font, int size, int align, int blurradius, int blurscale )
892 if( size >= FM_SIZE_MAX )
894 if( blurradius >= FM_BLUR_RADIUS_MAX )
895 blurradius = FM_BLUR_RADIUS_MAX;
896 else if( blurradius < 0 )
898 if( blurscale >= FM_BLUR_SCALE_MAX )
899 blurscale = FM_BLUR_SCALE_MAX;
900 else if( blurscale < 1 )
902 state = &states[ nstates - 1 ];
904 state->size = (uint16)size;
905 state->align = (uint16)align;
906 state->blurradius = (uint16)blurradius;
907 state->blurscale = (uint16)blurscale;
910 void setFont( FMFont font )
912 states[ nstates - 1 ].font = font;
915 void setSize( int size )
917 if( size >= FM_SIZE_MAX )
920 states[ nstates - 1 ].size = (uint16)size;
923 void setAlign( int align )
925 states[ nstates - 1 ].align = (uint16)align;
928 void setBlur( int radius, int scale )
930 if( radius >= FM_BLUR_RADIUS_MAX )
931 radius = FM_BLUR_RADIUS_MAX;
932 else if( radius < 0 )
934 if( scale >= FM_BLUR_SCALE_MAX )
935 scale = FM_BLUR_SCALE_MAX;
938 states[ nstates - 1 ].blurradius = (uint16)radius;
939 states[ nstates - 1 ].blurscale = (uint16)scale;
942 // Set image manipuation callback
943 void setFontImageProcessing( FMFont font, void (*processImage)( byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingwidth, void *opaquecontext ), void *opaquecontext )
951 if(nstates < FM_MAX_STATES)
954 memcpy( &states[nstates], &states[nstates-1], sizeof(FMState) );
967 FMState *state = &states[ nstates - 1 ];
970 state->blurradius = 0;
971 state->blurscale = 1;
972 state->align = { left, baseline };
978 static void freeFont(FMFont font)
982 fontList.Remove((IteratorPointer)font);
987 // Add font from file
988 FMFont addFont(const String path, int glyphPaddingWidth )
991 File f = FileOpen(path, read);
994 // Read in the font data
995 int dataSize = f.GetSize();
996 byte *data = new byte[dataSize];
999 f.Read(data, 1, dataSize);
1000 font = addFontData(data, dataSize, glyphPaddingWidth);
1009 // Add font from data ; do not free( data ), the font manager will do that when removing the font
1010 FMFont addFontData( byte *data, int dataSize, int glyphPaddingWidth )
1014 glyphs = new FMGlyph[FM_INIT_GLYPHS];
1015 glyphalloc = FM_INIT_GLYPHS;
1016 glyphPaddingWidth = glyphPaddingWidth;
1021 float ascent, descent, fontHeight;
1026 for( i = 0 ; i < FM_HASH_TABLE_SIZE ; i++ )
1027 font.hashtable[i] = -1;
1030 if(!font.ftFont.loadFont(data, dataSize))
1036 // Store normalized line height. The real line height is got by multiplying by font size.
1037 font.ftFont.getFontVMetrics( &ascent, &descent, &font.lineHeight, &font.limitminy, &font.limitmaxy );
1038 fontHeight = ascent - descent;
1039 font.ascender = ascent / fontHeight;
1040 font.descender = descent / fontHeight;
1041 font.middleAlign = 0.5f * ( font.ascender + font.descender );
1042 font.fontdata = data;
1048 void removeFont( FMFont font )
1054 int drawText( int x, int y, const char *string, int stringlength )
1056 int subpixel, index;
1062 int blurradius, blurscale;
1065 state = &states[ nstates - 1 ];
1068 prevGlyphIndex = -1;
1069 blurradius = state->blurradius;
1070 blurscale = state->blurscale;
1071 if( !( state->font ) )
1074 if( !( stringlength ) )
1075 stringlength = strlen( string );
1077 // Align horizontally
1078 if( state->align.horzAlignment == right )
1079 x -= getTextWidth(string, stringlength );
1080 else if( state->align.horzAlignment == center )
1081 x -= getTextWidth(string, stringlength ) >> 1;
1084 y += roundf( font.getVertAlign(state->align, state->size ) );
1087 for( index = 0 ; index < stringlength ; index++ )
1089 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1091 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1094 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1095 drawTextGlyph(font, glyph, x, y );
1096 addGlyphAdvance( &x, &subpixel, glyph );
1098 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1101 return x + ( subpixel >= 32 );
1104 int drawTextWithCursor( int x, int y, const char *string, int stringlength, int cursoroffset )
1106 int subpixel, index;
1112 int blurradius, blurscale;
1115 state = &states[ nstates - 1 ];
1118 prevGlyphIndex = -1;
1119 blurradius = state->blurradius;
1120 blurscale = state->blurscale;
1121 if( !( state->font ) )
1124 if( !( stringlength ) )
1125 stringlength = strlen( string );
1127 // Align horizontally
1128 if( state->align.horzAlignment == right )
1129 x -= getTextWidth(string, stringlength );
1130 else if( state->align.horzAlignment == center )
1131 x -= getTextWidth(string, stringlength ) >> 1;
1134 y += roundf( font.getVertAlign(state->align, state->size ) );
1137 for( index = 0 ; ; index++ )
1139 if( index == cursoroffset )
1141 glyph = getGlyph(font, FM_GLYPH_CODEPOINT_CURSOR, state->size, subpixel, blurradius, blurscale );
1143 drawTextCursorGlyph(font, glyph, x, y );
1145 if( index >= stringlength )
1147 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1149 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1152 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1153 drawTextGlyph(font, glyph, x, y );
1154 addGlyphAdvance( &x, &subpixel, glyph );
1156 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1159 return x + ( subpixel >= 32 );
1162 int drawTextTruncate( int x, int y, int truncatewidth, const char *string, int stringlength, char *extstring, int extwidth )
1164 int subpixel, index, textwidth, truncatepoint;
1170 int blurradius, blurscale;
1173 state = &states[ nstates - 1 ];
1176 prevGlyphIndex = -1;
1177 blurradius = state->blurradius;
1178 blurscale = state->blurscale;
1179 if( !( state->font ) )
1182 if( !( stringlength ) )
1183 stringlength = strlen( string );
1184 textwidth = getTextWidthTruncate(string, stringlength, truncatewidth );
1186 truncatepoint = x + truncatewidth;
1187 if( textwidth <= truncatewidth )
1191 if( extwidth >= truncatewidth )
1193 truncatepoint -= extwidth;
1196 // Align horizontally
1197 if( state->align.horzAlignment == right )
1199 else if( state->align.horzAlignment == center )
1200 x -= textwidth >> 1;
1203 y += roundf( font.getVertAlign(state->align, state->size ) );
1206 for( index = 0 ; index < stringlength ; index++ )
1208 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1210 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1213 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1214 drawTextGlyph(font, glyph, x, y );
1215 addGlyphAdvance( &x, &subpixel, glyph );
1216 if( x > truncatepoint )
1219 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1221 x += ( subpixel >= 32 );
1223 drawText(x, y, extstring, 0 );
1233 int getTextWidth( const char *string, int stringlength )
1235 return getTextWidthTruncate(string, stringlength, ( (unsigned)1 << ( ( sizeof(int) * CHAR_BIT ) - 1 ) ) - 1 );
1239 int getTextWidthTruncate( const char *string, int stringlength, int truncatewidth )
1241 int subpixel, index, advance;
1247 int blurradius, blurscale;
1250 state = &states[ nstates - 1 ];
1253 prevGlyphIndex = -1;
1254 blurradius = state->blurradius;
1255 blurscale = state->blurscale;
1256 if( !( state->font ) )
1259 if( !( stringlength ) )
1260 stringlength = strlen( string );
1264 for( index = 0 ; index < stringlength ; index++ )
1266 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1268 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1271 font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1272 addGlyphAdvance( &advance, &subpixel, glyph );
1273 if( advance > truncatewidth )
1276 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1279 return advance + ( subpixel >= 32 );
1282 int getTextBounds( int x, int y, const char *string, int stringlength, int *bounds )
1284 int subpixel, index;
1291 int blurradius, blurscale;
1293 int startx, advance;
1294 int minx, miny, maxx, maxy;
1296 state = &states[ nstates - 1 ];
1299 prevGlyphIndex = -1;
1300 blurradius = state->blurradius;
1301 blurscale = state->blurscale;
1302 if( !( state->font ) )
1305 if( !( stringlength ) )
1306 stringlength = strlen( string );
1309 y += font.getVertAlign(state->align, state->size );
1316 for( index = 0 ; index < stringlength ; index++ )
1318 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1320 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1323 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1324 glyph->getQuad(x, y, q);
1333 addGlyphAdvance( &x, &subpixel, glyph );
1335 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1338 advance = x - startx;
1340 /* Align horizontally */
1341 if( state->align.horzAlignment == right )
1346 else if( state->align.horzAlignment == center )
1348 minx -= advance * 0.5f;
1349 maxx -= advance * 0.5f;
1360 return advance + ( subpixel >= 32 );
1363 // Find text offset up to truncatewidth
1364 int getTextTruncateOffset( int truncatewidth, const char *string, int stringlength, int extwidth, int *retextflag, int *retfullwidth )
1366 int subpixel, index, advance, truncatewidthext, truncateindex, extflag, fullwidth = 0;
1372 int blurradius, blurscale;
1375 state = &states[ nstates - 1 ];
1380 if( extwidth >= truncatewidth )
1384 prevGlyphIndex = -1;
1385 blurradius = state->blurradius;
1386 blurscale = state->blurscale;
1387 if( !( state->font ) )
1390 if( stringlength <= 0 )
1391 stringlength = strlen( string );
1393 truncatewidthext = truncatewidth - extwidth;
1399 for( index = 0 ; ; index++ )
1401 if( index >= stringlength )
1403 truncateindex = index;
1404 fullwidth = advance + ( subpixel >= 32 );
1407 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1409 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1412 font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1413 addGlyphAdvance( &advance, &subpixel, glyph );
1414 if( advance > truncatewidth )
1419 if( advance <= truncatewidthext )
1421 truncateindex = index + 1;
1422 fullwidth = advance + ( subpixel >= 32 );
1425 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1431 *retfullwidth = fullwidth;
1433 *retfullwidth = fullwidth;
1436 *retextflag = extflag;
1438 return truncateindex;
1441 // Find text offset nearest to the given width
1442 int getTextNearestOffset( int targetwidth, const char *string, int stringlength )
1444 int subpixel, index, advance, truncateindex, distance, bestdistance;
1450 int blurradius, blurscale;
1453 state = &states[ nstates - 1 ];
1456 prevGlyphIndex = -1;
1457 blurradius = state->blurradius;
1458 blurscale = state->blurscale;
1459 if( !( state->font ) )
1462 if( stringlength <= 0 )
1463 stringlength = strlen( string );
1468 bestdistance = abs( targetwidth );
1469 for( index = 0 ; index < stringlength ; index++ )
1471 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1473 glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale );
1476 font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1477 addGlyphAdvance( &advance, &subpixel, glyph );
1478 distance = abs( targetwidth - ( advance + ( subpixel >= 32 ) ) );
1479 if( distance > bestdistance )
1481 bestdistance = distance;
1482 truncateindex = index + 1;
1484 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1487 return truncateindex;
1492 static void flush( bool rendererFlush )
1494 // Flush texture updates
1495 if( ( dirtyrect[0] < dirtyrect[2] ) && ( dirtyrect[1] < dirtyrect[3] ) )
1497 renderer.updateTexture( dirtyrect, texdata );
1499 dirtyrect[0] = width;
1500 dirtyrect[1] = height;
1504 // Flush buffered glyphs
1505 if( rendererFlush ) renderer.flush( );
1508 // Flush buffered texture updates, renderer->updateTexture()
1518 void getFontExtent( int *retascent, int *retdescent )
1521 FMState *state = &states[ nstates - 1 ];
1527 *retascent = -(int)ceilf( font.ascender * (float)state->size );
1529 *retdescent = -(int)floorf( font.descender * (float)state->size );
1533 void getFontLimits( int *retlimitminy, int *retlimitmaxy )
1538 state = &states[ nstates - 1 ];
1539 if( !( state->font ) )
1544 *retlimitminy = -(int)ceilf( font.limitmaxy * state->size );
1546 *retlimitmaxy = -(int)floorf( font.limitminy * state->size );
1551 int getFontLineHeight( )
1556 state = &states[ nstates - 1 ];
1557 if( !( state->font ) )
1560 return (int)ceilf( font.lineHeight * state->size );
1566 // Pull texture changes
1567 const byte *getTextureData( int *width, int *height )
1570 *width = this.width;
1572 *height = this.height;
1576 // Retrieve the dirty rectangle, telling fontmanager you will manually perform the update, returns 0 if no part of the texture requires an update
1577 bool validateTexture( int *retdirtyrect )
1579 if( ( dirtyrect[0] < dirtyrect[2] ) && ( dirtyrect[1] < dirtyrect[3] ) )
1581 retdirtyrect[0] = dirtyrect[0];
1582 retdirtyrect[1] = dirtyrect[1];
1583 retdirtyrect[2] = dirtyrect[2];
1584 retdirtyrect[3] = dirtyrect[3];
1586 dirtyrect[0] = width;
1587 dirtyrect[1] = height;
1595 // Returns current atlas size
1596 void getAtlasSize( int *retwidth, int *retheight )
1598 *retwidth = this.width;
1599 *retheight = this.height;
1603 // Expands the atlas size
1604 bool expandAtlas( int width, int height )
1606 width = Max( width, this.width );
1607 height = Max( height, this.height );
1609 if( ( width == this.width ) && ( height == this.height ) )
1612 // Flush all pending glyphs
1615 // Create new texture
1616 if( renderer.resizeTexture( width, height ) )
1621 // Copy old texture data over.
1622 if( !( data = new byte[width * bytesperline] ) )
1624 for( i = 0 ; i < this.height ; i++ )
1626 byte * dst = &data[ (i * width) * bytesperpixel ];
1627 byte * src = &this.texdata[ i * this.bytesperline ];
1628 memcpy( dst, src, bytesperline);
1629 if( width > this.width )
1630 memset( dst+bytesperline, 0, (width - this.width) * bytesperpixel );
1632 if( height > this.height )
1633 memset( &data[ width * this.height * bytesperpixel], 0, ( height - this.height ) *bytesperline );
1635 delete this.texdata;
1638 // Increase atlas size
1639 atlas.expand( width, height );
1641 // Add existing data as dirty.
1644 dirtyrect[2] = this.width;
1645 dirtyrect[3] = atlas.getAtlasMaxHeight();
1648 this.height = height;
1649 this.bytesperline = this.width * bytesperpixel;
1650 widthinv = 1.0f / this.width;
1651 heightinv = 1.0f / this.height;
1658 // Reset the whole fm
1659 bool resetAtlas( int width, int height )
1661 // Flush all pending glyphs
1664 // Create new texture
1665 if(renderer.resizeTexture( width, height ) )
1668 atlas.reset( width, height );
1670 // Clear texture data.
1671 texdata = renew texdata byte[width * height * bytesperpixel];
1672 if(!texdata) return 0;
1673 memset( this.texdata, 0, width * height );
1676 dirtyrect[0] = width;
1677 dirtyrect[1] = height;
1681 // Reset cached glyphs
1682 for(font : fontList)
1685 font.glyphcount = 0;
1686 for( i = 0 ; i < FM_HASH_TABLE_SIZE ; i++ )
1687 font.hashtable[i] = -1;
1689 renderer.resetImages( );
1692 this.height = height;
1693 this.bytesperline = width * bytesperpixel;
1694 this.widthinv = 1.0f / this.width;
1695 this.heightinv = 1.0f / this.height;
1703 bool initPathDraw( FMPathDraw pathdraw )
1705 FMState *state = &states[ nstates - 1 ];
1706 FMFont font = state->font;
1709 pathdraw.prevGlyphIndex = -1;
1710 pathdraw.middleAlign = font.middleAlign * (float)state->size;
1716 float pathDrawCharacter( FMPathDraw pathdraw, float x, float y, float vectorx, float vectory, int unicode )
1719 FMState *state = &states[ nstates - 1 ];
1720 int blurradius = state->blurradius;
1721 int blurscale = state->blurscale;
1722 FMFont font = state->font;
1723 FMGlyph *glyph = getGlyph(font, unicode, state->size, 0, blurradius, blurscale );
1726 subpixel = font.ftFont.getGlyphKernAdvance( pathdraw.prevGlyphIndex, glyph->glyphindex );
1727 drawTextGlyphFloat(font, glyph, x, y, vectorx, vectory, (float)subpixel * (1.0f/64.0f), pathdraw.middleAlign );
1728 subpixel += glyph->advance;
1729 pathdraw.prevGlyphIndex = glyph->glyphindex;
1734 pathdraw.prevGlyphIndex = -1;
1737 return (float)subpixel * (1.0f/64.0f);
1740 float pathDrawPredictAdvance( FMPathDraw pathdraw, unichar unicode )
1743 FMState *state = &states[ nstates - 1 ];
1744 FMFont font = state->font;
1745 int blurradius = state->blurradius;
1746 int blurscale = state->blurscale;
1747 FMGlyph *glyph = getGlyph(font, unicode, state->size, 0, blurradius, blurscale );
1750 subpixel = font.ftFont.getGlyphKernAdvance( pathdraw.prevGlyphIndex, glyph->glyphindex );
1751 subpixel += glyph->advance;
1753 return (float)subpixel * (1.0f/64.0f);