slightly better float output
[dd2d.git] / d2dfont.d
blobd58d3581621c8d70041d02a97a1e4909f42e9a2d
1 /* DooM2D: Midnight on the Firing Line
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 module d2dfont is aliced;
19 private:
21 import arsd.color;
22 import iv.vfs.augs;
24 import iv.glbinds;
25 import glutils;
26 import console;
27 import wadarc;
29 import d2dimage;
30 import tatlas;
32 import iv.vfs.koi8;
35 // ////////////////////////////////////////////////////////////////////////// //
36 struct D2DFont {
37 bool imagesLoaded;
38 D2DImage[256] vga;
39 TexAtlas.Rect[256] rects;
40 GLuint listBase;
41 TexAtlas fontAtlas;
43 // called when context is destroyed; so we don't need to free resources
44 void glRegen () {
45 // create display lists
46 listBase = glGenLists(256);
47 fontAtlas.updateTexture();
48 glPushMatrix();
49 scope(exit) glPopMatrix();
50 foreach (ubyte cc; 0..256) {
51 auto vi = vga[cc];
52 TexAtlas.Rect arc = rects[cc];
53 if (vi is null) {
54 // try uppercase
55 ubyte cn = cast(ubyte)koi8upper(cast(char)cc);
56 vi = vga[cn];
57 arc = rects[cn];
58 if (vi is null) {
59 // try lowercase
60 cn = cast(ubyte)koi8lower(cast(char)cc);
61 vi = vga[cn];
62 arc = rects[cn];
64 if (vi is null) {
65 // else use space
66 vi = vga[32];
67 arc = rects[32];
70 glNewList(listBase+cc, GL_COMPILE);
71 // draw char sprite
72 int x0 = -vi.sx;
73 int y0 = -vi.sy;
74 int x1 = x0+vi.width;
75 int y1 = y0+vi.height;
76 auto frect = fontAtlas.texCoords(arc);
77 glBegin(GL_QUADS);
78 glTexCoord2f(frect.x0, frect.y0); glVertex2i(x0, y0); // top-left
79 glTexCoord2f(frect.x1, frect.y0); glVertex2i(x1, y0); // top-right
80 glTexCoord2f(frect.x1, frect.y1); glVertex2i(x1, y1); // bottom-right
81 glTexCoord2f(frect.x0, frect.y1); glVertex2i(x0, y1); // bottom-left
82 glEnd();
83 // move coords
84 glTranslatef(vi.width, 0, 0);
85 // done
86 glEndList();
90 // build texture atlas
91 private void buildAtlas (bool asBW=false, int spwdt=-1, int sphgt=-1) {
92 // create empty thing for space (if necessary)
93 if (vga[32] is null) {
94 int height = 0, wdt, count;
95 foreach (D2DImage v; vga) if (v !is null) { if (height < v.height) height = v.height; wdt += v.width; ++count; }
96 wdt /= count;
97 if (spwdt <= 0) spwdt = wdt;
98 if (sphgt <= 0) sphgt = height;
99 vga[32] = new D2DImage(spwdt, sphgt);
101 int asz = 128;
102 for (;;) {
103 bool success = true;
104 fontAtlas = new TexAtlas(asz, asz);
105 foreach (ubyte cc; 0..256) {
106 if (vga[cc] is null) continue;
107 auto img = (asBW ? vga[cc].blackAndWhiteChan!"red".img : vga[cc].img);
108 auto rc = fontAtlas.insert(img);
109 if (!rc.valid) { success = false; break; }
110 rects[cc] = rc;
112 if (success) break;
113 asz *= 2; // increase atlas size
114 if (asz > 2048) assert(0, "invalid bitmap font (too huge)");
120 __gshared D2DFont smfont;
121 __gshared D2DFont bffont;
122 __gshared D2DFont smbwfont;
125 //TODO: reinit font function
126 public void loadSmFont () { loadSmFontAt(smfont, false); loadSmFontAt(smbwfont, true); }
127 public void loadBfFont () { loadBfFontAt(bffont); }
130 void loadSmFontAt (ref D2DFont font, bool asBW) {
131 if (!font.imagesLoaded) {
132 foreach (ubyte cc; 0..256) {
133 //if (cc == 'y') continue; // smfont has invalid glyph there (why?!)
134 try {
135 import std.string : format;
136 font.vga[cc] = new D2DImage("fonts/stcf/stcfnx%02x.vga".format(cc));
137 } catch (Exception) {
141 font.buildAtlas(asBW);
142 font.glRegen();
145 import arsd.png;
146 writePng("_zfont.png", font.fontAtlas.img);
152 void loadBfFontAt (ref D2DFont font) {
153 if (!font.imagesLoaded) {
154 foreach (ubyte cc; 0..256) {
155 try {
156 import std.string : format;
157 font.vga[cc] = new D2DImage("fonts/stbf/stbf_x%02x.vga".format(cc));
158 } catch (Exception) {
162 font.buildAtlas();
163 font.glRegen();
166 import arsd.png;
167 writePng("_zfont.png", font.fontAtlas.img);
173 void fontDrawText (ref D2DFont font, int x, int y, const(char)[] str) {
174 if (str.length == 0) return;
175 glPushMatrix();
176 glPushAttrib(GL_LIST_BIT/*|GL_TEXTURE_BIT*/);
177 scope(exit) {
178 glPopAttrib();
179 glPopMatrix();
181 bindTexture(font.fontAtlas.tex.id);
182 //glRasterPos2i(x, y);
183 glTranslatef(x, y, 0);
184 glListBase(font.listBase);
185 glCallLists(cast(uint)str.length, GL_UNSIGNED_BYTE, str.ptr);
189 int fontTextWidth (ref D2DFont font, const(char)[] str) {
190 int res = 0;
191 foreach (char ch; str) {
192 auto v = font.vga.ptr[ch];
193 if (v !is null) res += v.width;
195 return res;
199 int fontTextHeight (ref D2DFont font, const(char)[] str) {
200 int res = 0;
201 foreach (char ch; str) {
202 auto v = font.vga.ptr[ch];
203 if (v !is null && res < v.height) res = v.height;
205 return res;
209 public void smDrawText (int x, int y, const(char)[] str) { fontDrawText(smfont, x, y, str); }
210 public void bfDrawText (int x, int y, const(char)[] str) { fontDrawText(bffont, x, y, str); }
211 public void smbwDrawText (int x, int y, const(char)[] str) { fontDrawText(smbwfont, x, y, str); }
213 public int smTextWidth (const(char)[] str) { return fontTextWidth(smfont, str); }
214 public int bfTextWidth (const(char)[] str) { return fontTextWidth(bffont, str); }
216 public int smTextHeight (const(char)[] str) { return fontTextHeight(smfont, str); }
217 public int bfTextHeight (const(char)[] str) { return fontTextHeight(bffont, str); }
220 // ////////////////////////////////////////////////////////////////////////// //
221 __gshared Texture conFontTex;
222 __gshared GLuint conListBase;
223 //enum ConCharWdt = 10;
224 //enum ConCharHgt = 16;
225 enum ConCharWdt = 8;
226 enum ConCharHgt = 8;
228 public void loadConFont () {
229 //conFontTex = new Texture("console/confont.png", Texture.Option.Nearest);
230 conFontTex = new Texture("console/confont8.png", Texture.Option.Nearest);
231 float iw = cast(float)conFontTex.width;
232 float ih = cast(float)conFontTex.height;
233 conListBase = glGenLists(256);
234 glPushMatrix();
235 scope(exit) glPopMatrix();
236 foreach (ubyte cc; 0..256) {
237 //int x = 0, y = 0;
238 //if (cc > ' ') { x = ((cc-32)%16)*16; y = ((cc-32)/16)*16; }
239 int x = (cc%16)*ConCharWdt;
240 int y = (cc/16)*ConCharHgt;
241 glNewList(conListBase+cc, GL_COMPILE);
242 // draw char sprite
243 int x0 = 0;
244 int y0 = 0;
245 int x1 = ConCharWdt;
246 int y1 = ConCharHgt;
247 float tx0 = x/iw, ty0 = y/ih;
248 float tx1 = (x+ConCharWdt)/iw, ty1 = (y+ConCharHgt)/ih;
249 glBegin(GL_QUADS);
250 glTexCoord2f(tx0, ty0); glVertex2i(x0, y0); // top-left
251 glTexCoord2f(tx1, ty0); glVertex2i(x1, y0); // top-right
252 glTexCoord2f(tx1, ty1); glVertex2i(x1, y1); // bottom-right
253 glTexCoord2f(tx0, ty1); glVertex2i(x0, y1); // bottom-left
254 glEnd();
255 // move coords
256 glTranslatef(ConCharWdt, 0, 0);
257 // done
258 glEndList();
262 public int conCharHeight () { pragma(inline, true); return ConCharHgt; }
263 public int conCharWidth (char ch) { pragma(inline, true); return ConCharWdt; }
265 public void conDrawChar (char ch) {
266 bindTexture(conFontTex.id);
267 glCallList(cast(uint)ch+conListBase);