1 /* *****************************************************************************
2 * Original Version Copyright (c) 2007-2014 Alexis Naveros.
4 * Ecere Corporation has unlimited/unrestricted rights.
5 * *****************************************************************************/
14 #include "glHelpers.h"
22 static inline uint32 decodeUTF8( uint32 b, uint32 *state, unichar *retCodePoint ) { return ccUtf8ToUnicode(b, state, (uint *)retCodePoint); }
26 static void buildCursorGlyph( byte *dst, int glyphwidth, int glyphheight, int dststride )
28 int x, y, hbarheight, hgap, hline, vline;
33 hbarheight = 1 + ( glyphheight >> 6 );
34 hgap = ( glyphwidth - ( 1 + ( glyphheight >> 6 ) ) ) >> 1;
35 hline = hgap + glyphwidth - ( hgap << 1 );
36 vline = glyphheight - ( hbarheight << 1 );
37 for( y = 0 ; y < hbarheight ; y++ )
40 for( x = 0 ; x < glyphwidth ; x++ )
44 for( y = 0 ; y < vline ; y++ )
47 for( x = 0 ; x < hgap ; x++ )
49 for( ; x < hline ; x++ )
51 for( ; x < glyphwidth ; x++ )
55 for( y = 0 ; y < hbarheight ; y++ )
58 for( x = 0 ; x < glyphwidth ; x++ )
65 for( y = 0 ; y < glyphheight ; y++ )
68 for( x = 0 ; x < glyphwidth ; x++ )
77 static inline void addGlyphAdvance( int *x, int *subpixel, FMGlyph *glyph )
79 *subpixel += glyph->advance;
80 #if FM_SUBPIXEL_ROUNDING_RANGE
81 *subpixel = ( *subpixel + (FM_SUBPIXEL_ROUNDING_RANGE>>1) ) & ~(FM_SUBPIXEL_ROUNDING_RANGE-1);
87 public class FontManagerRenderer
92 virtual bool init(int channelCount);
94 // Create, resize or update texture data ; rect is [minx,maxx,miny,maxy]
95 virtual int createTexture( int width, int height );
96 virtual int resizeTexture( int width, int height );
97 virtual void updateTexture( int *rect, const byte *data );
99 // If the renderer does any buffering of drawImage(), flush it all right now (texture may change immediately after)
100 virtual void flush( );
102 // If the renderer records per-image data, return an imageIndex passed to drawImage()
103 virtual int registerImage( int offsetx, int offsety, int width, int height );
104 // Draw an image, imageIndex passed as the value previously returned by registerImage()
105 virtual void drawImage( int targetx, int targety, int imageIndex, bool useExtColor );
106 // Draw an image, called instead of drawImage() for text cursors, can point to exactly the same function
107 virtual void drawImageCursor( int targetx, int targety, int imageIndex );
108 // If drawImage is zero, then this alternate function is called, passing everything required to render the glyph
109 virtual void drawImageAlt( byte *texdata, int targetx, int targety, int offsetx, int offsety, int width, int height );
110 // Draw a non-aligned image, imageIndex passed as the value previously returned by registerImage()
111 virtual void drawImageFloat( float targetx, float targety, float angsin, float angcos, int imageIndex, bool useExtColor );
113 // The renderer must flush all recorded images, registerImage() will be called for new images
114 virtual void resetImages( );
116 virtual void setLayer( uint32 layerIndex );
123 #include <ft2build.h>
125 #include FT_FREETYPE_H
126 #include FT_ADVANCES_H
132 #define FM_SUPPORT_GLYPH_ROTATION (1)
138 #define FM_ENABLE_HINTING (1)
139 #define FM_SUBPIXEL_ROUNDING_RANGE (16)
142 #define FM_HASH_TABLE_SIZE (4096)
143 #define FM_INIT_GLYPHS (1024)
144 #define FM_INIT_ATLAS_NODES (512)
146 #define FM_MAX_STATES (16)
151 struct FMFreeTypeFont
155 static inline int ::init()
158 ftError = FT_Init_FreeType( &ftLibrary2 );
162 static inline int loadFont( byte *data, int dataSize )
164 FT_Error ftError = FT_New_Memory_Face( ftLibrary2, (const FT_Byte*)data, dataSize, 0, &face );
170 FT_Done_Face( face );
173 static inline void getFontVMetrics( float *ascent, float *descent, float *lineHeight, float *limitminy, float *limitmaxy )
175 *ascent = (float)face->ascender;
176 *descent = (float)face->descender;
177 *lineHeight = (float)face->height / (float)face->units_per_EM;
178 *limitminy = (float)face->bbox.yMin / (float)face->units_per_EM;
179 *limitmaxy = (float)face->bbox.yMax / (float)face->units_per_EM;
182 static inline int getGlyphIndex( unichar codepoint )
184 return FT_Get_Char_Index( this.face, codepoint );
187 static int buildGlyphBitmap( int glyph, int size, int subpixel, int *advance, int *x0, int *y0, int *x1, int *y1 )
190 FT_GlyphSlot glyphslot;
193 ftError = FT_Set_Pixel_Sizes( this.face, 0, (FT_UInt)size );
197 subvector.x = subpixel;
199 FT_Set_Transform( face, 0, &subvector );
201 #if FM_ENABLE_HINTING
202 ftError = FT_Load_Glyph( face, glyph, FT_LOAD_RENDER );
204 ftError = FT_Load_Glyph( face, glyph, FT_LOAD_RENDER | FT_LOAD_NO_HINTING );
209 glyphslot = face->glyph;
210 *advance = (int)glyphslot->metrics.horiAdvance;
211 *x0 = glyphslot->bitmap_left;
212 *x1 = glyphslot->bitmap_left + glyphslot->bitmap.width;
213 *y0 = -glyphslot->bitmap_top;
214 *y1 = -glyphslot->bitmap_top + glyphslot->bitmap.rows;
219 static inline int getGlyphKernAdvance( int glyph1, int glyph2 )
222 FT_Get_Kerning( face, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning );
223 return (int)ftKerning.x;
226 static inline byte *getGlyphBitmap( int glyphindex )
228 FT_GlyphSlot glyphslot;
229 glyphslot = face->glyph;
230 return glyphslot->bitmap.buffer;
234 class FMDefBits : uint64
238 int size:11, subPixel:6;
242 static define FM_SIZE_MAX = (1<<11)-1;
243 static define FM_BLUR_RADIUS_MAX = (1<<6)-1;
244 static define FM_BLUR_SCALE_MAX = (1<<6)-1;
246 static define FM_GLYPH_CODEPOINT_CURSOR = 0x1;
247 static define FM_GLYPH_CODEPOINT_REPLACEMENT = 0xfffd;
249 public enum FMVerticalAlignment { baseline, top, middle, bottom };
251 public class FMTextAlignment : uint16
254 Alignment horzAlignment:2;
255 FMVerticalAlignment vertAlignment:2;
258 public struct FMPathDraw
264 struct FMQuad { int x0, y0, x1, y1; };
270 short x0, y0, x1, y1;
271 short advance, offsetx, offsety;
275 static void getQuad( float x, float y, FMQuad q )
277 int rx = (int)(x + offsetx);
278 int ry = (int)(y + offsety);
279 q = { rx, ry, rx + x1 - x0, ry + y1 - y0 };
283 public class FMFont : struct
285 public LinkElement link;
286 FMFreeTypeFont ftFont;
292 float limitminy, limitmaxy;
296 int hashtable[FM_HASH_TABLE_SIZE];
297 int glyphPaddingWidth;
299 void (*processImage)( void *opaquecontext, byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingwidth, int pass);
300 void *processImageContext;
303 public void setFontImageProcessing(
304 void (*processImage)( byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingsize, void *opaquecontext ),
305 void *opaquecontext )
307 this.processImage = processImage;
308 this.processImageContext = opaquecontext;
313 float outlineAlphaFactor;
314 float outlineIntensityFactor;
316 static void ::outlineProcessGlyphImage( FMFont font, byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingwidth, int isOutline )
319 byte *src, *dst, *dstrow;
320 float intensityfactor, alphafactor, range, alpha, intensity, rangeinv, rangebase;
321 float *distancemap, *dmap;
323 distancemap = new float[width * height];
326 imgDistMapBuild( distancemap, src, width, height, bytesperpixel, bytesperline );
328 alphafactor = font.outlineAlphaFactor; //2.0f;
329 intensityfactor = font.outlineIntensityFactor; // 0.2f;
330 range = (float)font.outlineRadius;
331 rangeinv = 1.0f / range;
335 for( y = 0 ; y < height ; y++ )
338 for( x = 0 ; x < width ; x++ )
340 rangebase = ( range - dmap[ x ] ) * rangeinv;
341 alpha = alphafactor * rangebase;
342 intensity = fmaxf( (float)dstrow[0] * (1.0f/255.0f), intensityfactor * rangebase );
343 if(bytesperpixel == 2)
346 dstrow[0] = (byte)roundf( fmaxf( 0.0f, fminf( 255.0f, alpha * 255.0f ) ) );
347 /* Intensity channel */
348 dstrow[1] = (byte)roundf( fmaxf( 0.0f, fminf( 255.0f, intensity * 255.0f ) ) );
353 dstrow[0] = (byte)roundf( fmaxf( 0.0f, fminf( 255.0f, alpha * 255.0f ) ) );
355 dstrow[0] = (byte)roundf( fmaxf( 0.0f, fminf( 255.0f, intensity * 255.0f ) ) );
357 dstrow += bytesperpixel;
365 public void setOutline(float size, float fade)
367 outlineIntensityFactor = 1.0f / (0.2f + size);
368 outlineAlphaFactor = 1.0f / (0.2f + fade);
369 outlineRadius = size;
370 processImage = outlineProcessGlyphImage;
371 processImageContext = this;
381 static FMGlyph *allocGlyph( )
383 if( glyphcount >= glyphalloc )
386 if( !(glyphs = renew glyphs FMGlyph[glyphalloc]) )
389 return &glyphs[ glyphcount++ ];
394 static float getVertAlign( FMTextAlignment align, int size )
397 if( align.vertAlignment == top )
398 return ascender * sizef;
399 else if( align.vertAlignment == middle )
400 return middleAlign * sizef;
401 else if( align.vertAlignment == bottom )
402 return descender * sizef;
406 static inline void addKerning( int prevGlyphIndex, FMGlyph *glyph, int *x, int *subpixel )
408 if( prevGlyphIndex != -1 )
410 *subpixel += ftFont.getGlyphKernAdvance( prevGlyphIndex, glyph->glyphindex );
411 #if FM_SUBPIXEL_ROUNDING_RANGE
412 *subpixel = ( *subpixel + (FM_SUBPIXEL_ROUNDING_RANGE>>1) ) & ~(FM_SUBPIXEL_ROUNDING_RANGE-1);
414 *x += *subpixel >> 6;
425 FMTextAlignment align;
431 static FT_Library ftLibrary2;
433 static void copyGlyphBitmap1( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride )
436 for( y = 0 ; y < glyphheight ; y++ )
438 byte *dstrow = &dst[ y * dststride ];
439 for( x = 0 ; x < glyphwidth ; x++ )
440 dstrow[ x ] = src[ x ];
445 static void copyGlyphBitmap2( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride )
448 for( y = 0 ; y < glyphheight ; y++ )
450 byte *dstrow = &dst[ y * dststride ];
451 for( x = 0 ; x < glyphwidth ; x++ )
452 dstrow[ x << 1 ] = src[ x ];
457 static void copyGlyphBitmap4( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride )
460 for( y = 0 ; y < glyphheight ; y++ )
462 byte *dstrow = &dst[ y * dststride ];
463 for( x = 0 ; x < glyphwidth ; x++ )
464 dstrow[ x << 2 ] = src[ x ];
469 public class FontManager
471 FontManagerRenderer renderer;
473 float widthinv, heightinv;
478 AtlasBuilder atlas { };
482 LinkList<FMFont, link = link> fontList { };
484 FMState states[FM_MAX_STATES];
487 void (*copyGlyphBitmap)( byte *dst, byte *src, int glyphwidth, int glyphheight, int dststride );
490 static FMGlyph *getGlyph( FMFont font, unichar codepoint, int size, int subpixel, bool outlinePass )
492 int i, glyphindex, advance, x0, y0, x1, y1, gx, gy;
493 int glyphwidth, glyphheight, glyphareawidth, glyphareaheight;
504 padding = font.glyphPaddingWidth;
506 // Find code point and size.
507 glyphdef = FMDefBits { codepoint, size, subpixel, outlinePass };
508 hashindex = ccHash32Int64Inline( glyphdef ) & ( FM_HASH_TABLE_SIZE - 1 );
509 i = font.hashtable[hashindex];
512 if( glyphdef == font.glyphs[i].glyphdef )
513 return &font.glyphs[i];
514 i = font.glyphs[i].listnext;
517 /* Could not find glyph, create it */
518 if( codepoint == FM_GLYPH_CODEPOINT_CURSOR )
529 y0 = -(int)ceilf( font.limitmaxy * (float)size );
530 y1 = -(int)floorf( font.limitminy * (float)size );
531 i = ( (y1 - y0) - size ) / 3;
537 glyphindex = font.ftFont.getGlyphIndex( codepoint );
538 if( !( font.ftFont.buildGlyphBitmap( glyphindex, size, subpixel, &advance, &x0, &y0, &x1, &y1 ) ) )
540 if( codepoint != FM_GLYPH_CODEPOINT_REPLACEMENT )
541 return getGlyph(font, FM_GLYPH_CODEPOINT_REPLACEMENT, size, subpixel, outlinePass );
545 glyphwidth = ( x1 - x0 );
546 glyphheight = ( y1 - y0 );
547 glyphareawidth = glyphwidth + (padding << 1);
548 glyphareaheight = glyphheight + (padding << 1);
550 // Find free spot for the rect in the atlas
551 added = atlas.addRect( glyphareawidth, glyphareaheight, &gx, &gy );
552 if( !( added ) && ( onAtlasFull ) )
554 /* Atlas is full, let the user to resize the atlas (or not), and try again. */
556 added = atlas.addRect( glyphareawidth, glyphareaheight, &gx, &gy );
561 /* Build the glyph */
562 glyph = font.allocGlyph();
563 glyph->glyphdef = glyphdef;
564 glyph->glyphindex = glyphindex;
565 glyph->x0 = (short)gx;
566 glyph->y0 = (short)gy;
567 glyph->x1 = (short)( glyph->x0 + glyphareawidth );
568 glyph->y1 = (short)( glyph->y0 + glyphareaheight );
569 glyph->advance = (short)advance;
570 glyph->offsetx = (short)( x0 - padding );
571 glyph->offsety = (short)( y0 - padding );
573 glyph->imageIndex = -1;
574 if( renderer.registerImage )
576 renderer.setLayer(outlinePass ? 3 : 6);
577 glyph->imageIndex = renderer.registerImage( gx, gy, glyphareawidth, glyphareaheight);
580 // Add char to hash table
581 glyph->listnext = font.hashtable[hashindex];
582 font.hashtable[hashindex] = font.glyphcount - 1;
584 // Clear glyph image area (TODO: wasteful when single channel without prepare callback?)
585 dst = &texdata[ ( glyph->x0 * bytesperpixel ) + ( glyph->y0 * bytesperline ) ];
586 for( i = 0 ; i < glyphareaheight ; i++, dst += bytesperline )
587 memset( dst, 0, glyphareawidth * bytesperpixel );
590 dst = &texdata[ ( glyph->x0 + padding ) * bytesperpixel + ( ( glyph->y0 + padding ) * bytesperline ) + channelindex];
591 if( codepoint == FM_GLYPH_CODEPOINT_CURSOR )
593 src = new byte[ glyphwidth * glyphheight ];
594 buildCursorGlyph( src, glyphwidth, glyphheight, glyphwidth );
595 copyGlyphBitmap( dst, src, glyphwidth, glyphheight, bytesperline );
600 src = font.ftFont.getGlyphBitmap(glyphindex);
601 copyGlyphBitmap( dst, src, glyphwidth, glyphheight, bytesperline );
604 // User custom font image processing
605 if(font.processImage)
607 dst = &texdata[ ( glyph->x0 * bytesperpixel ) + ( glyph->y0 * bytesperline ) ];
608 font.processImage( font.processImageContext, dst, glyphareawidth, glyphareaheight, bytesperpixel, bytesperline, font.glyphPaddingWidth, outlinePass );
611 dirtyrect[0] = Min( dirtyrect[0], glyph->x0 );
612 dirtyrect[1] = Min( dirtyrect[1], glyph->y0 );
613 dirtyrect[2] = Max( dirtyrect[2], glyph->x1 );
614 dirtyrect[3] = Max( dirtyrect[3], glyph->y1 );
619 static inline void drawTextGlyph( FMFont font, FMGlyph *glyph, int x, int y, bool useExtColor )
621 int ptx = x + glyph->offsetx;
622 int pty = y + glyph->offsety;
623 if( renderer.drawImage )
624 renderer.drawImage( ptx, pty, glyph->imageIndex, useExtColor );
625 else if( renderer.drawImageAlt )
626 renderer.drawImageAlt( texdata, ptx, pty, glyph->x0, glyph->y0, glyph->x1 - glyph->x0, glyph->y1 - glyph->y0 );
629 static inline void drawTextCursorGlyph( FMFont font, FMGlyph *glyph, int x, int y )
632 ptx = x + glyph->offsetx;
633 pty = y + glyph->offsety;
634 if( renderer.drawImageCursor )
635 renderer.drawImageCursor( ptx, pty, glyph->imageIndex );
636 else if( renderer.drawImageAlt )
637 renderer.drawImageAlt( texdata, ptx, pty, glyph->x0, glyph->y0, glyph->x1 - glyph->x0, glyph->y1 - glyph->y0 );
640 static inline void drawTextGlyphFloat( FMFont font, FMGlyph *glyph, float x, float y, float vectorx, float vectory, float offsetx, float offsety, bool useExtColor )
642 float vectx = (float)glyph->offsetx + offsetx;
643 float vecty = (float)glyph->offsety + offsety;
644 float ptx = x + ( vectorx * vectx ) - ( vectory * vecty );
645 float pty = y + ( vectorx * vecty ) + ( vectory * vectx );
646 renderer.drawImageFloat( ptx, pty, vectory, vectorx, glyph->imageIndex, useExtColor );
651 property FontManagerRenderer renderer { set { renderer = value; } get { return renderer; } }
653 // When notified of a full atlas, you should call fmExpandAtlas() or fmResetAtlas() within the callback
654 virtual void onAtlasFull()
657 atlas.reset(atlas.width, atlas.height);
660 // Create and destroy font manager
661 bool create( int width, int height, int channelCount, int channelIndex, FontManagerRenderer renderer)
665 if( ( channelCount != 1 ) && ( channelCount != 2 ) && ( channelCount != 4 ) )
667 if( ( width <= 0 ) || ( height <= 0 ) )
670 this.renderer = renderer;
672 renderer.init(channelCount);
674 // Initialize implementation library
675 if(FMFreeTypeFont::init() )
678 this.height = height;
679 bytesperpixel = channelCount;
680 bytesperline = width * bytesperpixel;
681 this.channelindex = channelIndex;
682 if(renderer.createTexture( width, height ))
684 if((atlas.create( this.width, this.height, FM_INIT_ATLAS_NODES )))
686 // Create texture for the cache.
687 widthinv = 1.0f / width;
688 heightinv = 1.0f / height;
689 texdata = new byte[width * height * bytesperpixel];
692 memset( texdata, 0, height * bytesperline );
694 dirtyrect[0] = this.width;
695 dirtyrect[1] = this.height;
699 if( bytesperpixel == 1 )
700 copyGlyphBitmap = copyGlyphBitmap1;
701 else if( bytesperpixel == 2 )
702 copyGlyphBitmap = copyGlyphBitmap2;
704 copyGlyphBitmap = copyGlyphBitmap4;
731 void setState( FMFont font, int size, int align)
734 if( size >= FM_SIZE_MAX )
736 state = &states[ nstates - 1 ];
738 state->size = (uint16)size;
739 state->align = (uint16)align;
742 void setFont( FMFont font )
744 states[ nstates - 1 ].font = font;
747 void setSize( int size )
749 if( size >= FM_SIZE_MAX )
752 states[ nstates - 1 ].size = (uint16)size;
755 void setAlign( int align )
757 states[ nstates - 1 ].align = (uint16)align;
760 // Set image manipuation callback
761 void setFontImageProcessing( FMFont font, void (*processImage)( byte *image, int width, int height, int bytesperpixel, int bytesperline, int paddingwidth, void *opaquecontext ), void *opaquecontext )
769 if(nstates < FM_MAX_STATES)
772 memcpy( &states[nstates], &states[nstates-1], sizeof(FMState) );
785 FMState *state = &states[ nstates - 1 ];
788 state->align = { left, baseline };
794 static void freeFont(FMFont font)
798 fontList.Remove((IteratorPointer)font);
803 // Add font from FontResource
804 FMFont getFont(FontResource fontResource)
808 Array<FaceInfo> infos = ResolveFont(fontResource.faceName, fontResource.size, fontResource.flags);
813 font = addFont(i.fileName, Max(2, (int)(1+fontResource.outlineSize)));
816 font.setOutline(fontResource.outlineSize, fontResource.outlineFade);
826 // Add font from file
827 FMFont addFont(const String path, int glyphPaddingWidth )
830 File f = FileOpen(path, read);
833 // Read in the font data
834 int dataSize = f.GetSize();
835 byte *data = new byte[dataSize];
838 f.Read(data, 1, dataSize);
839 font = addFontData(data, dataSize, glyphPaddingWidth);
848 // Add font from data ; do not free( data ), the font manager will do that when removing the font
849 FMFont addFontData( byte *data, int dataSize, int glyphPaddingWidth )
853 glyphs = new FMGlyph[FM_INIT_GLYPHS];
854 glyphalloc = FM_INIT_GLYPHS;
855 glyphPaddingWidth = glyphPaddingWidth;
860 float ascent, descent, fontHeight;
865 for( i = 0 ; i < FM_HASH_TABLE_SIZE ; i++ )
866 font.hashtable[i] = -1;
869 if(!font.ftFont.loadFont(data, dataSize))
875 // Store normalized line height. The real line height is got by multiplying by font size.
876 font.ftFont.getFontVMetrics( &ascent, &descent, &font.lineHeight, &font.limitminy, &font.limitmaxy );
877 fontHeight = ascent - descent;
878 font.ascender = ascent / fontHeight;
879 font.descender = descent / fontHeight;
880 font.middleAlign = 0.5f * ( font.ascender + font.descender );
881 font.fontdata = data;
887 void removeFont( FMFont font )
893 int drawText( int x, int y, const char *string, int stringlength )
903 state = &states[ nstates - 1 ];
907 if( !( state->font ) )
910 if( !( stringlength ) )
911 stringlength = strlen( string );
913 // Align horizontally
914 if( state->align.horzAlignment == right )
915 x -= getTextWidth(string, stringlength );
916 else if( state->align.horzAlignment == center )
917 x -= getTextWidth(string, stringlength ) >> 1;
920 y += roundf( font.getVertAlign(state->align, state->size ) );
923 for( index = 0 ; index < stringlength ; index++ )
925 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
927 glyph = getGlyph(font, codepoint, state->size, subpixel, false );
930 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
931 if(font.processImage)
933 FMGlyph *outlineGlyph = getGlyph(font, codepoint, state->size, subpixel, true );
935 drawTextGlyph(font, outlineGlyph, x, y, true );
937 drawTextGlyph(font, glyph, x, y, false );
938 addGlyphAdvance( &x, &subpixel, glyph );
940 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
943 return x + ( subpixel >= 32 );
946 int drawTextWithCursor( int x, int y, const char *string, int stringlength, int cursoroffset )
956 state = &states[ nstates - 1 ];
960 if( !( state->font ) )
963 if( !( stringlength ) )
964 stringlength = strlen( string );
966 // Align horizontally
967 if( state->align.horzAlignment == right )
968 x -= getTextWidth(string, stringlength );
969 else if( state->align.horzAlignment == center )
970 x -= getTextWidth(string, stringlength ) >> 1;
973 y += roundf( font.getVertAlign(state->align, state->size ) );
976 for( index = 0 ; ; index++ )
978 if( index == cursoroffset )
980 glyph = getGlyph(font, FM_GLYPH_CODEPOINT_CURSOR, state->size, subpixel, false );
982 drawTextCursorGlyph(font, glyph, x, y );
984 if( index >= stringlength )
986 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
988 glyph = getGlyph(font, codepoint, state->size, subpixel, false );
991 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
992 if(font.processImage)
994 FMGlyph *outlineGlyph = getGlyph(font, codepoint, state->size, subpixel, true );
996 drawTextGlyph(font, outlineGlyph, x, y, true );
998 drawTextGlyph(font, glyph, x, y, false );
999 addGlyphAdvance( &x, &subpixel, glyph );
1001 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1004 return x + ( subpixel >= 32 );
1007 int drawTextTruncate( int x, int y, int truncatewidth, const char *string, int stringlength, char *extstring, int extwidth )
1009 int subpixel, index, textwidth, truncatepoint;
1017 state = &states[ nstates - 1 ];
1020 prevGlyphIndex = -1;
1021 if( !( state->font ) )
1024 if( !( stringlength ) )
1025 stringlength = strlen( string );
1026 textwidth = getTextWidthTruncate(string, stringlength, truncatewidth );
1028 truncatepoint = x + truncatewidth;
1029 if( textwidth <= truncatewidth )
1033 if( extwidth >= truncatewidth )
1035 truncatepoint -= extwidth;
1038 // Align horizontally
1039 if( state->align.horzAlignment == right )
1041 else if( state->align.horzAlignment == center )
1042 x -= textwidth >> 1;
1045 y += roundf( font.getVertAlign(state->align, state->size ) );
1048 for( index = 0 ; index < stringlength ; index++ )
1050 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1052 glyph = getGlyph(font, codepoint, state->size, subpixel, false );
1055 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1056 if(font.processImage)
1058 FMGlyph *outlineGlyph = getGlyph(font, codepoint, state->size, subpixel, true );
1060 drawTextGlyph(font, outlineGlyph, x, y, true );
1062 drawTextGlyph(font, glyph, x, y, false );
1063 addGlyphAdvance( &x, &subpixel, glyph );
1064 if( x > truncatepoint )
1067 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1069 x += ( subpixel >= 32 );
1071 drawText(x, y, extstring, 0 );
1081 int getTextWidth( const char *string, int stringlength )
1083 return getTextWidthTruncate(string, stringlength, ( (unsigned)1 << ( ( sizeof(int) * CHAR_BIT ) - 1 ) ) - 1 );
1087 int getTextWidthTruncate( const char *string, int stringlength, int truncatewidth )
1089 int subpixel, index, advance;
1097 state = &states[ nstates - 1 ];
1100 prevGlyphIndex = -1;
1101 if( !( state->font ) )
1104 if( !( stringlength ) )
1105 stringlength = strlen( string );
1109 for( index = 0 ; index < stringlength ; index++ )
1111 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1113 glyph = getGlyph(font, codepoint, state->size, subpixel, false );
1116 font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1117 addGlyphAdvance( &advance, &subpixel, glyph );
1118 if( advance > truncatewidth )
1121 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1124 return advance + ( subpixel >= 32 );
1127 int getTextBounds( int x, int y, const char *string, int stringlength, int *bounds )
1129 int subpixel, index;
1137 int startx, advance;
1138 int minx, miny, maxx, maxy;
1140 state = &states[ nstates - 1 ];
1143 prevGlyphIndex = -1;
1144 if( !( state->font ) )
1147 if( !( stringlength ) )
1148 stringlength = strlen( string );
1151 y += font.getVertAlign(state->align, state->size );
1158 for( index = 0 ; index < stringlength ; index++ )
1160 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1162 glyph = getGlyph(font, codepoint, state->size, subpixel, false );
1165 font.addKerning(prevGlyphIndex, glyph, &x, &subpixel );
1166 glyph->getQuad(x, y, q);
1175 addGlyphAdvance( &x, &subpixel, glyph );
1177 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1180 advance = x - startx;
1182 /* Align horizontally */
1183 if( state->align.horzAlignment == right )
1188 else if( state->align.horzAlignment == center )
1190 minx -= advance * 0.5f;
1191 maxx -= advance * 0.5f;
1202 return advance + ( subpixel >= 32 );
1205 // Find text offset up to truncatewidth
1206 int getTextTruncateOffset( int truncatewidth, const char *string, int stringlength, int extwidth, int *retextflag, int *retfullwidth )
1208 int subpixel, index, advance, truncatewidthext, truncateindex, extflag, fullwidth = 0;
1216 state = &states[ nstates - 1 ];
1221 if( extwidth >= truncatewidth )
1225 prevGlyphIndex = -1;
1226 if( !( state->font ) )
1229 if( stringlength <= 0 )
1230 stringlength = strlen( string );
1232 truncatewidthext = truncatewidth - extwidth;
1238 for( index = 0 ; ; index++ )
1240 if( index >= stringlength )
1242 truncateindex = index;
1243 fullwidth = advance + ( subpixel >= 32 );
1246 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1248 glyph = getGlyph(font, codepoint, state->size, subpixel, false );
1251 font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1252 addGlyphAdvance( &advance, &subpixel, glyph );
1253 if( advance > truncatewidth )
1258 if( advance <= truncatewidthext )
1260 truncateindex = index + 1;
1261 fullwidth = advance + ( subpixel >= 32 );
1264 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1270 *retfullwidth = fullwidth;
1272 *retfullwidth = fullwidth;
1275 *retextflag = extflag;
1277 return truncateindex;
1280 // Find text offset nearest to the given width
1281 int getTextNearestOffset( int targetwidth, const char *string, int stringlength )
1283 int subpixel, index, advance, truncateindex, distance, bestdistance;
1291 state = &states[ nstates - 1 ];
1294 prevGlyphIndex = -1;
1295 if( !( state->font ) )
1298 if( stringlength <= 0 )
1299 stringlength = strlen( string );
1304 bestdistance = abs( targetwidth );
1305 for( index = 0 ; index < stringlength ; index++ )
1307 if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) )
1309 glyph = getGlyph(font, codepoint, state->size, subpixel, false );
1312 font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel );
1313 addGlyphAdvance( &advance, &subpixel, glyph );
1314 distance = abs( targetwidth - ( advance + ( subpixel >= 32 ) ) );
1315 if( distance > bestdistance )
1317 bestdistance = distance;
1318 truncateindex = index + 1;
1320 prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 );
1323 return truncateindex;
1328 static void flush( bool rendererFlush )
1330 // Flush texture updates
1331 if( ( dirtyrect[0] < dirtyrect[2] ) && ( dirtyrect[1] < dirtyrect[3] ) )
1333 renderer.updateTexture( dirtyrect, texdata );
1335 dirtyrect[0] = width;
1336 dirtyrect[1] = height;
1340 // Flush buffered glyphs
1341 if( rendererFlush ) renderer.flush( );
1344 // Flush buffered texture updates, renderer->updateTexture()
1354 void getFontExtent( int *retascent, int *retdescent )
1357 FMState *state = &states[ nstates - 1 ];
1363 *retascent = -(int)ceilf( font.ascender * (float)state->size );
1365 *retdescent = -(int)floorf( font.descender * (float)state->size );
1369 void getFontLimits( int *retlimitminy, int *retlimitmaxy )
1374 state = &states[ nstates - 1 ];
1375 if( !( state->font ) )
1380 *retlimitminy = -(int)ceilf( font.limitmaxy * state->size );
1382 *retlimitmaxy = -(int)floorf( font.limitminy * state->size );
1387 int getFontLineHeight( )
1392 state = &states[ nstates - 1 ];
1393 if( !( state->font ) )
1396 return (int)ceilf( font.lineHeight * state->size );
1402 // Pull texture changes
1403 const byte *getTextureData( int *width, int *height )
1406 *width = this.width;
1408 *height = this.height;
1412 // Retrieve the dirty rectangle, telling fontmanager you will manually perform the update, returns 0 if no part of the texture requires an update
1413 bool validateTexture( int *retdirtyrect )
1415 if( ( dirtyrect[0] < dirtyrect[2] ) && ( dirtyrect[1] < dirtyrect[3] ) )
1417 retdirtyrect[0] = dirtyrect[0];
1418 retdirtyrect[1] = dirtyrect[1];
1419 retdirtyrect[2] = dirtyrect[2];
1420 retdirtyrect[3] = dirtyrect[3];
1422 dirtyrect[0] = width;
1423 dirtyrect[1] = height;
1431 // Returns current atlas size
1432 void getAtlasSize( int *retwidth, int *retheight )
1434 *retwidth = this.width;
1435 *retheight = this.height;
1439 // Expands the atlas size
1440 bool expandAtlas( int width, int height )
1442 width = Max( width, this.width );
1443 height = Max( height, this.height );
1445 if( ( width == this.width ) && ( height == this.height ) )
1448 // Flush all pending glyphs
1451 // Create new texture
1452 if( renderer.resizeTexture( width, height ) )
1457 // Copy old texture data over.
1458 if( !( data = new byte[width * bytesperline] ) )
1460 for( i = 0 ; i < this.height ; i++ )
1462 byte * dst = &data[ (i * width) * bytesperpixel ];
1463 byte * src = &this.texdata[ i * this.bytesperline ];
1464 memcpy( dst, src, bytesperline);
1465 if( width > this.width )
1466 memset( dst+bytesperline, 0, (width - this.width) * bytesperpixel );
1468 if( height > this.height )
1469 memset( &data[ width * this.height * bytesperpixel], 0, ( height - this.height ) *bytesperline );
1471 delete this.texdata;
1474 // Increase atlas size
1475 atlas.expand( width, height );
1477 // Add existing data as dirty.
1480 dirtyrect[2] = this.width;
1481 dirtyrect[3] = atlas.getAtlasMaxHeight();
1484 this.height = height;
1485 this.bytesperline = this.width * bytesperpixel;
1486 widthinv = 1.0f / this.width;
1487 heightinv = 1.0f / this.height;
1494 // Reset the whole fm
1495 bool resetAtlas( int width, int height )
1497 // Flush all pending glyphs
1500 // Create new texture
1501 if(renderer.resizeTexture( width, height ) )
1504 atlas.reset( width, height );
1506 // Clear texture data.
1507 texdata = renew texdata byte[width * height * bytesperpixel];
1508 if(!texdata) return 0;
1509 memset( this.texdata, 0, width * height );
1512 dirtyrect[0] = width;
1513 dirtyrect[1] = height;
1517 // Reset cached glyphs
1518 for(font : fontList)
1521 font.glyphcount = 0;
1522 for( i = 0 ; i < FM_HASH_TABLE_SIZE ; i++ )
1523 font.hashtable[i] = -1;
1525 renderer.resetImages( );
1528 this.height = height;
1529 this.bytesperline = width * bytesperpixel;
1530 this.widthinv = 1.0f / this.width;
1531 this.heightinv = 1.0f / this.height;
1539 bool initPathDraw( FMPathDraw pathdraw )
1541 FMState *state = &states[ nstates - 1 ];
1542 FMFont font = state->font;
1545 pathdraw.prevGlyphIndex = -1;
1546 pathdraw.middleAlign = font.middleAlign * (float)state->size;
1552 float pathDrawCharacter( FMPathDraw pathdraw, float x, float y, float vectorx, float vectory, int unicode )
1555 FMState *state = &states[ nstates - 1 ];
1556 FMFont font = state->font;
1557 FMGlyph *glyph = getGlyph(font, unicode, state->size, 0, false );
1560 subpixel = font.ftFont.getGlyphKernAdvance( pathdraw.prevGlyphIndex, glyph->glyphindex );
1561 if(font.processImage)
1563 FMGlyph *outlineGlyph = getGlyph(font, unicode, state->size, 0, true );
1565 drawTextGlyphFloat(font, outlineGlyph, x, y, vectorx, vectory, (float)subpixel * (1.0f/64.0f), pathdraw.middleAlign, true );
1567 drawTextGlyphFloat(font, glyph, x, y, vectorx, vectory, (float)subpixel * (1.0f/64.0f), pathdraw.middleAlign, false );
1569 subpixel += glyph->advance;
1570 pathdraw.prevGlyphIndex = glyph->glyphindex;
1575 pathdraw.prevGlyphIndex = -1;
1578 return (float)subpixel * (1.0f/64.0f);
1581 float pathDrawPredictAdvance( FMPathDraw pathdraw, unichar unicode )
1584 FMState *state = &states[ nstates - 1 ];
1585 FMFont font = state->font;
1586 FMGlyph *glyph = getGlyph(font, unicode, state->size, 0, false );
1589 subpixel = font.ftFont.getGlyphKernAdvance( pathdraw.prevGlyphIndex, glyph->glyphindex );
1590 subpixel += glyph->advance;
1592 return (float)subpixel * (1.0f/64.0f);