1 /* *****************************************************************************
2 * Original Version Copyright (c) 2007-2014 Alexis Naveros.
4 * Ecere Corporation has unlimited/unrestricted rights.
5 * *****************************************************************************/
21 static inline uint32 decodeUTF8( uint32 b, uint32 *state, unichar *retCodePoint ) { return ccUtf8ToUnicode(b, state, (uint *)retCodePoint); }
25 static void buildCursorGlyph( byte *dst, int glyphwidth, int glyphheight, int dststride )
27 int x, y, hbarheight, hgap, hline, vline;
32 hbarheight = 1 + ( glyphheight >> 6 );
33 hgap = ( glyphwidth - ( 1 + ( glyphheight >> 6 ) ) ) >> 1;
34 hline = hgap + glyphwidth - ( hgap << 1 );
35 vline = glyphheight - ( hbarheight << 1 );
36 for( y = 0 ; y < hbarheight ; y++ )
39 for( x = 0 ; x < glyphwidth ; x++ )
43 for( y = 0 ; y < vline ; y++ )
46 for( x = 0 ; x < hgap ; x++ )
48 for( ; x < hline ; x++ )
50 for( ; x < glyphwidth ; x++ )
54 for( y = 0 ; y < hbarheight ; y++ )
57 for( x = 0 ; x < glyphwidth ; x++ )
64 for( y = 0 ; y < glyphheight ; y++ )
67 for( x = 0 ; x < glyphwidth ; x++ )
76 static inline void addGlyphAdvance( int *x, int *subpixel, FMGlyph *glyph )
78 *subpixel += glyph->advance;
79 #if FM_SUBPIXEL_ROUNDING_RANGE
80 *subpixel = ( *subpixel + (FM_SUBPIXEL_ROUNDING_RANGE>>1) ) & ~(FM_SUBPIXEL_ROUNDING_RANGE-1);
86 public class FontManagerRenderer
91 virtual bool init(int channelCount);
93 // Create, resize or update texture data ; rect is [minx,maxx,miny,maxy]
94 virtual int createTexture( int width, int height );
95 virtual int resizeTexture( int width, int height );
96 virtual void updateTexture( int *rect, const byte *data );
98 // If the renderer does any buffering of drawImage(), flush it all right now (texture may change immediately after)
99 virtual void flush( );
101 // If the renderer records per-image data, return an imageIndex passed to drawImage()
102 virtual int registerImage( int offsetx, int offsety, int width, int height );
103 // Draw an image, imageIndex passed as the value previously returned by registerImage()
104 virtual void drawImage( int targetx, int targety, int imageIndex, bool useExtColor );
105 // Draw an image, called instead of drawImage() for text cursors, can point to exactly the same function
106 virtual void drawImageCursor( int targetx, int targety, int imageIndex );
107 // If drawImage is zero, then this alternate function is called, passing everything required to render the glyph
108 virtual void drawImageAlt( byte *texdata, int targetx, int targety, int offsetx, int offsety, int width, int height );
109 // Draw a non-aligned image, imageIndex passed as the value previously returned by registerImage()
110 virtual void drawImageFloat( float targetx, float targety, float angsin, float angcos, int imageIndex, bool useExtColor );
112 // The renderer must flush all recorded images, registerImage() will be called for new images
113 virtual void resetImages( );
115 virtual void setLayer( uint32 layerIndex );
122 #include <ft2build.h>
124 #include FT_FREETYPE_H
125 #include FT_ADVANCES_H
131 #define FM_SUPPORT_GLYPH_ROTATION (1)
137 #define FM_ENABLE_HINTING (1)
138 #define FM_SUBPIXEL_ROUNDING_RANGE (16)
141 #define FM_HASH_TABLE_SIZE (4096)
142 #define FM_INIT_GLYPHS (1024)
143 #define FM_INIT_ATLAS_NODES (512)
145 #define FM_MAX_STATES (16)
150 struct FMFreeTypeFont
154 static inline int ::init()
157 ftError = FT_Init_FreeType( &ftLibrary2 );
161 static inline int loadFont( byte *data, int dataSize )
163 FT_Error ftError = FT_New_Memory_Face( ftLibrary2, (const FT_Byte*)data, dataSize, 0, &face );
169 FT_Done_Face( face );
172 static inline void getFontVMetrics( float *ascent, float *descent, float *lineHeight, float *limitminy, float *limitmaxy )
174 *ascent = (float)face->ascender;
175 *descent = (float)face->descender;
176 *lineHeight = (float)face->height / (float)face->units_per_EM;
177 *limitminy = (float)face->bbox.yMin / (float)face->units_per_EM;
178 *limitmaxy = (float)face->bbox.yMax / (float)face->units_per_EM;
181 static inline int getGlyphIndex( unichar codepoint )
183 return FT_Get_Char_Index( this.face, codepoint );
186 static int buildGlyphBitmap( int glyph, int size, int subpixel, int *advance, int *x0, int *y0, int *x1, int *y1 )
189 FT_GlyphSlot glyphslot;
192 ftError = FT_Set_Pixel_Sizes( this.face, 0, (FT_UInt)size );
196 subvector.x = subpixel;
198 FT_Set_Transform( face, 0, &subvector );
200 #if FM_ENABLE_HINTING
201 ftError = FT_Load_Glyph( face, glyph, FT_LOAD_RENDER );
203 ftError = FT_Load_Glyph( face, glyph, FT_LOAD_RENDER | FT_LOAD_NO_HINTING );
208 glyphslot = face->glyph;
209 *advance = (int)glyphslot->metrics.horiAdvance;
210 *x0 = glyphslot->bitmap_left;
211 *x1 = glyphslot->bitmap_left + glyphslot->bitmap.width;
212 *y0 = -glyphslot->bitmap_top;
213 *y1 = -glyphslot->bitmap_top + glyphslot->bitmap.rows;
218 static inline int getGlyphKernAdvance( int glyph1, int glyph2 )
221 FT_Get_Kerning( face, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning );
222 return (int)ftKerning.x;
225 static inline byte *getGlyphBitmap( int glyphindex )
227 FT_GlyphSlot glyphslot;
228 glyphslot = face->glyph;
229 return glyphslot->bitmap.buffer;
233 class FMDefBits : uint64
237 int size:11, subPixel:6;
241 static define FM_SIZE_MAX = (1<<11)-1;
242 static define FM_BLUR_RADIUS_MAX = (1<<6)-1;
243 static define FM_BLUR_SCALE_MAX = (1<<6)-1;
245 static define FM_GLYPH_CODEPOINT_CURSOR = 0x1;
246 static define FM_GLYPH_CODEPOINT_REPLACEMENT = 0xfffd;
248 public enum FMVerticalAlignment { baseline, top, middle, bottom };
250 public class FMTextAlignment : uint16
253 Alignment horzAlignment:2;
254 FMVerticalAlignment vertAlignment:2;
257 public struct FMPathDraw
263 struct FMQuad { int x0, y0, x1, y1; };
269 short x0, y0, x1, y1;
270 short advance, offsetx, offsety;
274 static void getQuad( float x, float y, FMQuad q )
276 int rx = (int)(x + offsetx);
277 int ry = (int)(y + offsety);
278 q = { rx, ry, rx + x1 - x0, ry + y1 - y0 };
282 public class FMFont : struct
284 public LinkElement link;
285 FMFreeTypeFont ftFont;
291 float limitminy, limitmaxy;
295 int hashtable[FM_HASH_TABLE_SIZE];
296 int glyphPaddingWidth;
298 void (*processImage)( void *opaquecontext, byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingwidth, int pass);
299 void *processImageContext;
302 public void setFontImageProcessing(
303 void (*processImage)( byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingsize, void *opaquecontext ),
304 void *opaquecontext )
306 this.processImage = processImage;
307 this.processImageContext = opaquecontext;
312 float outlineAlphaFactor;
313 float outlineIntensityFactor;
315 static void ::outlineProcessGlyphImage( FMFont font, byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingwidth, int isOutline )
318 byte *src, *dst, *dstrow;
319 float intensityfactor, alphafactor, range, alpha, intensity, rangeinv, rangebase;
320 float *distancemap, *dmap;
322 distancemap = new float[width * height];
325 imgDistMapBuild( distancemap, src, width, height, bytesperpixel, bytesperline );
327 alphafactor = font.outlineAlphaFactor; //2.0f;
328 intensityfactor = font.outlineIntensityFactor; // 0.2f;
329 range = (float)font.outlineRadius;
330 rangeinv = 1.0f / range;
334 for( y = 0 ; y < height ; y++ )
337 for( x = 0 ; x < width ; x++ )
339 rangebase = ( range - dmap[ x ] ) * rangeinv;
340 alpha = alphafactor * rangebase;
341 intensity = fmaxf( (float)dstrow[0] * (1.0f/255.0f), intensityfactor * rangebase );
342 if(bytesperpixel == 2)
345 dstrow[0] = (byte)roundf( fmaxf( 0.0f, fminf( 255.0f, alpha * 255.0f ) ) );
346 /* Intensity channel */
347 dstrow[1] = (byte)roundf( fmaxf( 0.0f, fminf( 255.0f, intensity * 255.0f ) ) );
352 dstrow[0] = (byte)roundf( fmaxf( 0.0f, fminf( 255.0f, alpha * 255.0f ) ) );
354 dstrow[0] = (byte)roundf( fmaxf( 0.0f, fminf( 255.0f, intensity * 255.0f ) ) );
356 dstrow += bytesperpixel;
364 public void setOutline(float size, float fade)
366 outlineIntensityFactor = 1.0f / (0.2f + size);
367 outlineAlphaFactor = 1.0f / (0.2f + fade);
368 outlineRadius = size;
369 processImage = outlineProcessGlyphImage;
370 processImageContext = this;
380 static FMGlyph *allocGlyph( )
382 if( glyphcount >= glyphalloc )
385 if( !(glyphs = renew glyphs FMGlyph[glyphalloc]) )
388 return &glyphs[ glyphcount++ ];
393 static float getVertAlign( FMTextAlignment align, int size )
396 if( align.vertAlignment == top )
397 return ascender * sizef;
398 else if( align.vertAlignment == middle )
399 return middleAlign * sizef;
400 else if( align.vertAlignment == bottom )
401 return descender * sizef;
405 static inline void addKerning( int prevGlyphIndex, FMGlyph *glyph, int *x, int *subpixel )
407 if( prevGlyphIndex != -1 )
409 *subpixel += ftFont.getGlyphKernAdvance( prevGlyphIndex, glyph->glyphindex );
410 #if FM_SUBPIXEL_ROUNDING_RANGE
411 *subpixel = ( *subpixel + (FM_SUBPIXEL_ROUNDING_RANGE>>1) ) & ~(FM_SUBPIXEL_ROUNDING_RANGE-1);
413 *x += *subpixel >> 6;
424 FMTextAlignment align;
430 static FT_Library ftLibrary2;
432 static void copyGlyphBitmap1( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride )
435 for( y = 0 ; y < glyphheight ; y++ )
437 byte *dstrow = &dst[ y * dststride ];
438 for( x = 0 ; x < glyphwidth ; x++ )
439 dstrow[ x ] = src[ x ];
444 static void copyGlyphBitmap2( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride )
447 for( y = 0 ; y < glyphheight ; y++ )
449 byte *dstrow = &dst[ y * dststride ];
450 for( x = 0 ; x < glyphwidth ; x++ )
451 dstrow[ x << 1 ] = src[ x ];
456 static void copyGlyphBitmap4( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride )
459 for( y = 0 ; y < glyphheight ; y++ )
461 byte *dstrow = &dst[ y * dststride ];
462 for( x = 0 ; x < glyphwidth ; x++ )
463 dstrow[ x << 2 ] = src[ x ];
468 public class FontManager
470 FontManagerRenderer renderer;
472 float widthinv, heightinv;
477 AtlasBuilder atlas { };
481 LinkList<FMFont, link = link> fontList { };
483 FMState states[FM_MAX_STATES];
486 void (*copyGlyphBitmap)( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride );
489 static FMGlyph *getGlyph( FMFont font, unichar codepoint, int size, int subpixel, bool outlinePass )
491 int i, glyphindex, advance, x0, y0, x1, y1, gx, gy;
492 int glyphwidth, glyphheight, glyphareawidth, glyphareaheight;
503 padding = font.glyphPaddingWidth;
505 // Find code point and size.
506 glyphdef = FMDefBits { codepoint, size, subpixel, outlinePass };
507 hashindex = ccHash32Int64Inline( glyphdef ) & ( FM_HASH_TABLE_SIZE - 1 );
508 i = font.hashtable[hashindex];
511 if( glyphdef == font.glyphs[i].glyphdef )
512 return &font.glyphs[i];
513 i = font.glyphs[i].listnext;
516 /* Could not find glyph, create it */
517 if( codepoint == FM_GLYPH_CODEPOINT_CURSOR )
528 y0 = -(int)ceilf( font.limitmaxy * (float)size );
529 y1 = -(int)floorf( font.limitminy * (float)size );
530 i = ( (y1 - y0) - size ) / 3;
536 glyphindex = font.ftFont.getGlyphIndex( codepoint );
537 if( !( font.ftFont.buildGlyphBitmap( glyphindex, size, subpixel, &advance, &x0, &y0, &x1, &y1 ) ) )
539 if( codepoint != FM_GLYPH_CODEPOINT_REPLACEMENT )
540 return getGlyph(font, FM_GLYPH_CODEPOINT_REPLACEMENT, size, subpixel, outlinePass );
544 glyphwidth = ( x1 - x0 );
545 glyphheight = ( y1 - y0 );
546 glyphareawidth = glyphwidth + (padding << 1);
547 glyphareaheight = glyphheight + (padding << 1);
549 // Find free spot for the rect in the atlas
550 added = atlas.addRect( glyphareawidth, glyphareaheight, &gx, &gy );
551 if( !( added ) && ( onAtlasFull ) )
553 /* Atlas is full, let the user to resize the atlas (or not), and try again. */
555 added = atlas.addRect( glyphareawidth, glyphareaheight, &gx, &gy );
560 /* Build the glyph */
561 glyph = font.allocGlyph();
562 glyph->glyphdef = glyphdef;
563 glyph->glyphindex = glyphindex;
564 glyph->x0 = (short)gx;
565 glyph->y0 = (short)gy;
566 glyph->x1 = (short)( glyph->x0 + glyphareawidth );
567 glyph->y1 = (short)( glyph->y0 + glyphareaheight );
568 glyph->advance = (short)advance;
569 glyph->offsetx = (short)( x0 - padding );
570 glyph->offsety = (short)( y0 - padding );
572 glyph->imageIndex = -1;
573 if( renderer.registerImage )
577 renderer.setLayer(3); //DM_LAYER_BELOW);
579 glyph->imageIndex = renderer.registerImage( gx, gy, glyphareawidth, glyphareaheight );
582 renderer.setLayer(6); //DM_LAYER_NORMAL);
586 // Add char to hash table
587 glyph->listnext = font.hashtable[hashindex];
588 font.hashtable[hashindex] = font.glyphcount - 1;
590 // Clear glyph image area (TODO: wasteful when single channel without prepare callback?)
591 dst = &texdata[ ( glyph->x0 * bytesperpixel ) + ( glyph->y0 * bytesperline ) ];
592 for( i = 0 ; i < glyphareaheight ; i++, dst += bytesperline )
593 memset( dst, 0, glyphareawidth * bytesperpixel );
596 dst = &texdata[ ( glyph->x0 + padding ) * bytesperpixel + ( ( glyph->y0 + padding ) * bytesperline ) + channelindex];
597 if( codepoint == FM_GLYPH_CODEPOINT_CURSOR )
599 src = new byte[ glyphwidth * glyphheight ];
600 buildCursorGlyph( src, glyphwidth, glyphheight, glyphwidth );
601 copyGlyphBitmap( dst, src, glyphwidth, glyphheight, bytesperline );
606 src = font.ftFont.getGlyphBitmap(glyphindex);
607 copyGlyphBitmap( dst, src, glyphwidth, glyphheight, bytesperline );
610 // User custom font image processing
611 if(font.processImage)
613 dst = &texdata[ ( glyph->x0 * bytesperpixel ) + ( glyph->y0 * bytesperline ) ];
614 font.processImage( font.processImageContext, dst, glyphareawidth, glyphareaheight, bytesperpixel, bytesperline, font.glyphPaddingWidth, outlinePass );
617 dirtyrect[0] = Min( dirtyrect[0], glyph->x0 );
618 dirtyrect[1] = Min( dirtyrect[1], glyph->y0 );
619 dirtyrect[2] = Max( dirtyrect[2], glyph->x1 );
620 dirtyrect[3] = Max( dirtyrect[3], glyph->y1 );
625 static inline void drawTextGlyph( FMFont font, FMGlyph *glyph, int x, int y, bool useExtColor )
627 int ptx = x + glyph->offsetx;
628 int pty = y + glyph->offsety;
629 if( renderer.drawImage )
630 renderer.drawImage( ptx, pty, glyph->imageIndex, useExtColor );
631 else if( renderer.drawImageAlt )
632 renderer.drawImageAlt( texdata, ptx, pty, glyph->x0, glyph->y0, glyph->x1 - glyph->x0, glyph->y1 - glyph->y0 );
635 static inline void drawTextCursorGlyph( FMFont font, FMGlyph *glyph, int x, int y )
638 ptx = x + glyph->offsetx;
639 pty = y + glyph->offsety;
640 if( renderer.drawImageCursor )
641 renderer.drawImageCursor( ptx, pty, glyph->imageIndex );
642 else if( renderer.drawImageAlt )
643 renderer.drawImageAlt( texdata, ptx, pty, glyph->x0, glyph->y0, glyph->x1 - glyph->x0, glyph->y1 - glyph->y0 );
646 static inline void drawTextGlyphFloat( FMFont font, FMGlyph *glyph, float x, float y, float vectorx, float vectory, float offsetx, float offsety, bool useExtColor )
648 float vectx = (float)glyph->offsetx + offsetx;
649 float vecty = (float)glyph->offsety + offsety;
650 float ptx = x + ( vectorx * vectx ) - ( vectory * vecty );
651 float pty = y + ( vectorx * vecty ) + ( vectory * vectx );
652 renderer.drawImageFloat( ptx, pty, vectory, vectorx, glyph->imageIndex, useExtColor );
657 property FontManagerRenderer renderer { set { renderer = value; } get { return renderer; } }
659 // When notified of a full atlas, you should call fmExpandAtlas() or fmResetAtlas() within the callback
660 virtual void onAtlasFull()
663 atlas.reset(atlas.width, atlas.height);
666 // Create and destroy font manager
667 bool create( int width, int height, int channelCount, int channelIndex, FontManagerRenderer renderer)
671 if( ( channelCount != 1 ) && ( channelCount != 2 ) && ( channelCount != 4 ) )
673 if( ( width <= 0 ) || ( height <= 0 ) )
676 this.renderer = renderer;
678 renderer.init(channelCount);
680 // Initialize implementation library
681 if(FMFreeTypeFont::init() )
684 this.height = height;
685 bytesperpixel = channelCount;
686 bytesperline = width * bytesperpixel;
687 this.channelindex = channelIndex;
688 if(renderer.createTexture( width, height ))
690 if((atlas.create( this.width, this.height, FM_INIT_ATLAS_NODES )))
692 // Create texture for the cache.
693 widthinv = 1.0f / width;
694 heightinv = 1.0f / height;
695 texdata = new byte[width * height * bytesperpixel];
698 memset( texdata, 0, height * bytesperline );
700 dirtyrect[0] = this.width;
701 dirtyrect[1] = this.height;
705 if( bytesperpixel == 1 )
706 copyGlyphBitmap = copyGlyphBitmap1;
707 else if( bytesperpixel == 2 )
708 copyGlyphBitmap = copyGlyphBitmap2;
710 copyGlyphBitmap = copyGlyphBitmap4;
737 void setState( FMFont font, int size, int align)
740 if( size >= FM_SIZE_MAX )
742 state = &states[ nstates - 1 ];
744 state->size = (uint16)size;
745 state->align = (uint16)align;
748 void setFont( FMFont font )
750 states[ nstates - 1 ].font = font;
753 void setSize( int size )
755 if( size >= FM_SIZE_MAX )
758 states[ nstates - 1 ].size = (uint16)size;
761 void setAlign( int align )
763 states[ nstates - 1 ].align = (uint16)align;
766 // Set image manipuation callback
767 void setFontImageProcessing( FMFont font, void (*processImage)( byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingwidth, void *opaquecontext ), void *opaquecontext )
775 if(nstates < FM_MAX_STATES)
778 memcpy( &states[nstates], &states[nstates-1], sizeof(FMState) );
791 FMState *state = &states[ nstates - 1 ];
794 state->align = { left, baseline };
800 static void freeFont(FMFont font)
804 fontList.Remove((IteratorPointer)font);
809 // Add font from FontResource
810 FMFont getFont(FontResource fontResource)
814 Array<FaceInfo> infos = ResolveFont(fontResource.faceName, fontResource.size, fontResource.flags);
819 font = addFont(i.fileName, Max(2, (int)(1+fontResource.outlineSize)));
822 font.setOutline(fontResource.outlineSize, fontResource.outlineFade);
832 // Add font from file
833 FMFont addFont(const String path, int glyphPaddingWidth )
836 File f = FileOpen(path, read);
839 // Read in the font data
840 int dataSize = f.GetSize();
841 byte *data = new byte[dataSize];
844 f.Read(data, 1, dataSize);
845 font = addFontData(data, dataSize, glyphPaddingWidth);
854 // Add font from data ; do not free( data ), the font manager will do that when removing the font
855 FMFont addFontData( byte *data, int dataSize, int glyphPaddingWidth )
859 glyphs = new FMGlyph[FM_INIT_GLYPHS];
860 glyphalloc = FM_INIT_GLYPHS;
861 glyphPaddingWidth = glyphPaddingWidth;
866 float ascent, descent, fontHeight;
871 for( i = 0 ; i < FM_HASH_TABLE_SIZE ; i++ )
872 font.hashtable[i] = -1;
875 if(!font.ftFont.loadFont(data, dataSize))
881 // Store normalized line height. The real line height is got by multiplying by font size.
882 font.ftFont.getFontVMetrics( &ascent, &descent, &font.lineHeight, &font.limitminy, &font.limitmaxy );
883 fontHeight = ascent - descent;
884 font.ascender = ascent / fontHeight;
885 font.descender = descent / fontHeight;
886 font.middleAlign = 0.5f * ( font.ascender + font.descender );
887 font.fontdata = data;
893 void removeFont( FMFont font )
899 int drawText( int x, int y, const char *string, int stringlength )
909 state = &states[ nstates - 1 ];
913 if( !( state->font ) )
916 if( !( stringlength ) )
917 stringlength = strlen( string );
919 // Align horizontally
920 if( state->align.horzAlignment == right )
921 x -= getTextWidth(string, stringlength );
922 else if( state->align.horzAlignment == center )
923 x -= getTextWidth(string, stringlength ) >> 1;
926 y += roundf( font.getVertAlign(state->align, state->size ) );
929 for( index = 0 ; index < stringlength ; index++ )
931 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
933 glyph = getGlyph(font, codepoint, state->size, subpixel, false );
936 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
937 #if !defined(SHADERS)
938 if(font.processImage)
940 FMGlyph *outlineGlyph = getGlyph(font, codepoint, state->size, subpixel, true );
942 drawTextGlyph(font, outlineGlyph, x, y, true );
945 drawTextGlyph(font, glyph, x, y, false );
946 addGlyphAdvance( &x, &subpixel, glyph );
948 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
951 return x + ( subpixel >= 32 );
954 int drawTextWithCursor( int x, int y, const char *string, int stringlength, int cursoroffset )
964 state = &states[ nstates - 1 ];
968 if( !( state->font ) )
971 if( !( stringlength ) )
972 stringlength = strlen( string );
974 // Align horizontally
975 if( state->align.horzAlignment == right )
976 x -= getTextWidth(string, stringlength );
977 else if( state->align.horzAlignment == center )
978 x -= getTextWidth(string, stringlength ) >> 1;
981 y += roundf( font.getVertAlign(state->align, state->size ) );
984 for( index = 0 ; ; index++ )
986 if( index == cursoroffset )
988 glyph = getGlyph(font, FM_GLYPH_CODEPOINT_CURSOR, state->size, subpixel, false );
990 drawTextCursorGlyph(font, glyph, x, y );
992 if( index >= stringlength )
994 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
996 glyph = getGlyph(font, codepoint, state->size, subpixel, false );
999 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1000 #if !defined(SHADERS)
1001 if(font.processImage)
1003 FMGlyph *outlineGlyph = getGlyph(font, codepoint, state->size, subpixel, true );
1005 drawTextGlyph(font, outlineGlyph, x, y, true );
1008 drawTextGlyph(font, glyph, x, y, false );
1009 addGlyphAdvance( &x, &subpixel, glyph );
1011 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1014 return x + ( subpixel >= 32 );
1017 int drawTextTruncate( int x, int y, int truncatewidth, const char *string, int stringlength, char *extstring, int extwidth )
1019 int subpixel, index, textwidth, truncatepoint;
1027 state = &states[ nstates - 1 ];
1030 prevGlyphIndex = -1;
1031 if( !( state->font ) )
1034 if( !( stringlength ) )
1035 stringlength = strlen( string );
1036 textwidth = getTextWidthTruncate(string, stringlength, truncatewidth );
1038 truncatepoint = x + truncatewidth;
1039 if( textwidth <= truncatewidth )
1043 if( extwidth >= truncatewidth )
1045 truncatepoint -= extwidth;
1048 // Align horizontally
1049 if( state->align.horzAlignment == right )
1051 else if( state->align.horzAlignment == center )
1052 x -= textwidth >> 1;
1055 y += roundf( font.getVertAlign(state->align, state->size ) );
1058 for( index = 0 ; index < stringlength ; index++ )
1060 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1062 glyph = getGlyph(font, codepoint, state->size, subpixel, false );
1065 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1066 #if !defined(SHADERS)
1067 if(font.processImage)
1069 FMGlyph *outlineGlyph = getGlyph(font, codepoint, state->size, subpixel, true );
1071 drawTextGlyph(font, outlineGlyph, x, y, true );
1074 drawTextGlyph(font, glyph, x, y, false );
1075 addGlyphAdvance( &x, &subpixel, glyph );
1076 if( x > truncatepoint )
1079 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1081 x += ( subpixel >= 32 );
1083 drawText(x, y, extstring, 0 );
1093 int getTextWidth( const char *string, int stringlength )
1095 return getTextWidthTruncate(string, stringlength, ( (unsigned)1 << ( ( sizeof(int) * CHAR_BIT ) - 1 ) ) - 1 );
1099 int getTextWidthTruncate( const char *string, int stringlength, int truncatewidth )
1101 int subpixel, index, advance;
1109 state = &states[ nstates - 1 ];
1112 prevGlyphIndex = -1;
1113 if( !( state->font ) )
1116 if( !( stringlength ) )
1117 stringlength = strlen( string );
1121 for( index = 0 ; index < stringlength ; index++ )
1123 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1125 glyph = getGlyph(font, codepoint, state->size, subpixel, false );
1128 font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1129 addGlyphAdvance( &advance, &subpixel, glyph );
1130 if( advance > truncatewidth )
1133 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1136 return advance + ( subpixel >= 32 );
1139 int getTextBounds( int x, int y, const char *string, int stringlength, int *bounds )
1141 int subpixel, index;
1149 int startx, advance;
1150 int minx, miny, maxx, maxy;
1152 state = &states[ nstates - 1 ];
1155 prevGlyphIndex = -1;
1156 if( !( state->font ) )
1159 if( !( stringlength ) )
1160 stringlength = strlen( string );
1163 y += font.getVertAlign(state->align, state->size );
1170 for( index = 0 ; index < stringlength ; index++ )
1172 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1174 glyph = getGlyph(font, codepoint, state->size, subpixel, false );
1177 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1178 glyph->getQuad(x, y, q);
1187 addGlyphAdvance( &x, &subpixel, glyph );
1189 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1192 advance = x - startx;
1194 /* Align horizontally */
1195 if( state->align.horzAlignment == right )
1200 else if( state->align.horzAlignment == center )
1202 minx -= advance * 0.5f;
1203 maxx -= advance * 0.5f;
1214 return advance + ( subpixel >= 32 );
1217 // Find text offset up to truncatewidth
1218 int getTextTruncateOffset( int truncatewidth, const char *string, int stringlength, int extwidth, int *retextflag, int *retfullwidth )
1220 int subpixel, index, advance, truncatewidthext, truncateindex, extflag, fullwidth = 0;
1228 state = &states[ nstates - 1 ];
1233 if( extwidth >= truncatewidth )
1237 prevGlyphIndex = -1;
1238 if( !( state->font ) )
1241 if( stringlength <= 0 )
1242 stringlength = strlen( string );
1244 truncatewidthext = truncatewidth - extwidth;
1250 for( index = 0 ; ; index++ )
1252 if( index >= stringlength )
1254 truncateindex = index;
1255 fullwidth = advance + ( subpixel >= 32 );
1258 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1260 glyph = getGlyph(font, codepoint, state->size, subpixel, false );
1263 font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1264 addGlyphAdvance( &advance, &subpixel, glyph );
1265 if( advance > truncatewidth )
1270 if( advance <= truncatewidthext )
1272 truncateindex = index + 1;
1273 fullwidth = advance + ( subpixel >= 32 );
1276 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1282 *retfullwidth = fullwidth;
1284 *retfullwidth = fullwidth;
1287 *retextflag = extflag;
1289 return truncateindex;
1292 // Find text offset nearest to the given width
1293 int getTextNearestOffset( int targetwidth, const char *string, int stringlength )
1295 int subpixel, index, advance, truncateindex, distance, bestdistance;
1303 state = &states[ nstates - 1 ];
1306 prevGlyphIndex = -1;
1307 if( !( state->font ) )
1310 if( stringlength <= 0 )
1311 stringlength = strlen( string );
1316 bestdistance = abs( targetwidth );
1317 for( index = 0 ; index < stringlength ; index++ )
1319 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1321 glyph = getGlyph(font, codepoint, state->size, subpixel, false );
1324 font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1325 addGlyphAdvance( &advance, &subpixel, glyph );
1326 distance = abs( targetwidth - ( advance + ( subpixel >= 32 ) ) );
1327 if( distance > bestdistance )
1329 bestdistance = distance;
1330 truncateindex = index + 1;
1332 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1335 return truncateindex;
1340 static void flush( bool rendererFlush )
1342 // Flush texture updates
1343 if( ( dirtyrect[0] < dirtyrect[2] ) && ( dirtyrect[1] < dirtyrect[3] ) )
1345 renderer.updateTexture( dirtyrect, texdata );
1347 dirtyrect[0] = width;
1348 dirtyrect[1] = height;
1352 // Flush buffered glyphs
1353 if( rendererFlush ) renderer.flush( );
1356 // Flush buffered texture updates, renderer->updateTexture()
1366 void getFontExtent( int *retascent, int *retdescent )
1369 FMState *state = &states[ nstates - 1 ];
1375 *retascent = -(int)ceilf( font.ascender * (float)state->size );
1377 *retdescent = -(int)floorf( font.descender * (float)state->size );
1381 void getFontLimits( int *retlimitminy, int *retlimitmaxy )
1386 state = &states[ nstates - 1 ];
1387 if( !( state->font ) )
1392 *retlimitminy = -(int)ceilf( font.limitmaxy * state->size );
1394 *retlimitmaxy = -(int)floorf( font.limitminy * state->size );
1399 int getFontLineHeight( )
1404 state = &states[ nstates - 1 ];
1405 if( !( state->font ) )
1408 return (int)ceilf( font.lineHeight * state->size );
1414 // Pull texture changes
1415 const byte *getTextureData( int *width, int *height )
1418 *width = this.width;
1420 *height = this.height;
1424 // Retrieve the dirty rectangle, telling fontmanager you will manually perform the update, returns 0 if no part of the texture requires an update
1425 bool validateTexture( int *retdirtyrect )
1427 if( ( dirtyrect[0] < dirtyrect[2] ) && ( dirtyrect[1] < dirtyrect[3] ) )
1429 retdirtyrect[0] = dirtyrect[0];
1430 retdirtyrect[1] = dirtyrect[1];
1431 retdirtyrect[2] = dirtyrect[2];
1432 retdirtyrect[3] = dirtyrect[3];
1434 dirtyrect[0] = width;
1435 dirtyrect[1] = height;
1443 // Returns current atlas size
1444 void getAtlasSize( int *retwidth, int *retheight )
1446 *retwidth = this.width;
1447 *retheight = this.height;
1451 // Expands the atlas size
1452 bool expandAtlas( int width, int height )
1454 width = Max( width, this.width );
1455 height = Max( height, this.height );
1457 if( ( width == this.width ) && ( height == this.height ) )
1460 // Flush all pending glyphs
1463 // Create new texture
1464 if( renderer.resizeTexture( width, height ) )
1469 // Copy old texture data over.
1470 if( !( data = new byte[width * bytesperline] ) )
1472 for( i = 0 ; i < this.height ; i++ )
1474 byte * dst = &data[ (i * width) * bytesperpixel ];
1475 byte * src = &this.texdata[ i * this.bytesperline ];
1476 memcpy( dst, src, bytesperline);
1477 if( width > this.width )
1478 memset( dst+bytesperline, 0, (width - this.width) * bytesperpixel );
1480 if( height > this.height )
1481 memset( &data[ width * this.height * bytesperpixel], 0, ( height - this.height ) *bytesperline );
1483 delete this.texdata;
1486 // Increase atlas size
1487 atlas.expand( width, height );
1489 // Add existing data as dirty.
1492 dirtyrect[2] = this.width;
1493 dirtyrect[3] = atlas.getAtlasMaxHeight();
1496 this.height = height;
1497 this.bytesperline = this.width * bytesperpixel;
1498 widthinv = 1.0f / this.width;
1499 heightinv = 1.0f / this.height;
1506 // Reset the whole fm
1507 bool resetAtlas( int width, int height )
1509 // Flush all pending glyphs
1512 // Create new texture
1513 if(renderer.resizeTexture( width, height ) )
1516 atlas.reset( width, height );
1518 // Clear texture data.
1519 texdata = renew texdata byte[width * height * bytesperpixel];
1520 if(!texdata) return 0;
1521 memset( this.texdata, 0, width * height );
1524 dirtyrect[0] = width;
1525 dirtyrect[1] = height;
1529 // Reset cached glyphs
1530 for(font : fontList)
1533 font.glyphcount = 0;
1534 for( i = 0 ; i < FM_HASH_TABLE_SIZE ; i++ )
1535 font.hashtable[i] = -1;
1537 renderer.resetImages( );
1540 this.height = height;
1541 this.bytesperline = width * bytesperpixel;
1542 this.widthinv = 1.0f / this.width;
1543 this.heightinv = 1.0f / this.height;
1551 bool initPathDraw( FMPathDraw pathdraw )
1553 FMState *state = &states[ nstates - 1 ];
1554 FMFont font = state->font;
1557 pathdraw.prevGlyphIndex = -1;
1558 pathdraw.middleAlign = font.middleAlign * (float)state->size;
1564 float pathDrawCharacter( FMPathDraw pathdraw, float x, float y, float vectorx, float vectory, int unicode )
1567 FMState *state = &states[ nstates - 1 ];
1568 FMFont font = state->font;
1569 FMGlyph *glyph = getGlyph(font, unicode, state->size, 0, false );
1572 subpixel = font.ftFont.getGlyphKernAdvance( pathdraw.prevGlyphIndex, glyph->glyphindex );
1573 #if !defined(SHADERS)
1574 if(font.processImage)
1576 FMGlyph *outlineGlyph = getGlyph(font, unicode, state->size, 0, true );
1578 drawTextGlyphFloat(font, outlineGlyph, x, y, vectorx, vectory, (float)subpixel * (1.0f/64.0f), pathdraw.middleAlign, true );
1581 drawTextGlyphFloat(font, glyph, x, y, vectorx, vectory, (float)subpixel * (1.0f/64.0f), pathdraw.middleAlign, false );
1583 subpixel += glyph->advance;
1584 pathdraw.prevGlyphIndex = glyph->glyphindex;
1589 pathdraw.prevGlyphIndex = -1;
1592 return (float)subpixel * (1.0f/64.0f);
1595 float pathDrawPredictAdvance( FMPathDraw pathdraw, unichar unicode )
1598 FMState *state = &states[ nstates - 1 ];
1599 FMFont font = state->font;
1600 FMGlyph *glyph = getGlyph(font, unicode, state->size, 0, false );
1603 subpixel = font.ftFont.getGlyphKernAdvance( pathdraw.prevGlyphIndex, glyph->glyphindex );
1604 subpixel += glyph->advance;
1606 return (float)subpixel * (1.0f/64.0f);