better image loader; it can load pngs now (with special chunk for offset)
[dd2d.git] / d2dfont.d
blobdb5d1b75ecee629bf1bc37bf9b9df373e108e4b4
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); }