1 /* *****************************************************************************
2 * Original Version Copyright (c) 2007-2014 Alexis Naveros.
4 * Ecere Corporation has unlimited/unrestricted rights.
5 * *****************************************************************************/
26 static inline uint32 decodeUTF8( uint32 b, uint32 *state, unichar *retCodePoint ) { return ccUtf8ToUnicode(b, state, (uint *)retCodePoint); }
30 static void buildCursorGlyph( byte *dst, int glyphwidth, int glyphheight, int dststride )
32 int x, y, hbarheight, hgap, hline, vline;
37 hbarheight = 1 + ( glyphheight >> 6 );
38 hgap = ( glyphwidth - ( 1 + ( glyphheight >> 6 ) ) ) >> 1;
39 hline = hgap + glyphwidth - ( hgap << 1 );
40 vline = glyphheight - ( hbarheight << 1 );
41 for( y = 0 ; y < hbarheight ; y++ )
44 for( x = 0 ; x < glyphwidth ; x++ )
48 for( y = 0 ; y < vline ; y++ )
51 for( x = 0 ; x < hgap ; x++ )
53 for( ; x < hline ; x++ )
55 for( ; x < glyphwidth ; x++ )
59 for( y = 0 ; y < hbarheight ; y++ )
62 for( x = 0 ; x < glyphwidth ; x++ )
69 for( y = 0 ; y < glyphheight ; y++ )
72 for( x = 0 ; x < glyphwidth ; x++ )
81 static inline void addGlyphAdvance( int *x, int *subpixel, FMGlyph *glyph )
83 *subpixel += glyph->advance;
84 #if FM_SUBPIXEL_ROUNDING_RANGE
85 *subpixel = ( *subpixel + (FM_SUBPIXEL_ROUNDING_RANGE>>1) ) & ~(FM_SUBPIXEL_ROUNDING_RANGE-1);
91 public class FontManagerRenderer
96 virtual bool init(int channelCount);
98 // Create, resize or update texture data ; rect is [minx,maxx,miny,maxy]
99 virtual int createTexture( int width, int height );
100 virtual int resizeTexture( int width, int height );
101 virtual void updateTexture( int *rect, const byte *data );
103 // If the renderer does any buffering of drawImage(), flush it all right now (texture may change immediately after)
104 virtual void flush( );
106 // If the renderer records per-image data, return an imageIndex passed to drawImage()
107 virtual int registerImage( int offsetx, int offsety, int width, int height );
108 // Draw an image, imageIndex passed as the value previously returned by registerImage()
109 virtual void drawImage( int targetx, int targety, int imageIndex, bool useExtColor );
110 // Draw an image, called instead of drawImage() for text cursors, can point to exactly the same function
111 virtual void drawImageCursor( int targetx, int targety, int imageIndex );
112 // If drawImage is zero, then this alternate function is called, passing everything required to render the glyph
113 virtual void drawImageAlt( byte *texdata, int targetx, int targety, int offsetx, int offsety, int width, int height );
114 // Draw a non-aligned image, imageIndex passed as the value previously returned by registerImage()
115 virtual void drawImageFloat( float targetx, float targety, float angsin, float angcos, int imageIndex, bool useExtColor );
117 // The renderer must flush all recorded images, registerImage() will be called for new images
118 virtual void resetImages( );
120 virtual void setLayer( uint32 layerIndex );
127 #include <ft2build.h>
129 #include FT_FREETYPE_H
130 #include FT_ADVANCES_H
136 #define FM_SUPPORT_GLYPH_ROTATION (1)
142 #define FM_ENABLE_HINTING (1)
143 #define FM_SUBPIXEL_ROUNDING_RANGE (16)
146 #define FM_HASH_TABLE_SIZE (4096)
147 #define FM_INIT_GLYPHS (1024)
148 #define FM_INIT_ATLAS_NODES (512)
150 #define FM_MAX_STATES (16)
155 struct FMFreeTypeFont
159 static inline int ::init()
162 ftError = FT_Init_FreeType( &ftLibrary2 );
166 static inline int loadFont( byte *data, int dataSize )
168 FT_Error ftError = FT_New_Memory_Face( ftLibrary2, (const FT_Byte*)data, dataSize, 0, &face );
174 FT_Done_Face( face );
177 static inline void getFontVMetrics( float *ascent, float *descent, float *lineHeight, float *limitminy, float *limitmaxy )
179 *ascent = (float)face->ascender;
180 *descent = (float)face->descender;
181 *lineHeight = (float)face->height / (float)face->units_per_EM;
182 *limitminy = (float)face->bbox.yMin / (float)face->units_per_EM;
183 *limitmaxy = (float)face->bbox.yMax / (float)face->units_per_EM;
186 static inline int getGlyphIndex( unichar codepoint )
188 return FT_Get_Char_Index( this.face, codepoint );
191 static int buildGlyphBitmap( int glyph, int size, int subpixel, int *advance, int *x0, int *y0, int *x1, int *y1 )
194 FT_GlyphSlot glyphslot;
197 ftError = FT_Set_Pixel_Sizes( this.face, 0, (FT_UInt)size );
201 subvector.x = subpixel;
203 FT_Set_Transform( face, 0, &subvector );
205 #if FM_ENABLE_HINTING
206 ftError = FT_Load_Glyph( face, glyph, FT_LOAD_RENDER );
208 ftError = FT_Load_Glyph( face, glyph, FT_LOAD_RENDER | FT_LOAD_NO_HINTING );
213 glyphslot = face->glyph;
214 *advance = (int)glyphslot->metrics.horiAdvance;
215 *x0 = glyphslot->bitmap_left;
216 *x1 = glyphslot->bitmap_left + glyphslot->bitmap.width;
217 *y0 = -glyphslot->bitmap_top;
218 *y1 = -glyphslot->bitmap_top + glyphslot->bitmap.rows;
223 static inline int getGlyphKernAdvance( int glyph1, int glyph2 )
226 FT_Get_Kerning( face, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning );
227 return (int)ftKerning.x;
230 static inline byte *getGlyphBitmap( int glyphindex )
232 FT_GlyphSlot glyphslot;
233 glyphslot = face->glyph;
234 return glyphslot->bitmap.buffer;
238 class FMDefBits : uint64
242 int size:11, subPixel:6;
246 static define FM_SIZE_MAX = (1<<11)-1;
247 static define FM_BLUR_RADIUS_MAX = (1<<6)-1;
248 static define FM_BLUR_SCALE_MAX = (1<<6)-1;
250 static define FM_GLYPH_CODEPOINT_CURSOR = 0x1;
251 static define FM_GLYPH_CODEPOINT_REPLACEMENT = 0xfffd;
253 public enum FMVerticalAlignment { baseline, top, middle, bottom };
255 public class FMTextAlignment : uint16
258 Alignment horzAlignment:2;
259 FMVerticalAlignment vertAlignment:2;
262 public struct FMPathDraw
268 struct FMQuad { int x0, y0, x1, y1; };
274 short x0, y0, x1, y1;
275 short advance, offsetx, offsety;
279 static void getQuad( float x, float y, FMQuad q )
281 int rx = (int)(x + offsetx);
282 int ry = (int)(y + offsety);
283 q = { rx, ry, rx + x1 - x0, ry + y1 - y0 };
287 public class FMFont : struct
289 public LinkElement link;
290 FMFreeTypeFont ftFont;
296 float limitminy, limitmaxy;
300 int hashtable[FM_HASH_TABLE_SIZE];
301 int glyphPaddingWidth;
303 void (*processImage)( void *opaquecontext, byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingwidth, int pass);
304 void *processImageContext;
307 public void setFontImageProcessing(
308 void (*processImage)( byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingsize, void *opaquecontext ),
309 void *opaquecontext )
311 this.processImage = processImage;
312 this.processImageContext = opaquecontext;
317 float outlineAlphaFactor;
318 float outlineIntensityFactor;
320 static void ::outlineProcessGlyphImage( FMFont font, byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingwidth, int isOutline )
323 byte *src, *dst, *dstrow;
324 float intensityfactor, alphafactor, range, alpha, intensity, rangeinv, rangebase;
325 float *distancemap, *dmap;
327 distancemap = new float[width * height];
330 imgDistMapBuild( distancemap, src, width, height, bytesperpixel, bytesperline );
332 alphafactor = font.outlineAlphaFactor; //2.0f;
333 intensityfactor = font.outlineIntensityFactor; // 0.2f;
334 range = (float)font.outlineRadius;
335 rangeinv = 1.0f / range;
339 for( y = 0 ; y < height ; y++ )
342 for( x = 0 ; x < width ; x++ )
344 rangebase = ( range - dmap[ x ] ) * rangeinv;
345 alpha = alphafactor * rangebase;
346 intensity = fmaxf( (float)dstrow[0] * (1.0f/255.0f), intensityfactor * rangebase );
347 if(bytesperpixel == 2)
350 dstrow[0] = (byte)roundf( fmaxf( 0.0f, fminf( 255.0f, alpha * 255.0f ) ) );
351 /* Intensity channel */
352 dstrow[1] = (byte)roundf( fmaxf( 0.0f, fminf( 255.0f, intensity * 255.0f ) ) );
357 dstrow[0] = (byte)roundf( fmaxf( 0.0f, fminf( 255.0f, alpha * 255.0f ) ) );
359 dstrow[0] = (byte)roundf( fmaxf( 0.0f, fminf( 255.0f, intensity * 255.0f ) ) );
361 dstrow += bytesperpixel;
369 public void setOutline(float size, float fade)
371 outlineIntensityFactor = 1.0f / (0.2f + size);
372 outlineAlphaFactor = 1.0f / (0.2f + fade);
373 outlineRadius = size;
374 processImage = outlineProcessGlyphImage;
375 processImageContext = this;
385 static FMGlyph *allocGlyph( )
387 if( glyphcount >= glyphalloc )
390 if( !(glyphs = renew glyphs FMGlyph[glyphalloc]) )
393 return &glyphs[ glyphcount++ ];
398 static float getVertAlign( FMTextAlignment align, int size )
401 if( align.vertAlignment == top )
402 return ascender * sizef;
403 else if( align.vertAlignment == middle )
404 return middleAlign * sizef;
405 else if( align.vertAlignment == bottom )
406 return descender * sizef;
410 static inline void addKerning( int prevGlyphIndex, FMGlyph *glyph, int *x, int *subpixel )
412 if( prevGlyphIndex != -1 )
414 *subpixel += ftFont.getGlyphKernAdvance( prevGlyphIndex, glyph->glyphindex );
415 #if FM_SUBPIXEL_ROUNDING_RANGE
416 *subpixel = ( *subpixel + (FM_SUBPIXEL_ROUNDING_RANGE>>1) ) & ~(FM_SUBPIXEL_ROUNDING_RANGE-1);
418 *x += *subpixel >> 6;
429 FMTextAlignment align;
435 static FT_Library ftLibrary2;
437 static void copyGlyphBitmap1( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride )
440 for( y = 0 ; y < glyphheight ; y++ )
442 byte *dstrow = &dst[ y * dststride ];
443 for( x = 0 ; x < glyphwidth ; x++ )
444 dstrow[ x ] = src[ x ];
449 static void copyGlyphBitmap2( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride )
452 for( y = 0 ; y < glyphheight ; y++ )
454 byte *dstrow = &dst[ y * dststride ];
455 for( x = 0 ; x < glyphwidth ; x++ )
456 dstrow[ x << 1 ] = src[ x ];
461 static void copyGlyphBitmap4( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride )
464 for( y = 0 ; y < glyphheight ; y++ )
466 byte *dstrow = &dst[ y * dststride ];
467 for( x = 0 ; x < glyphwidth ; x++ )
468 dstrow[ x << 2 ] = src[ x ];
473 public class FontManager
475 FontManagerRenderer renderer;
477 float widthinv, heightinv;
482 AtlasBuilder atlas { };
486 LinkList<FMFont, link = link> fontList { };
488 FMState states[FM_MAX_STATES];
491 void (*copyGlyphBitmap)( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride );
494 static FMGlyph *getGlyph( FMFont font, unichar codepoint, int size, int subpixel, bool outlinePass )
496 int i, glyphindex, advance, x0, y0, x1, y1, gx, gy;
497 int glyphwidth, glyphheight, glyphareawidth, glyphareaheight;
508 padding = font.glyphPaddingWidth;
510 // Find code point and size.
511 glyphdef = FMDefBits { codepoint, size, subpixel, outlinePass };
512 hashindex = ccHash32Int64Inline( glyphdef ) & ( FM_HASH_TABLE_SIZE - 1 );
513 i = font.hashtable[hashindex];
516 if( glyphdef == font.glyphs[i].glyphdef )
517 return &font.glyphs[i];
518 i = font.glyphs[i].listnext;
521 /* Could not find glyph, create it */
522 if( codepoint == FM_GLYPH_CODEPOINT_CURSOR )
533 y0 = -(int)ceilf( font.limitmaxy * (float)size );
534 y1 = -(int)floorf( font.limitminy * (float)size );
535 i = ( (y1 - y0) - size ) / 3;
541 glyphindex = font.ftFont.getGlyphIndex( codepoint );
542 if( !( font.ftFont.buildGlyphBitmap( glyphindex, size, subpixel, &advance, &x0, &y0, &x1, &y1 ) ) )
544 if( codepoint != FM_GLYPH_CODEPOINT_REPLACEMENT )
545 return getGlyph(font, FM_GLYPH_CODEPOINT_REPLACEMENT, size, subpixel, outlinePass );
549 glyphwidth = ( x1 - x0 );
550 glyphheight = ( y1 - y0 );
551 glyphareawidth = glyphwidth + (padding << 1);
552 glyphareaheight = glyphheight + (padding << 1);
554 // Find free spot for the rect in the atlas
555 added = atlas.addRect( glyphareawidth, glyphareaheight, &gx, &gy );
556 if( !( added ) && ( onAtlasFull ) )
558 /* Atlas is full, let the user to resize the atlas (or not), and try again. */
560 added = atlas.addRect( glyphareawidth, glyphareaheight, &gx, &gy );
565 /* Build the glyph */
566 glyph = font.allocGlyph();
567 glyph->glyphdef = glyphdef;
568 glyph->glyphindex = glyphindex;
569 glyph->x0 = (short)gx;
570 glyph->y0 = (short)gy;
571 glyph->x1 = (short)( glyph->x0 + glyphareawidth );
572 glyph->y1 = (short)( glyph->y0 + glyphareaheight );
573 glyph->advance = (short)advance;
574 glyph->offsetx = (short)( x0 - padding );
575 glyph->offsety = (short)( y0 - padding );
577 glyph->imageIndex = -1;
578 if( renderer.registerImage )
580 renderer.setLayer(outlinePass ? 3 : 6);
581 glyph->imageIndex = renderer.registerImage( gx, gy, glyphareawidth, glyphareaheight);
584 // Add char to hash table
585 glyph->listnext = font.hashtable[hashindex];
586 font.hashtable[hashindex] = font.glyphcount - 1;
588 // Clear glyph image area (TODO: wasteful when single channel without prepare callback?)
589 dst = &texdata[ ( glyph->x0 * bytesperpixel ) + ( glyph->y0 * bytesperline ) ];
590 for( i = 0 ; i < glyphareaheight ; i++, dst += bytesperline )
591 memset( dst, 0, glyphareawidth * bytesperpixel );
594 dst = &texdata[ ( glyph->x0 + padding ) * bytesperpixel + ( ( glyph->y0 + padding ) * bytesperline ) + channelindex];
595 if( codepoint == FM_GLYPH_CODEPOINT_CURSOR )
597 src = new byte[ glyphwidth * glyphheight ];
598 buildCursorGlyph( src, glyphwidth, glyphheight, glyphwidth );
599 copyGlyphBitmap( dst, src, glyphwidth, glyphheight, bytesperline );
604 src = font.ftFont.getGlyphBitmap(glyphindex);
605 copyGlyphBitmap( dst, src, glyphwidth, glyphheight, bytesperline );
608 // User custom font image processing
609 if(font.processImage)
611 dst = &texdata[ ( glyph->x0 * bytesperpixel ) + ( glyph->y0 * bytesperline ) ];
612 font.processImage( font.processImageContext, dst, glyphareawidth, glyphareaheight, bytesperpixel, bytesperline, font.glyphPaddingWidth, outlinePass );
615 dirtyrect[0] = Min( dirtyrect[0], glyph->x0 );
616 dirtyrect[1] = Min( dirtyrect[1], glyph->y0 );
617 dirtyrect[2] = Max( dirtyrect[2], glyph->x1 );
618 dirtyrect[3] = Max( dirtyrect[3], glyph->y1 );
623 static inline void drawTextGlyph( FMFont font, FMGlyph *glyph, int x, int y, bool useExtColor )
625 int ptx = x + glyph->offsetx;
626 int pty = y + glyph->offsety;
627 if( renderer.drawImage )
628 renderer.drawImage( ptx, pty, glyph->imageIndex, useExtColor );
629 else if( renderer.drawImageAlt )
630 renderer.drawImageAlt( texdata, ptx, pty, glyph->x0, glyph->y0, glyph->x1 - glyph->x0, glyph->y1 - glyph->y0 );
633 static inline void drawTextCursorGlyph( FMFont font, FMGlyph *glyph, int x, int y )
636 ptx = x + glyph->offsetx;
637 pty = y + glyph->offsety;
638 if( renderer.drawImageCursor )
639 renderer.drawImageCursor( ptx, pty, glyph->imageIndex );
640 else if( renderer.drawImageAlt )
641 renderer.drawImageAlt( texdata, ptx, pty, glyph->x0, glyph->y0, glyph->x1 - glyph->x0, glyph->y1 - glyph->y0 );
644 static inline void drawTextGlyphFloat( FMFont font, FMGlyph *glyph, float x, float y, float vectorx, float vectory, float offsetx, float offsety, bool useExtColor )
646 float vectx = (float)glyph->offsetx + offsetx;
647 float vecty = (float)glyph->offsety + offsety;
648 float ptx = x + ( vectorx * vectx ) - ( vectory * vecty );
649 float pty = y + ( vectorx * vecty ) + ( vectory * vectx );
650 renderer.drawImageFloat( ptx, pty, vectory, vectorx, glyph->imageIndex, useExtColor );
655 property FontManagerRenderer renderer { set { renderer = value; } get { return renderer; } }
657 // When notified of a full atlas, you should call fmExpandAtlas() or fmResetAtlas() within the callback
658 virtual void onAtlasFull()
661 atlas.reset(atlas.width, atlas.height);
664 // Create and destroy font manager
665 bool create( int width, int height, int channelCount, int channelIndex, FontManagerRenderer renderer)
669 if( ( channelCount != 1 ) && ( channelCount != 2 ) && ( channelCount != 4 ) )
671 if( ( width <= 0 ) || ( height <= 0 ) )
674 this.renderer = renderer;
676 renderer.init(channelCount);
678 // Initialize implementation library
679 if(FMFreeTypeFont::init() )
682 this.height = height;
683 bytesperpixel = channelCount;
684 bytesperline = width * bytesperpixel;
685 this.channelindex = channelIndex;
686 if(renderer.createTexture( width, height ))
688 if((atlas.create( this.width, this.height, FM_INIT_ATLAS_NODES )))
690 // Create texture for the cache.
691 widthinv = 1.0f / width;
692 heightinv = 1.0f / height;
693 texdata = new byte[width * height * bytesperpixel];
696 memset( texdata, 0, height * bytesperline );
698 dirtyrect[0] = this.width;
699 dirtyrect[1] = this.height;
703 if( bytesperpixel == 1 )
704 copyGlyphBitmap = copyGlyphBitmap1;
705 else if( bytesperpixel == 2 )
706 copyGlyphBitmap = copyGlyphBitmap2;
708 copyGlyphBitmap = copyGlyphBitmap4;
735 void setState( FMFont font, int size, int align)
738 if( size >= FM_SIZE_MAX )
740 state = &states[ nstates - 1 ];
742 state->size = (uint16)size;
743 state->align = (uint16)align;
746 void setFont( FMFont font )
748 states[ nstates - 1 ].font = font;
751 void setSize( int size )
753 if( size >= FM_SIZE_MAX )
756 states[ nstates - 1 ].size = (uint16)size;
759 void setAlign( int align )
761 states[ nstates - 1 ].align = (uint16)align;
764 // Set image manipuation callback
765 void setFontImageProcessing( FMFont font, void (*processImage)( byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingwidth, void *opaquecontext ), void *opaquecontext )
773 if(nstates < FM_MAX_STATES)
776 memcpy( &states[nstates], &states[nstates-1], sizeof(FMState) );
789 FMState *state = &states[ nstates - 1 ];
792 state->align = { left, baseline };
798 static void freeFont(FMFont font)
802 fontList.Remove((IteratorPointer)font);
807 // Add font from FontResource
808 FMFont getFont(FontResource fontResource)
812 Array<FaceInfo> infos = ResolveFont(fontResource.faceName, fontResource.size, fontResource.flags);
817 font = addFont(i.fileName, Max(2, (int)(1+fontResource.outlineSize)));
820 font.setOutline(fontResource.outlineSize, fontResource.outlineFade);
830 // Add font from file
831 FMFont addFont(const String path, int glyphPaddingWidth )
834 File f = FileOpen(path, read);
837 // Read in the font data
838 int dataSize = f.GetSize();
839 byte *data = new byte[dataSize];
842 f.Read(data, 1, dataSize);
843 font = addFontData(data, dataSize, glyphPaddingWidth);
852 // Add font from data ; do not free( data ), the font manager will do that when removing the font
853 FMFont addFontData( byte *data, int dataSize, int glyphPaddingWidth )
857 glyphs = new FMGlyph[FM_INIT_GLYPHS];
858 glyphalloc = FM_INIT_GLYPHS;
859 glyphPaddingWidth = glyphPaddingWidth;
864 float ascent, descent, fontHeight;
869 for( i = 0 ; i < FM_HASH_TABLE_SIZE ; i++ )
870 font.hashtable[i] = -1;
873 if(!font.ftFont.loadFont(data, dataSize))
879 // Store normalized line height. The real line height is got by multiplying by font size.
880 font.ftFont.getFontVMetrics( &ascent, &descent, &font.lineHeight, &font.limitminy, &font.limitmaxy );
881 fontHeight = ascent - descent;
882 font.ascender = ascent / fontHeight;
883 font.descender = descent / fontHeight;
884 font.middleAlign = 0.5f * ( font.ascender + font.descender );
885 font.fontdata = data;
891 void removeFont( FMFont font )
897 int drawText( int x, int y, const char *string, int stringlength )
907 state = &states[ nstates - 1 ];
911 if( !( state->font ) )
914 if( !( stringlength ) )
915 stringlength = strlen( string );
917 // Align horizontally
918 if( state->align.horzAlignment == right )
919 x -= getTextWidth(string, stringlength );
920 else if( state->align.horzAlignment == center )
921 x -= getTextWidth(string, stringlength ) >> 1;
924 y += roundf( font.getVertAlign(state->align, state->size ) );
927 for( index = 0 ; index < stringlength ; index++ )
929 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
931 glyph = getGlyph(font, codepoint, state->size, subpixel, false );
934 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
935 if(font.processImage)
937 FMGlyph *outlineGlyph = getGlyph(font, codepoint, state->size, subpixel, true );
939 drawTextGlyph(font, outlineGlyph, x, y, true );
941 drawTextGlyph(font, glyph, x, y, false );
942 addGlyphAdvance( &x, &subpixel, glyph );
944 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
947 return x + ( subpixel >= 32 );
950 int drawTextWithCursor( int x, int y, const char *string, int stringlength, int cursoroffset )
960 state = &states[ nstates - 1 ];
964 if( !( state->font ) )
967 if( !( stringlength ) )
968 stringlength = strlen( string );
970 // Align horizontally
971 if( state->align.horzAlignment == right )
972 x -= getTextWidth(string, stringlength );
973 else if( state->align.horzAlignment == center )
974 x -= getTextWidth(string, stringlength ) >> 1;
977 y += roundf( font.getVertAlign(state->align, state->size ) );
980 for( index = 0 ; ; index++ )
982 if( index == cursoroffset )
984 glyph = getGlyph(font, FM_GLYPH_CODEPOINT_CURSOR, state->size, subpixel, false );
986 drawTextCursorGlyph(font, glyph, x, y );
988 if( index >= stringlength )
990 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
992 glyph = getGlyph(font, codepoint, state->size, subpixel, false );
995 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
996 if(font.processImage)
998 FMGlyph *outlineGlyph = getGlyph(font, codepoint, state->size, subpixel, true );
1000 drawTextGlyph(font, outlineGlyph, x, y, true );
1002 drawTextGlyph(font, glyph, x, y, false );
1003 addGlyphAdvance( &x, &subpixel, glyph );
1005 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1008 return x + ( subpixel >= 32 );
1011 int drawTextTruncate( int x, int y, int truncatewidth, const char *string, int stringlength, char *extstring, int extwidth )
1013 int subpixel, index, textwidth, truncatepoint;
1021 state = &states[ nstates - 1 ];
1024 prevGlyphIndex = -1;
1025 if( !( state->font ) )
1028 if( !( stringlength ) )
1029 stringlength = strlen( string );
1030 textwidth = getTextWidthTruncate(string, stringlength, truncatewidth );
1032 truncatepoint = x + truncatewidth;
1033 if( textwidth <= truncatewidth )
1037 if( extwidth >= truncatewidth )
1039 truncatepoint -= extwidth;
1042 // Align horizontally
1043 if( state->align.horzAlignment == right )
1045 else if( state->align.horzAlignment == center )
1046 x -= textwidth >> 1;
1049 y += roundf( font.getVertAlign(state->align, state->size ) );
1052 for( index = 0 ; index < stringlength ; index++ )
1054 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1056 glyph = getGlyph(font, codepoint, state->size, subpixel, false );
1059 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1060 if(font.processImage)
1062 FMGlyph *outlineGlyph = getGlyph(font, codepoint, state->size, subpixel, true );
1064 drawTextGlyph(font, outlineGlyph, x, y, true );
1066 drawTextGlyph(font, glyph, x, y, false );
1067 addGlyphAdvance( &x, &subpixel, glyph );
1068 if( x > truncatepoint )
1071 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1073 x += ( subpixel >= 32 );
1075 drawText(x, y, extstring, 0 );
1085 int getTextWidth( const char *string, int stringlength )
1087 return getTextWidthTruncate(string, stringlength, ( (unsigned)1 << ( ( sizeof(int) * CHAR_BIT ) - 1 ) ) - 1 );
1091 int getTextWidthTruncate( const char *string, int stringlength, int truncatewidth )
1093 int subpixel, index, advance;
1101 state = &states[ nstates - 1 ];
1104 prevGlyphIndex = -1;
1105 if( !( state->font ) )
1108 if( !( stringlength ) )
1109 stringlength = strlen( string );
1113 for( index = 0 ; index < stringlength ; index++ )
1115 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1117 glyph = getGlyph(font, codepoint, state->size, subpixel, false );
1120 font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1121 addGlyphAdvance( &advance, &subpixel, glyph );
1122 if( advance > truncatewidth )
1125 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1128 return advance + ( subpixel >= 32 );
1131 int getTextBounds( int x, int y, const char *string, int stringlength, int *bounds )
1133 int subpixel, index;
1141 int startx, advance;
1142 int minx, miny, maxx, maxy;
1144 state = &states[ nstates - 1 ];
1147 prevGlyphIndex = -1;
1148 if( !( state->font ) )
1151 if( !( stringlength ) )
1152 stringlength = strlen( string );
1155 y += font.getVertAlign(state->align, state->size );
1162 for( index = 0 ; index < stringlength ; index++ )
1164 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1166 glyph = getGlyph(font, codepoint, state->size, subpixel, false );
1169 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1170 glyph->getQuad(x, y, q);
1179 addGlyphAdvance( &x, &subpixel, glyph );
1181 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1184 advance = x - startx;
1186 /* Align horizontally */
1187 if( state->align.horzAlignment == right )
1192 else if( state->align.horzAlignment == center )
1194 minx -= advance * 0.5f;
1195 maxx -= advance * 0.5f;
1206 return advance + ( subpixel >= 32 );
1209 // Find text offset up to truncatewidth
1210 int getTextTruncateOffset( int truncatewidth, const char *string, int stringlength, int extwidth, int *retextflag, int *retfullwidth )
1212 int subpixel, index, advance, truncatewidthext, truncateindex, extflag, fullwidth = 0;
1220 state = &states[ nstates - 1 ];
1225 if( extwidth >= truncatewidth )
1229 prevGlyphIndex = -1;
1230 if( !( state->font ) )
1233 if( stringlength <= 0 )
1234 stringlength = strlen( string );
1236 truncatewidthext = truncatewidth - extwidth;
1242 for( index = 0 ; ; index++ )
1244 if( index >= stringlength )
1246 truncateindex = index;
1247 fullwidth = advance + ( subpixel >= 32 );
1250 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1252 glyph = getGlyph(font, codepoint, state->size, subpixel, false );
1255 font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1256 addGlyphAdvance( &advance, &subpixel, glyph );
1257 if( advance > truncatewidth )
1262 if( advance <= truncatewidthext )
1264 truncateindex = index + 1;
1265 fullwidth = advance + ( subpixel >= 32 );
1268 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1274 *retfullwidth = fullwidth;
1276 *retfullwidth = fullwidth;
1279 *retextflag = extflag;
1281 return truncateindex;
1284 // Find text offset nearest to the given width
1285 int getTextNearestOffset( int targetwidth, const char *string, int stringlength )
1287 int subpixel, index, advance, truncateindex, distance, bestdistance;
1295 state = &states[ nstates - 1 ];
1298 prevGlyphIndex = -1;
1299 if( !( state->font ) )
1302 if( stringlength <= 0 )
1303 stringlength = strlen( string );
1308 bestdistance = abs( targetwidth );
1309 for( index = 0 ; index < stringlength ; index++ )
1311 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1313 glyph = getGlyph(font, codepoint, state->size, subpixel, false );
1316 font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1317 addGlyphAdvance( &advance, &subpixel, glyph );
1318 distance = abs( targetwidth - ( advance + ( subpixel >= 32 ) ) );
1319 if( distance > bestdistance )
1321 bestdistance = distance;
1322 truncateindex = index + 1;
1324 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1327 return truncateindex;
1332 static void flush( bool rendererFlush )
1334 // Flush texture updates
1335 if( ( dirtyrect[0] < dirtyrect[2] ) && ( dirtyrect[1] < dirtyrect[3] ) )
1337 renderer.updateTexture( dirtyrect, texdata );
1339 dirtyrect[0] = width;
1340 dirtyrect[1] = height;
1344 // Flush buffered glyphs
1345 if( rendererFlush ) renderer.flush( );
1348 // Flush buffered texture updates, renderer->updateTexture()
1358 void getFontExtent( int *retascent, int *retdescent )
1361 FMState *state = &states[ nstates - 1 ];
1367 *retascent = -(int)ceilf( font.ascender * (float)state->size );
1369 *retdescent = -(int)floorf( font.descender * (float)state->size );
1373 void getFontLimits( int *retlimitminy, int *retlimitmaxy )
1378 state = &states[ nstates - 1 ];
1379 if( !( state->font ) )
1384 *retlimitminy = -(int)ceilf( font.limitmaxy * state->size );
1386 *retlimitmaxy = -(int)floorf( font.limitminy * state->size );
1391 int getFontLineHeight( )
1396 state = &states[ nstates - 1 ];
1397 if( !( state->font ) )
1400 return (int)ceilf( font.lineHeight * state->size );
1406 // Pull texture changes
1407 const byte *getTextureData( int *width, int *height )
1410 *width = this.width;
1412 *height = this.height;
1416 // Retrieve the dirty rectangle, telling fontmanager you will manually perform the update, returns 0 if no part of the texture requires an update
1417 bool validateTexture( int *retdirtyrect )
1419 if( ( dirtyrect[0] < dirtyrect[2] ) && ( dirtyrect[1] < dirtyrect[3] ) )
1421 retdirtyrect[0] = dirtyrect[0];
1422 retdirtyrect[1] = dirtyrect[1];
1423 retdirtyrect[2] = dirtyrect[2];
1424 retdirtyrect[3] = dirtyrect[3];
1426 dirtyrect[0] = width;
1427 dirtyrect[1] = height;
1435 // Returns current atlas size
1436 void getAtlasSize( int *retwidth, int *retheight )
1438 *retwidth = this.width;
1439 *retheight = this.height;
1443 // Expands the atlas size
1444 bool expandAtlas( int width, int height )
1446 width = Max( width, this.width );
1447 height = Max( height, this.height );
1449 if( ( width == this.width ) && ( height == this.height ) )
1452 // Flush all pending glyphs
1455 // Create new texture
1456 if( renderer.resizeTexture( width, height ) )
1461 // Copy old texture data over.
1462 if( !( data = new byte[width * bytesperline] ) )
1464 for( i = 0 ; i < this.height ; i++ )
1466 byte * dst = &data[ (i * width) * bytesperpixel ];
1467 byte * src = &this.texdata[ i * this.bytesperline ];
1468 memcpy( dst, src, bytesperline);
1469 if( width > this.width )
1470 memset( dst+bytesperline, 0, (width - this.width) * bytesperpixel );
1472 if( height > this.height )
1473 memset( &data[ width * this.height * bytesperpixel], 0, ( height - this.height ) *bytesperline );
1475 delete this.texdata;
1478 // Increase atlas size
1479 atlas.expand( width, height );
1481 // Add existing data as dirty.
1484 dirtyrect[2] = this.width;
1485 dirtyrect[3] = atlas.getAtlasMaxHeight();
1488 this.height = height;
1489 this.bytesperline = this.width * bytesperpixel;
1490 widthinv = 1.0f / this.width;
1491 heightinv = 1.0f / this.height;
1498 // Reset the whole fm
1499 bool resetAtlas( int width, int height )
1501 // Flush all pending glyphs
1504 // Create new texture
1505 if(renderer.resizeTexture( width, height ) )
1508 atlas.reset( width, height );
1510 // Clear texture data.
1511 texdata = renew texdata byte[width * height * bytesperpixel];
1512 if(!texdata) return 0;
1513 memset( this.texdata, 0, width * height );
1516 dirtyrect[0] = width;
1517 dirtyrect[1] = height;
1521 // Reset cached glyphs
1522 for(font : fontList)
1525 font.glyphcount = 0;
1526 for( i = 0 ; i < FM_HASH_TABLE_SIZE ; i++ )
1527 font.hashtable[i] = -1;
1529 renderer.resetImages( );
1532 this.height = height;
1533 this.bytesperline = width * bytesperpixel;
1534 this.widthinv = 1.0f / this.width;
1535 this.heightinv = 1.0f / this.height;
1543 bool initPathDraw( FMPathDraw pathdraw )
1545 FMState *state = &states[ nstates - 1 ];
1546 FMFont font = state->font;
1549 pathdraw.prevGlyphIndex = -1;
1550 pathdraw.middleAlign = font.middleAlign * (float)state->size;
1556 float pathDrawCharacter( FMPathDraw pathdraw, float x, float y, float vectorx, float vectory, int unicode )
1559 FMState *state = &states[ nstates - 1 ];
1560 FMFont font = state->font;
1561 FMGlyph *glyph = getGlyph(font, unicode, state->size, 0, false );
1564 subpixel = font.ftFont.getGlyphKernAdvance( pathdraw.prevGlyphIndex, glyph->glyphindex );
1565 if(font.processImage)
1567 FMGlyph *outlineGlyph = getGlyph(font, unicode, state->size, 0, true );
1569 drawTextGlyphFloat(font, outlineGlyph, x, y, vectorx, vectory, (float)subpixel * (1.0f/64.0f), pathdraw.middleAlign, true );
1571 drawTextGlyphFloat(font, glyph, x, y, vectorx, vectory, (float)subpixel * (1.0f/64.0f), pathdraw.middleAlign, false );
1573 subpixel += glyph->advance;
1574 pathdraw.prevGlyphIndex = glyph->glyphindex;
1579 pathdraw.prevGlyphIndex = -1;
1582 return (float)subpixel * (1.0f/64.0f);
1585 float pathDrawPredictAdvance( FMPathDraw pathdraw, unichar unicode )
1588 FMState *state = &states[ nstates - 1 ];
1589 FMFont font = state->font;
1590 FMGlyph *glyph = getGlyph(font, unicode, state->size, 0, false );
1593 subpixel = font.ftFont.getGlyphKernAdvance( pathdraw.prevGlyphIndex, glyph->glyphindex );
1594 subpixel += glyph->advance;
1596 return (float)subpixel * (1.0f/64.0f);