freetypefont.monkey2 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. Namespace mojo.graphics
  2. #Import "<freetype>"
  3. Private
  4. Using freetype
  5. Global FreeType:FT_Library
  6. Function FontError()
  7. RuntimeError( "Font error" )
  8. End
  9. Public
  10. #rem monkeydoc @hidden
  11. #end
  12. Class FreeTypeFont Extends Font
  13. Method GetGlyph:Glyph( char:Int ) Override
  14. Local page:=char Shr 8
  15. If page<0 Or page>=_pages.Length Return _nullGlyph
  16. Local gpage:=_pages[page]
  17. If Not gpage Return _nullGlyph
  18. If Not gpage.image LoadGlyphPage( page,gpage )
  19. Local index:=char & 255
  20. If index>=gpage.glyphs.Length Return _nullGlyph
  21. Return gpage.glyphs[index]
  22. End
  23. Method GetGlyphPage:Image( char:Int ) Override
  24. Local page:=char Shr 8
  25. If page<0 Or page>=_pages.Length Return Null
  26. Local gpage:=_pages[page]
  27. If Not gpage Return Null
  28. If Not gpage.image LoadGlyphPage( page,gpage )
  29. Local index:=char & 255
  30. If index>=gpage.glyphs.Length Return Null
  31. Return gpage.image
  32. End
  33. Method GetKerning:Float( firstChar:Int,secondChar:Int ) Override
  34. If Not _hasKerning Return 0
  35. Local firstGlyph:=FT_Get_Char_Index( _face,firstChar )
  36. Local secondGlyph:=FT_Get_Char_Index( _face,secondChar )
  37. Local delta:FT_Vector
  38. FT_Get_Kerning( _face,firstGlyph,secondGlyph,0,Varptr delta )
  39. Return delta.x Shr 6
  40. End
  41. Function Load:FreeTypeFont( path:String,height:Float,shader:Shader=Null,textureFlags:TextureFlags=TextureFlags.FilterMipmap )
  42. If Not FreeType And FT_Init_FreeType( Varptr FreeType ) Return Null
  43. Local data:=DataBuffer.Load( path )
  44. If Not data
  45. If Not ExtractRootDir( path ) data=DataBuffer.Load( "font::"+path )
  46. If Not data Return Null
  47. Endif
  48. Local face:FT_Face
  49. If FT_New_Memory_Face( FreeType,data.Data,data.Length,0,Varptr face )
  50. data.Discard()
  51. Return Null
  52. Endif
  53. If Not shader shader=Shader.Open( "font" )
  54. Local font:=New FreeTypeFont( data,face,height,shader,textureFlags )
  55. Return font
  56. End
  57. Protected
  58. Method OnDiscard() Override
  59. FT_Done_Face( _face )
  60. _data.Discard()
  61. _data=Null
  62. _face=Null
  63. End
  64. Private
  65. Class GlyphPage
  66. Field image:Image
  67. Field glyphs:Glyph[]
  68. End
  69. Field _data:DataBuffer
  70. Field _face:FT_Face
  71. Field _shader:Shader
  72. Field _textureFlags:TextureFlags
  73. Field _hasKerning:Bool
  74. Field _height:Int
  75. Field _ascent:Int
  76. Field _pages:GlyphPage[]
  77. Field _nullGlyph:Glyph
  78. Method LoadGlyphPage( page:Int,gpage:GlyphPage )
  79. Const MaxTexWidth:=1024
  80. Local firstChar:=page * 256
  81. Local numChars:=256
  82. Local slot:=_face->glyph
  83. 'Measure atlas first
  84. '
  85. 'Would really rather not render glyphs here, but can't see how...
  86. '
  87. Local tx:=0,ty:=0,texw:=0,texh:=0,maxh:=0
  88. For Local i:=-1 Until numChars
  89. If i<0
  90. If FT_Load_Char( _face,0,FT_LOAD_RENDER|FT_LOAD_FORCE_AUTOHINT ) FontError()
  91. Else
  92. If Not FT_Get_Char_Index( _face,firstChar+i ) Or FT_Load_Char( _face,firstChar+i,FT_LOAD_RENDER|FT_LOAD_FORCE_AUTOHINT ) Continue
  93. Endif
  94. Local gw:=Int( slot->bitmap.width )
  95. Local gh:=Int( slot->bitmap.rows )
  96. If tx+gw+1>MaxTexWidth
  97. texw=Max( texw,tx )
  98. texh+=maxh
  99. maxh=0
  100. tx=0
  101. Endif
  102. maxh=Max( maxh,gh+1 )
  103. tx+=gw+1
  104. Next
  105. texw=Max( texw,tx )
  106. If tx texh+=maxh
  107. 'round up texw, texh to ^2 in case we're mipmapping on mobile/webgl.
  108. texw=1 Shl Int( Ceil( Log2( texw ) ) )
  109. texh=1 Shl Int( Ceil( Log2( texh ) ) )
  110. Local pixmap:=New Pixmap( texw,texh,PixelFormat.I8 )
  111. pixmap.Clear( Color.None )
  112. Local glyphs:=New Glyph[numChars],glyph:Glyph,nullGlyph:Glyph
  113. tx=0;ty=0;maxh=0
  114. For Local i:=-1 Until numChars
  115. If i<0
  116. If FT_Load_Char( _face,0,FT_LOAD_RENDER|FT_LOAD_FORCE_AUTOHINT ) FontError()
  117. Else
  118. If Not FT_Get_Char_Index( _face,firstChar+i ) Or FT_Load_Char( _face,firstChar+i,FT_LOAD_RENDER|FT_LOAD_FORCE_AUTOHINT )
  119. glyphs[i]=nullGlyph
  120. Continue
  121. Endif
  122. Endif
  123. Local gw:=Int( slot->bitmap.width )
  124. Local gh:=Int( slot->bitmap.rows )
  125. Local tmp:=New Pixmap( gw,gh,PixelFormat.I8,slot->bitmap.buffer,slot->bitmap.pitch )
  126. If tx+gw+1>pixmap.Width
  127. ty+=maxh
  128. maxh=0
  129. tx=0
  130. Endif
  131. pixmap.Paste( tmp,tx,ty )
  132. tmp.Discard()
  133. glyph.rect=New Recti( tx,ty,tx+gw,ty+gh )
  134. glyph.offset=New Vec2f( slot->bitmap_left,_ascent-slot->bitmap_top )
  135. glyph.advance=slot->advance.x Shr 6
  136. If i>=0 glyphs[i]=glyph Else nullGlyph=glyph
  137. maxh=Max( maxh,gh+1 )
  138. tx+=gw+1
  139. Next
  140. gpage.image=New Image( pixmap,_textureFlags,_shader )
  141. gpage.glyphs=glyphs
  142. ' Print "Loading glyph page "+page+", image size="+gpage.image.Rect.Size
  143. End
  144. Method New( data:DataBuffer,face:FT_Face,fheight:Float,shader:Shader,textureFlags:TextureFlags )
  145. _data=data
  146. _face=face
  147. _shader=shader
  148. _textureFlags=textureFlags
  149. _hasKerning=FT_HAS_KERNING( _face )
  150. Local size_req:FT_Size_RequestRec
  151. size_req.type=FT_SIZE_REQUEST_TYPE_REAL_DIM
  152. size_req.width=0
  153. size_req.height=fheight * 64
  154. size_req.horiResolution=0
  155. size_req.vertResolution=0
  156. If FT_Request_Size( face,Varptr size_req ) FontError()
  157. _height=(face->size->metrics.height+32) Shr 6
  158. _ascent=(face->size->metrics.ascender+32) Shr 6
  159. Local gindex:FT_UInt
  160. Local char:=FT_Get_First_Char( face,Varptr gindex )
  161. Local minChar:=char,maxChar:=char,maxPage:=0
  162. _pages=New GlyphPage[256]
  163. While gindex
  164. Local page:Int=char Shr 8
  165. If page>=0 And page<_pages.Length And Not _pages[page]
  166. maxPage=Max( page,maxPage )
  167. _pages[page]=New GlyphPage
  168. Endif
  169. char=FT_Get_Next_Char( face,char,Varptr gindex )
  170. minChar=Min( char,minChar )
  171. maxChar=Max( char,maxChar )
  172. Wend
  173. _pages=_pages.Slice( 0,maxPage+1 )
  174. Super.Init( _height,minChar,maxChar-minChar+1 )
  175. _nullGlyph=GetGlyph( 0 )
  176. End
  177. End