1 /** @file compositebitmapfont.cpp Composite bitmap font.
2 *
3 * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4 * @authors Copyright © 2006-2013 Daniel Swanson <danij@dengine.net>
5 *
6 * @par License
7 * GPL: http://www.gnu.org/licenses/gpl.html
8 *
9 * <small>This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version. This program is distributed in the hope that it
13 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15 * Public License for more details. You should have received a copy of the GNU
16 * General Public License along with this program; if not, write to the Free
17 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18 * 02110-1301 USA</small>
19 */
20
21 #include "clientapp.h"
22 #include "resource/compositebitmapfont.h"
23
24 #include <doomsday/res/Textures>
25
26 #include "api_resource.h" // R_GetPatchInfo
27 #include "dd_main.h" // App_Resources(), isDedicated
28 #include "sys_system.h" // novideo
29 #include "gl/gl_texmanager.h" // GL_TextureVariantSpec
30
31 using namespace de;
32
DENG2_PIMPL(CompositeBitmapFont)33 DENG2_PIMPL(CompositeBitmapFont)
34 {
35 ded_compositefont_t *def; /// Definition on which "this" font is derived (if any).
36 bool needGLInit;
37
38 /// Font metrics.
39 int leading;
40 int ascent;
41 int descent;
42
43 Glyph glyphs[MAX_CHARS];
44 Glyph missingGlyph;
45
46 Impl(Public *i)
47 : Base(i)
48 , def(0)
49 , needGLInit(true)
50 , leading(0)
51 , ascent(0)
52 , descent(0)
53 {
54 zap(glyphs);
55 zap(missingGlyph);
56 self()._flags |= AbstractFont::Colorize;
57 }
58
59 ~Impl()
60 {
61 self().glDeinit();
62 }
63
64 Glyph &glyph(uchar ch)
65 {
66 //if(ch >= MAX_CHARS) return missingGlyph;
67 if(!glyphs[ch].haveSourceImage) return missingGlyph;
68 return glyphs[ch];
69 }
70 };
71
CompositeBitmapFont(FontManifest & manifest)72 CompositeBitmapFont::CompositeBitmapFont(FontManifest &manifest)
73 : AbstractFont(manifest), d(new Impl(this))
74 {}
75
ascent() const76 int CompositeBitmapFont::ascent() const
77 {
78 glInit();
79 return d->ascent;
80 }
81
descent() const82 int CompositeBitmapFont::descent() const
83 {
84 glInit();
85 return d->descent;
86 }
87
lineSpacing() const88 int CompositeBitmapFont::lineSpacing() const
89 {
90 glInit();
91 return d->leading;
92 }
93
glyphPosCoords(uchar ch) const94 Rectanglei const &CompositeBitmapFont::glyphPosCoords(uchar ch) const
95 {
96 glInit();
97 return d->glyph(ch).geometry;
98 }
99
glyphTexCoords(uchar) const100 Rectanglei const &CompositeBitmapFont::glyphTexCoords(uchar /*ch*/) const
101 {
102 static Rectanglei coords(Vector2i(0, 0), Vector2i(1, 1));
103 glInit();
104 return coords;
105 }
106
glyphTextureBorder(uchar ch) const107 uint CompositeBitmapFont::glyphTextureBorder(uchar ch) const
108 {
109 glInit();
110 return d->glyph(ch).border;
111 }
112
glyphTexture(uchar ch) const113 TextureVariant *CompositeBitmapFont::glyphTexture(uchar ch) const
114 {
115 glInit();
116 return d->glyph(ch).tex;
117 }
118
glyphPatch(uchar ch) const119 patchid_t CompositeBitmapFont::glyphPatch(uchar ch) const
120 {
121 glInit();
122 return d->glyph(ch).patch;
123 }
124
glyphSetPatch(uchar ch,String encodedPatchName)125 void CompositeBitmapFont::glyphSetPatch(uchar ch, String encodedPatchName)
126 {
127 //if(ch >= MAX_CHARS) return;
128 d->glyphs[ch].patch = res::Textures::get().declarePatch(encodedPatchName);
129
130 // We'll need to rebuild the prepared GL resources.
131 d->needGLInit = true;
132 }
133
134 /// @todo fixme: Do not assume the texture-usage context is @c TC_UI.
glyphTextureSpec()135 static TextureVariantSpec const &glyphTextureSpec()
136 {
137 return ClientApp::resources().textureSpec(TC_UI,
138 TSF_MONOCHROME | TSF_UPSCALE_AND_SHARPEN, 0, 0, 0, GL_CLAMP_TO_EDGE,
139 GL_CLAMP_TO_EDGE, 0, -3, 0, false, false, false, false);
140 }
141
glInit() const142 void CompositeBitmapFont::glInit() const
143 {
144 if(!d->needGLInit) return;
145 if(novideo || BusyMode_Active()) return;
146
147 LOG_AS("CompositeBitmapFont");
148
149 glDeinit();
150
151 int foundGlyphs = 0;
152 Vector2ui avgSize;
153 for(int i = 0; i < MAX_CHARS; ++i)
154 {
155 Glyph *ch = &d->glyphs[i];
156 patchid_t patch = ch->patch;
157
158 ch->haveSourceImage = patch != 0;
159 if(!ch->haveSourceImage) continue;
160
161 try
162 {
163 res::Texture &tex = res::Textures::get().textureScheme("Patches").findByUniqueId(patch).texture();
164
165 ch->tex = static_cast<ClientTexture &>(tex).prepareVariant(glyphTextureSpec());
166 ch->geometry = Rectanglei::fromSize(tex.origin(), tex.dimensions().toVector2ui());
167
168 ch->border = 0;
169 if(ch->tex && ch->tex->source() == res::Original)
170 {
171 // Upscale & Sharpen will have been applied.
172 ch->border = 1;
173 }
174
175 avgSize += ch->geometry.size();
176 ++foundGlyphs;
177 }
178 catch(res::TextureManifest::MissingTextureError const &er)
179 {
180 // Log but otherwise ignore this error.
181 LOG_RES_WARNING(er.asText() + ", ignoring.");
182 }
183 catch(res::TextureScheme::NotFoundError const &er)
184 {
185 // Log but otherwise ignore this error.
186 LOG_RES_WARNING(er.asText() + ", ignoring.");
187 }
188 }
189
190 d->missingGlyph.geometry.setSize(avgSize / (foundGlyphs? foundGlyphs : 1));
191
192 // We have prepared all patches.
193 d->needGLInit = false;
194 }
195
glDeinit() const196 void CompositeBitmapFont::glDeinit() const
197 {
198 if(novideo) return;
199
200 d->needGLInit = true;
201 if(BusyMode_Active()) return;
202
203 for(int i = 0; i < 256; ++i)
204 {
205 Glyph *ch = &d->glyphs[i];
206 if(!ch->tex) continue;
207 ch->tex->release();
208 ch->tex = 0;
209 }
210 }
211
fromDef(FontManifest & manifest,ded_compositefont_t const & def)212 CompositeBitmapFont *CompositeBitmapFont::fromDef(FontManifest &manifest,
213 ded_compositefont_t const &def) // static
214 {
215 LOG_AS("CompositeBitmapFont::fromDef");
216
217 CompositeBitmapFont *font = new CompositeBitmapFont(manifest);
218 font->setDefinition(const_cast<ded_compositefont_t *>(&def));
219
220 for(int i = 0; i < def.charMap.size(); ++i)
221 {
222 if(!def.charMap[i].path) continue;
223 try
224 {
225 String glyphPatchPath = def.charMap[i].path->resolved();
226 font->glyphSetPatch(def.charMap[i].ch, glyphPatchPath);
227 }
228 catch(de::Uri::ResolveError const &er)
229 {
230 LOG_RES_WARNING(er.asText());
231 }
232 }
233
234 // Lets try to prepare it right away.
235 font->glInit();
236 return font;
237 }
238
definition() const239 ded_compositefont_t *CompositeBitmapFont::definition() const
240 {
241 return d->def;
242 }
243
setDefinition(ded_compositefont_t * newDef)244 void CompositeBitmapFont::setDefinition(ded_compositefont_t *newDef)
245 {
246 d->def = newDef;
247 }
248
rebuildFromDef(ded_compositefont_t const & newDef)249 void CompositeBitmapFont::rebuildFromDef(ded_compositefont_t const &newDef)
250 {
251 LOG_AS("CompositeBitmapFont::rebuildFromDef");
252
253 setDefinition(const_cast<ded_compositefont_t *>(&newDef));
254 if(!d->def) return;
255
256 for(int i = 0; i < d->def->charMap.size(); ++i)
257 {
258 if(!d->def->charMap[i].path) continue;
259
260 try
261 {
262 String glyphPatchPath = d->def->charMap[i].path->resolved();
263 glyphSetPatch(d->def->charMap[i].ch, glyphPatchPath);
264 }
265 catch(de::Uri::ResolveError const& er)
266 {
267 LOG_RES_WARNING(er.asText());
268 }
269 }
270 }
271