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 }