]> git.tdb.fi Git - libs/gl.git/blob - source/font.cpp
Add kerning support to Font
[libs/gl.git] / source / font.cpp
1 #include <msp/core/maputils.h>
2 #include <msp/datafile/collection.h>
3 #include "bindable.h"
4 #include "gl.h"
5 #include "font.h"
6 #include "immediate.h"
7 #include "primitivetype.h"
8 #include "texture2d.h"
9
10 using namespace std;
11
12 namespace Msp {
13 namespace GL {
14
15 Font::Font():
16         native_size(1),
17         ascent(1),
18         descent(0)
19 { }
20
21 // Avoid synthesizing ~RefPtr in files including font.h
22 Font::~Font()
23 { }
24
25 void Font::set_texture(const Texture2D &t)
26 {
27         texture = &t;
28         texture.keep();
29 }
30
31 const Texture2D &Font::get_texture() const
32 {
33         if(!texture)
34                 throw logic_error("No texture");
35         return *texture;
36 }
37
38 void Font::add_glyph(const Glyph &g)
39 {
40         insert_unique(glyphs, g.code, g);
41 }
42
43 void Font::set_kerning(unsigned l, unsigned r, float d)
44 {
45         kerning[KerningKey(l, r)] = d;
46 }
47
48 float Font::get_string_width(const string &str, StringCodec::Decoder &dec) const
49 {
50         float x = 0;
51
52         unsigned prev = 0;
53         for(string::const_iterator i=str.begin(); i!=str.end();)
54         {
55                 unsigned c = dec.decode_char(str, i);
56                 if(prev)
57                         x += get_glyph_advance(prev, c);
58                 prev = c;
59         }
60         x += get_glyph_advance(prev);
61
62         return x;
63 }
64
65 void Font::draw_string(const string &str, StringCodec::Decoder &dec, const Color &color) const
66 {
67         Bind bind_tex(get_texture(), true);
68         Immediate imm((TEXCOORD2, COLOR4_UBYTE, VERTEX2));
69         imm.color(color);
70         build_string(str, dec, imm);
71 }
72
73 void Font::build_string(const string &str, StringCodec::Decoder &dec, PrimitiveBuilder &bld) const
74 {
75         MatrixStack::Push push_mtx(bld.matrix());
76
77         bld.begin(QUADS);
78         unsigned prev = 0;
79         for(string::const_iterator i=str.begin(); i!=str.end();)
80         {
81                 unsigned c = dec.decode_char(str, i);
82                 GlyphMap::const_iterator j = glyphs.find(c);
83                 if(j==glyphs.end())
84                         continue;
85
86                 if(prev)
87                         bld.matrix() *= Matrix::translation(get_glyph_advance(prev, c), 0, 0);
88
89                 create_glyph_vertices(j->second, bld);
90                 prev = c;
91         }
92         bld.end();
93 }
94
95 void Font::create_glyph_vertices(const Glyph &glyph, VertexBuilder &bld) const
96 {
97         bld.texcoord(glyph.x1, glyph.y1);
98         bld.vertex(glyph.off_x, glyph.off_y);
99         bld.texcoord(glyph.x2, glyph.y1);
100         bld.vertex(glyph.off_x+glyph.w, glyph.off_y);
101         bld.texcoord(glyph.x2, glyph.y2);
102         bld.vertex(glyph.off_x+glyph.w, glyph.off_y+glyph.h);
103         bld.texcoord(glyph.x1, glyph.y2);
104         bld.vertex(glyph.off_x, glyph.off_y+glyph.h);
105 }
106
107 float Font::get_glyph_advance(unsigned code, unsigned next) const
108 {
109         GlyphMap::const_iterator i = glyphs.find(code);
110         if(i==glyphs.end())
111                 return 0;
112
113         float advance = i->second.advance;
114
115         if(next)
116         {
117                 KerningMap::const_iterator j = kerning.find(KerningKey(code, next));
118                 if(j!=kerning.end())
119                         advance += j->second;
120         }
121
122         return advance;
123 }
124
125
126 Font::Loader::Loader(Font &f):
127         DataFile::CollectionObjectLoader<Font>(f, 0)
128 {
129         init();
130 }
131
132 Font::Loader::Loader(Font &f, Collection &c):
133         DataFile::CollectionObjectLoader<Font>(f, &c)
134 {
135         init();
136 }
137
138 void Font::Loader::init()
139 {
140         add("native_size", &Font::native_size);
141         add("ascent",      &Font::ascent);
142         add("descent",     &Font::descent);
143         add("texture",     &Loader::texture);
144         add("texture",     &Loader::texture_ref);
145         add("glyph",       &Loader::glyph);
146         add("kerning",     &Loader::kerning);
147 }
148
149 void Font::Loader::glyph(unsigned c)
150 {
151         Glyph gl;
152         gl.code = c;
153         load_sub(gl);
154         obj.glyphs.insert(GlyphMap::value_type(c, gl));
155 }
156
157 void Font::Loader::kerning(unsigned l, unsigned r, float d)
158 {
159         obj.kerning[KerningKey(l, r)] = d;
160 }
161
162 void Font::Loader::texture()
163 {
164         RefPtr<Texture2D> tex = new Texture2D;
165         load_sub(*tex);
166         obj.texture = tex;
167 }
168
169 void Font::Loader::texture_ref(const string &name)
170 {
171         obj.texture = &get_collection().get<Texture2D>(name);
172         obj.texture.keep();
173 }
174
175
176 Font::Glyph::Loader::Loader(Glyph &g):
177         DataFile::ObjectLoader<Glyph>(g)
178 {
179         add("texcoords", &Loader::texcoords);
180         add("size",      &Glyph::w,     &Glyph::h);
181         add("offset",    &Glyph::off_x, &Glyph::off_y);
182         add("advance",   &Glyph::advance);
183 }
184
185 void Font::Glyph::Loader::texcoords(float x1, float y1, float x2, float y2)
186 {
187         obj.x1 = x1;
188         obj.y1 = y1;
189         obj.x2 = x2;
190         obj.y2 = y2;
191 }
192
193 } // namespace GL
194 } // namespace Msp