1 // Written in the D programming language. 2 /++ 3 + Authors: KanzakiKino 4 + Copyright: KanzakiKino 2018 5 + License: LGPL-3.0 6 ++/ 7 module g4d.ft.texture; 8 import g4d.ft.font, 9 g4d.ft.lib, 10 g4d.gl.buffer, 11 g4d.gl.texture, 12 g4d.math.rational, 13 g4d.util.bitmap; 14 import gl3n.linalg; 15 import std.algorithm, 16 std.array, 17 std.conv; 18 19 /// A texture of text. 20 class TextTexture : Tex2D 21 { 22 protected static Glyph[dchar] renderGlyphs ( FontFace face, dstring text ) 23 { 24 Glyph[dchar] result; 25 26 foreach ( c; text ) { 27 if ( c !in result ) { 28 result[c] = face.render( c ); 29 } 30 } 31 return result; 32 } 33 34 /// A metrics data of characters. 35 struct Metrics 36 { 37 protected int pos; 38 39 /// Size in pixel. 40 vec2i size; 41 /// UV buffer. 42 ArrayBuffer uv; 43 44 /// Width of collision. 45 ulong horiAdvance; 46 /// Size of moving from origin. 47 vec2i horiBearing; 48 } 49 50 protected Metrics[dchar] _chars; 51 /// Metrics data of characters drawn this texture. 52 const @property chars () { return _chars; } 53 54 /// Creates text texture from FontFace and text. 55 this ( FontFace face, dstring text ) 56 { 57 auto glyphs = renderGlyphs( face, text ); 58 super( renderBitmap( glyphs ), true ); 59 } 60 61 protected vec2i placeGlyphs ( Glyph[dchar] glyphs ) 62 { 63 Metrics m; 64 auto size = vec2i(0,0); 65 foreach ( c, g; glyphs ) { 66 m.pos = size.x; 67 m.size = g.bmp.size; 68 m.uv = null; 69 m.horiAdvance = g.advance; 70 m.horiBearing = g.bearing; 71 72 _chars[c] = m; 73 74 size.x += g.bmp.width + 1; 75 size.y = max( size.y, g.bmp.rows ).to!int; 76 } 77 return size; 78 } 79 80 protected BitmapA renderBitmap ( Glyph[dchar] glyphs ) 81 { 82 auto size = placeGlyphs( glyphs ); 83 auto result = new BitmapA( size ); 84 85 size.x = size.x.nextPower2; 86 size.y = size.y.nextPower2; 87 88 Metrics* m; 89 float left, top, right, bottom; 90 91 foreach ( c, g; glyphs ) { 92 m = &_chars[c]; 93 result.overwrite( vec2i(m.pos,0), g.bmp ); 94 95 left = m.pos*1f/size.x; 96 top = 0; 97 right = left + m.size.x*1f/size.x; 98 bottom = m.size.y*1f/size.y; 99 100 m.uv = new ArrayBuffer( [ 101 left,top, right,top, right,bottom, left,bottom, 102 ] ); 103 } 104 return result; 105 } 106 }