texturefont.cc (5472B)
1 /* Copyright 2013 Brian Swetland <swetland@frotz.net> 2 * 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #include <stdio.h> 17 18 #include "util.h" 19 #include "matrix.h" 20 #include "app.h" 21 #include "texturefont.h" 22 23 /* performance experiments to try sometime: 24 * - pass color via vtx attr instead of unpacking in shader 25 * - pass character vertex table via uniform instead of texturebuffer 26 * - instead of DrawArraysInstanced: 27 * - call DrawElements with a fixed 1,1,1,1,1,1,2,2,2,2,2,2,3,... index buffer 28 * - call DrawArrays with unpacked vtx array (6 per character) 29 * - explore different vtx formats (int vs float vs short, etc) 30 */ 31 32 TextureFont* TextureFont::load(const char *fontname) { 33 TextureFont *f = new TextureFont(); 34 if (f->init(fontname)) { 35 delete f; 36 return nullptr; 37 } 38 return f; 39 } 40 41 int TextureFont::init(const char *fontname) { 42 char tmp[256]; 43 float *cdata, *cp; 44 float dim, adj; 45 unsigned sz; 46 header = NULL; 47 48 sprintf(tmp, "%s.font.png", fontname); 49 if (glyphs.load(tmp, OPT_TEX2D_GRAY)) 50 goto fail; 51 52 sprintf(tmp, "%s.font.dat", fontname); 53 header = (FontInfo*) load_file(tmp, &sz); 54 info = header->info; 55 if (sz < sizeof(FontInfo)) { 56 error("invalid font header"); 57 goto fail; 58 } 59 if (header->magic != TEXTUREFONT_MAGIC) { 60 error("invalid font magic"); 61 goto fail; 62 } 63 if (sz < (sizeof(FontInfo) + sizeof(CharInfo) * header->count)) { 64 error("missing font data"); 65 goto fail; 66 } 67 first = header->first; 68 last = first + header->count - 1; 69 70 if (!(effect = Effect::load("texturefont"))) 71 goto fail; 72 73 cp = cdata = (float*) malloc(sizeof(float) * 4 * 6 * header->count); 74 75 /* to adjust int coords to texture coords */ 76 dim = glyphs.width; 77 adj = 0.5 / float(glyphs.width); 78 79 /* generate a 2d+uv quad for each character */ 80 for (unsigned n = 0; n < header->count; n++) { 81 *cp++ = 0; 82 *cp++ = -info[n].h; 83 *cp++ = float(info[n].x) / dim + adj; 84 *cp++ = float(info[n].y) / dim + adj; 85 86 *cp++ = info[n].w; 87 *cp++ = -info[n].h; 88 *cp++ = float(info[n].x + info[n].w - 1) / dim + adj; 89 *cp++ = float(info[n].y) / dim + adj; 90 91 *cp++ = 0; 92 *cp++ = 0; 93 *cp++ = float(info[n].x) / dim + adj; 94 *cp++ = float(info[n].y + info[n].h - 1) / dim + adj; 95 96 *cp++ = info[n].w; 97 *cp++ = 0; 98 *cp++ = float(info[n].x + info[n].w - 1) / dim + adj; 99 *cp++ = float(info[n].y + info[n].h - 1) / dim + adj; 100 101 *cp++ = 0; 102 *cp++ = 0; 103 *cp++ = float(info[n].x) / dim + adj; 104 *cp++ = float(info[n].y + info[n].h - 1) / dim + adj; 105 106 *cp++ = info[n].w; 107 *cp++ = -info[n].h; 108 *cp++ = float(info[n].x + info[n].w - 1) / dim + adj; 109 *cp++ = float(info[n].y) / dim + adj; 110 } 111 cbuf.load(cdata, sizeof(float) * 4 * 6 * header->count); 112 113 glGenTextures(1, &tbid); 114 glActiveTexture(GL_TEXTURE0 + 15); 115 glBindTexture(GL_TEXTURE_BUFFER, tbid); 116 glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, cbuf.id); 117 return 0; 118 fail: 119 if (header) { 120 free(header); 121 header = NULL; 122 } 123 124 return 0; 125 } 126 127 void TextureFont::measure(const char *s, unsigned *width, unsigned *height) { 128 unsigned w = 0; 129 unsigned h = 0; 130 while (*s) { 131 unsigned n = *s++; 132 if (n == 0) 133 break; 134 if ((n < first) || (n > last)) 135 continue; 136 n -= first; 137 unsigned ch = info[n].h; 138 if (ch > h) 139 h = ch; 140 w += info[n].advance; 141 } 142 *width = w; 143 *height = h; 144 } 145 146 // idx, src, dst, count, offset, stride, divisor 147 static VertexAttrDesc layout[] = { 148 { 0, SRC_INT32, DST_INTEGER, 4, 0, 16, 6 }, 149 }; 150 151 Text* Text::create(TextureFont *font) { 152 Text *t = new Text(); 153 VertexBuffer *vdata[] = { &t->vtx }; 154 t->font = font; 155 t->max = 128; 156 t->count = 0; 157 t->data = (CharData*) malloc(sizeof(CharData) * t->max); 158 t->next = t->data; 159 t->color = RGBA(255,255,255,255); 160 t->dirty = 0; 161 162 t->vtx.load(t->data, sizeof(CharData) * t->max); 163 t->attr.init(layout, vdata, sizeof(layout) / sizeof(layout[0])); 164 165 return t; 166 }; 167 168 void Text::clear(void) { 169 next = data; 170 count = 0; 171 dirty = 1; 172 } 173 174 void Text::setColor(unsigned rgba) { 175 color = rgba; 176 } 177 178 void Text::render(void) { 179 if (count == 0) 180 return; 181 182 if (dirty) { 183 vtx.load(data, sizeof(CharData) * count); 184 dirty = 0; 185 } 186 187 font->effect->apply(); 188 font->glyphs.use(1); 189 attr.use(); 190 glActiveTexture(GL_TEXTURE0 + 0); 191 glBindTexture(GL_TEXTURE_BUFFER, font->tbid); 192 glEnable(GL_BLEND); 193 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 194 glDrawArraysInstanced(GL_TRIANGLES, 0, 6, count * 6); 195 glDisable(GL_BLEND); 196 } 197 198 void Text::puts(int x, int y, const char *s) { 199 while (count < max) { 200 unsigned n = *s++; 201 if (n == 0) 202 break; 203 if ((n < font->first) || (n > font->last)) 204 continue; 205 n -= font->first; 206 data[count].x = x + font->info[n].dx; 207 data[count].y = y - font->info[n].dy; 208 data[count].id = n * 6; 209 data[count].rgba = color & 0xFFFFFF; // strip alpha 210 count++; 211 x += font->info[n].advance; 212 } 213 dirty = 1; 214 } 215 216 void Text::printf(int x, int y, const char *fmt, ...) { 217 char buf[128]; 218 int len; 219 va_list ap; 220 va_start(ap, fmt); 221 len = vsnprintf(buf, sizeof(buf), fmt, ap); 222 va_end(ap); 223 buf[127] = 0; 224 if (len > 127) len = 127; 225 puts(x, y, buf); 226 }