src/platform/switch/gui-font.c (view raw)
1/* Copyright (c) 2013-2018 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include <mgba-util/gui/font.h>
7#include <mgba-util/gui/font-metrics.h>
8#include <mgba-util/png-io.h>
9#include <mgba-util/string.h>
10#include <mgba-util/vfs.h>
11
12#include <GLES3/gl3.h>
13
14#define GLYPH_HEIGHT 24
15#define CELL_HEIGHT 32
16#define CELL_WIDTH 32
17#define MAX_GLYPHS 1024
18
19static const GLfloat _offsets[] = {
20 0.f, 0.f,
21 1.f, 0.f,
22 1.f, 1.f,
23 0.f, 1.f,
24};
25
26static const GLchar* const _gles3Header =
27 "#version 300 es\n"
28 "precision mediump float;\n";
29
30static const char* const _vertexShader =
31 "in vec2 offset;\n"
32 "in vec3 origin;\n"
33 "in vec2 glyph;\n"
34 "in vec2 dims;\n"
35 "in mat2 transform;\n"
36 "in vec4 color;\n"
37 "out vec4 fragColor;\n"
38 "out vec2 texCoord;\n"
39
40 "void main() {\n"
41 " texCoord = (glyph + offset * dims) / 512.0;\n"
42 " vec2 scaledOffset = (transform * (offset * 2.0 - vec2(1.0)) + vec2(1.0)) / 2.0 * dims;\n"
43 " fragColor = color;\n"
44 " gl_Position = vec4((origin.x + scaledOffset.x) / 640.0 - 1.0, -(origin.y + scaledOffset.y) / 360.0 + 1.0, origin.z, 1.0);\n"
45 "}";
46
47static const char* const _fragmentShader =
48 "in vec2 texCoord;\n"
49 "in vec4 fragColor;\n"
50 "out vec4 outColor;\n"
51 "uniform sampler2D tex;\n"
52 "uniform float cutoff;\n"
53 "uniform vec3 colorModulus;\n"
54
55 "void main() {\n"
56 " vec4 texColor = texture2D(tex, texCoord);\n"
57 " texColor.a = clamp((texColor.a - cutoff) / (1.0 - cutoff), 0.0, 1.0);\n"
58 " texColor.rgb = fragColor.rgb * colorModulus;\n"
59 " texColor.a *= fragColor.a;\n"
60 " outColor = texColor;\n"
61 "}";
62
63struct GUIFont {
64 GLuint font;
65 int currentGlyph;
66 GLuint program;
67 GLuint vbo;
68 GLuint vao;
69 GLuint texLocation;
70 GLuint cutoffLocation;
71 GLuint colorModulusLocation;
72
73 GLuint originLocation;
74 GLuint glyphLocation;
75 GLuint dimsLocation;
76 GLuint transformLocation[2];
77 GLuint colorLocation;
78
79 GLuint originVbo;
80 GLuint glyphVbo;
81 GLuint dimsVbo;
82 GLuint transformVbo[2];
83 GLuint colorVbo;
84
85 GLfloat originData[MAX_GLYPHS][3];
86 GLfloat glyphData[MAX_GLYPHS][2];
87 GLfloat dimsData[MAX_GLYPHS][2];
88 GLfloat transformData[2][MAX_GLYPHS][2];
89 GLfloat colorData[MAX_GLYPHS][4];
90};
91
92static bool _loadTexture(const char* path) {
93 struct VFile* vf = VFileOpen(path, O_RDONLY);
94 if (!vf) {
95 return false;
96 }
97 png_structp png = PNGReadOpen(vf, 0);
98 png_infop info = png_create_info_struct(png);
99 png_infop end = png_create_info_struct(png);
100 bool success = false;
101 if (png && info && end) {
102 success = PNGReadHeader(png, info);
103 }
104 void* pixels = NULL;
105 if (success) {
106 unsigned height = png_get_image_height(png, info);
107 unsigned width = png_get_image_width(png, info);
108 pixels = malloc(width * height);
109 if (pixels) {
110 success = PNGReadPixels8(png, info, pixels, width, height, width);
111 success = success && PNGReadFooter(png, end);
112 } else {
113 success = false;
114 }
115 if (success) {
116 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, pixels);
117 }
118 }
119 PNGReadClose(png, info, end);
120 if (pixels) {
121 free(pixels);
122 }
123 vf->close(vf);
124 return success;
125}
126
127struct GUIFont* GUIFontCreate(void) {
128 struct GUIFont* font = malloc(sizeof(struct GUIFont));
129 if (!font) {
130 return NULL;
131 }
132 glGenTextures(1, &font->font);
133 glActiveTexture(GL_TEXTURE0);
134 glBindTexture(GL_TEXTURE_2D, font->font);
135 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
136 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
137 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
138 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
139 if (!_loadTexture("romfs:/font-new.png")) {
140 GUIFontDestroy(font);
141 return NULL;
142 }
143
144 font->currentGlyph = 0;
145 font->program = glCreateProgram();
146 GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
147 GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
148 const GLchar* shaderBuffer[2];
149
150 shaderBuffer[0] = _gles3Header;
151
152 shaderBuffer[1] = _vertexShader;
153 glShaderSource(vertexShader, 2, shaderBuffer, NULL);
154
155 shaderBuffer[1] = _fragmentShader;
156 glShaderSource(fragmentShader, 2, shaderBuffer, NULL);
157
158 glAttachShader(font->program, vertexShader);
159 glAttachShader(font->program, fragmentShader);
160
161 glCompileShader(fragmentShader);
162
163 GLint success;
164 glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
165 if (!success) {
166 GLchar msg[512];
167 glGetShaderInfoLog(fragmentShader, sizeof(msg), NULL, msg);
168 puts(msg);
169 }
170
171 glCompileShader(vertexShader);
172
173 glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
174 if (!success) {
175 GLchar msg[512];
176 glGetShaderInfoLog(vertexShader, sizeof(msg), NULL, msg);
177 puts(msg);
178 }
179
180 glLinkProgram(font->program);
181
182 glGetProgramiv(font->program, GL_LINK_STATUS, &success);
183 if (!success) {
184 GLchar msg[512];
185 glGetProgramInfoLog(font->program, sizeof(msg), NULL, msg);
186 puts(msg);
187 }
188
189 glDeleteShader(vertexShader);
190 glDeleteShader(fragmentShader);
191
192 font->texLocation = glGetUniformLocation(font->program, "tex");
193 font->cutoffLocation = glGetUniformLocation(font->program, "cutoff");
194 font->colorModulusLocation = glGetUniformLocation(font->program, "colorModulus");
195
196 font->originLocation = glGetAttribLocation(font->program, "origin");
197 font->glyphLocation = glGetAttribLocation(font->program, "glyph");
198 font->dimsLocation = glGetAttribLocation(font->program, "dims");
199 font->transformLocation[0] = glGetAttribLocation(font->program, "transform");
200 font->transformLocation[1] = font->transformLocation[0] + 1;
201 font->colorLocation = glGetAttribLocation(font->program, "color");
202
203 GLuint offsetLocation = glGetAttribLocation(font->program, "offset");
204
205 glGenVertexArrays(1, &font->vao);
206 glBindVertexArray(font->vao);
207
208 glGenBuffers(1, &font->vbo);
209 glBindBuffer(GL_ARRAY_BUFFER, font->vbo);
210 glBufferData(GL_ARRAY_BUFFER, sizeof(_offsets), _offsets, GL_STATIC_DRAW);
211 glVertexAttribPointer(offsetLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
212 glVertexAttribDivisor(offsetLocation, 0);
213 glEnableVertexAttribArray(offsetLocation);
214
215 glGenBuffers(1, &font->originVbo);
216 glBindBuffer(GL_ARRAY_BUFFER, font->originVbo);
217 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * MAX_GLYPHS, NULL, GL_STREAM_DRAW);
218 glVertexAttribPointer(font->originLocation, 3, GL_FLOAT, GL_FALSE, 0, NULL);
219 glVertexAttribDivisor(font->originLocation, 1);
220 glEnableVertexAttribArray(font->originLocation);
221
222 glGenBuffers(1, &font->glyphVbo);
223 glBindBuffer(GL_ARRAY_BUFFER, font->glyphVbo);
224 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 2 * MAX_GLYPHS, NULL, GL_STREAM_DRAW);
225 glVertexAttribPointer(font->glyphLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
226 glVertexAttribDivisor(font->glyphLocation, 1);
227 glEnableVertexAttribArray(font->glyphLocation);
228
229 glGenBuffers(1, &font->dimsVbo);
230 glBindBuffer(GL_ARRAY_BUFFER, font->dimsVbo);
231 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 2 * MAX_GLYPHS, NULL, GL_STREAM_DRAW);
232 glVertexAttribPointer(font->dimsLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
233 glVertexAttribDivisor(font->dimsLocation, 1);
234 glEnableVertexAttribArray(font->dimsLocation);
235
236 glGenBuffers(2, font->transformVbo);
237 glBindBuffer(GL_ARRAY_BUFFER, font->transformVbo[0]);
238 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 2 * MAX_GLYPHS, NULL, GL_STREAM_DRAW);
239 glVertexAttribPointer(font->transformLocation[0], 2, GL_FLOAT, GL_FALSE, 0, NULL);
240 glVertexAttribDivisor(font->transformLocation[0], 1);
241 glEnableVertexAttribArray(font->transformLocation[0]);
242 glBindBuffer(GL_ARRAY_BUFFER, font->transformVbo[1]);
243 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 2 * MAX_GLYPHS, NULL, GL_STREAM_DRAW);
244 glVertexAttribPointer(font->transformLocation[1], 2, GL_FLOAT, GL_FALSE, 0, NULL);
245 glVertexAttribDivisor(font->transformLocation[1], 1);
246 glEnableVertexAttribArray(font->transformLocation[1]);
247
248 glGenBuffers(1, &font->colorVbo);
249 glBindBuffer(GL_ARRAY_BUFFER, font->colorVbo);
250 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 4 * MAX_GLYPHS, NULL, GL_STREAM_DRAW);
251 glVertexAttribPointer(font->colorLocation, 4, GL_FLOAT, GL_FALSE, 0, NULL);
252 glVertexAttribDivisor(font->colorLocation, 1);
253 glEnableVertexAttribArray(font->colorLocation);
254
255 glBindVertexArray(0);
256
257 return font;
258}
259
260void GUIFontDestroy(struct GUIFont* font) {
261 glDeleteBuffers(1, &font->vbo);
262 glDeleteBuffers(1, &font->originVbo);
263 glDeleteBuffers(1, &font->glyphVbo);
264 glDeleteBuffers(1, &font->dimsVbo);
265 glDeleteBuffers(2, font->transformVbo);
266 glDeleteBuffers(1, &font->colorVbo);
267 glDeleteProgram(font->program);
268 glDeleteTextures(1, &font->font);
269 glDeleteVertexArrays(1, &font->vao);
270 free(font);
271}
272
273unsigned GUIFontHeight(const struct GUIFont* font) {
274 UNUSED(font);
275 return GLYPH_HEIGHT;
276}
277
278unsigned GUIFontGlyphWidth(const struct GUIFont* font, uint32_t glyph) {
279 UNUSED(font);
280 if (glyph > 0x7F) {
281 glyph = '?';
282 }
283 return defaultFontMetrics[glyph].width * 2;
284}
285
286void GUIFontIconMetrics(const struct GUIFont* font, enum GUIIcon icon, unsigned* w, unsigned* h) {
287 UNUSED(font);
288 if (icon >= GUI_ICON_MAX) {
289 if (w) {
290 *w = 0;
291 }
292 if (h) {
293 *h = 0;
294 }
295 } else {
296 if (w) {
297 *w = defaultIconMetrics[icon].width * 2;
298 }
299 if (h) {
300 *h = defaultIconMetrics[icon].height * 2;
301 }
302 }
303}
304
305void GUIFontDrawGlyph(const struct GUIFont* font, int x, int y, uint32_t color, uint32_t glyph) {
306 if (glyph > 0x7F) {
307 glyph = '?';
308 }
309 struct GUIFontGlyphMetric metric = defaultFontMetrics[glyph];
310
311 struct GUIFont* mutfont = (struct GUIFont*) font;
312 if (font->currentGlyph >= MAX_GLYPHS) {
313 GUIFontDrawSubmit(mutfont);
314 }
315
316 int offset = font->currentGlyph;
317
318 mutfont->originData[offset][0] = x;
319 mutfont->originData[offset][1] = y - GLYPH_HEIGHT + metric.padding.top * 2;
320 mutfont->originData[offset][2] = 0;
321 mutfont->glyphData[offset][0] = (glyph & 15) * CELL_WIDTH + metric.padding.left * 2;
322 mutfont->glyphData[offset][1] = (glyph >> 4) * CELL_HEIGHT + metric.padding.top * 2;
323 mutfont->dimsData[offset][0] = CELL_WIDTH - (metric.padding.left + metric.padding.right) * 2;
324 mutfont->dimsData[offset][1] = CELL_HEIGHT - (metric.padding.top + metric.padding.bottom) * 2;
325 mutfont->transformData[0][offset][0] = 1.0f;
326 mutfont->transformData[0][offset][1] = 0.0f;
327 mutfont->transformData[1][offset][0] = 0.0f;
328 mutfont->transformData[1][offset][1] = 1.0f;
329 mutfont->colorData[offset][0] = (color & 0xFF) / 255.0f;
330 mutfont->colorData[offset][1] = ((color >> 8) & 0xFF) / 255.0f;
331 mutfont->colorData[offset][2] = ((color >> 16) & 0xFF) / 255.0f;
332 mutfont->colorData[offset][3] = ((color >> 24) & 0xFF) / 255.0f;
333
334 ++mutfont->currentGlyph;
335}
336
337void GUIFontDrawIcon(const struct GUIFont* font, int x, int y, enum GUIAlignment align, enum GUIOrientation orient, uint32_t color, enum GUIIcon icon) {
338 if (icon >= GUI_ICON_MAX) {
339 return;
340 }
341 struct GUIIconMetric metric = defaultIconMetrics[icon];
342
343 float hFlip = 1.0f;
344 float vFlip = 1.0f;
345 switch (align & GUI_ALIGN_HCENTER) {
346 case GUI_ALIGN_HCENTER:
347 x -= metric.width;
348 break;
349 case GUI_ALIGN_RIGHT:
350 x -= metric.width * 2;
351 break;
352 }
353 switch (align & GUI_ALIGN_VCENTER) {
354 case GUI_ALIGN_VCENTER:
355 y -= metric.height;
356 break;
357 case GUI_ALIGN_BOTTOM:
358 y -= metric.height * 2;
359 break;
360 }
361
362 switch (orient) {
363 case GUI_ORIENT_HMIRROR:
364 hFlip = -1.0;
365 break;
366 case GUI_ORIENT_VMIRROR:
367 vFlip = -1.0;
368 break;
369 case GUI_ORIENT_0:
370 default:
371 // TODO: Rotate
372 break;
373 }
374
375 struct GUIFont* mutfont = (struct GUIFont*) font;
376 if (font->currentGlyph >= MAX_GLYPHS) {
377 GUIFontDrawSubmit(mutfont);
378 }
379
380 int offset = font->currentGlyph;
381
382 mutfont->originData[offset][0] = x;
383 mutfont->originData[offset][1] = y;
384 mutfont->originData[offset][2] = 0;
385 mutfont->glyphData[offset][0] = metric.x * 2;
386 mutfont->glyphData[offset][1] = metric.y * 2 + 256;
387 mutfont->dimsData[offset][0] = metric.width * 2;
388 mutfont->dimsData[offset][1] = metric.height * 2;
389 mutfont->transformData[0][offset][0] = hFlip;
390 mutfont->transformData[0][offset][1] = 0.0f;
391 mutfont->transformData[1][offset][0] = 0.0f;
392 mutfont->transformData[1][offset][1] = vFlip;
393 mutfont->colorData[offset][0] = (color & 0xFF) / 255.0f;
394 mutfont->colorData[offset][1] = ((color >> 8) & 0xFF) / 255.0f;
395 mutfont->colorData[offset][2] = ((color >> 16) & 0xFF) / 255.0f;
396 mutfont->colorData[offset][3] = ((color >> 24) & 0xFF) / 255.0f;
397
398 ++mutfont->currentGlyph;
399}
400
401void GUIFontDrawIconSize(const struct GUIFont* font, int x, int y, int w, int h, uint32_t color, enum GUIIcon icon) {
402 if (icon >= GUI_ICON_MAX) {
403 return;
404 }
405 struct GUIIconMetric metric = defaultIconMetrics[icon];
406
407 if (!w) {
408 w = metric.width * 2;
409 }
410 if (!h) {
411 h = metric.height * 2;
412 }
413
414 struct GUIFont* mutfont = (struct GUIFont*) font;
415 if (font->currentGlyph >= MAX_GLYPHS) {
416 GUIFontDrawSubmit(mutfont);
417 }
418
419 int offset = font->currentGlyph;
420
421 mutfont->originData[offset][0] = x + w / 2 - metric.width;
422 mutfont->originData[offset][1] = y + h / 2 - metric.height;
423 mutfont->originData[offset][2] = 0;
424 mutfont->glyphData[offset][0] = metric.x * 2;
425 mutfont->glyphData[offset][1] = metric.y * 2 + 256;
426 mutfont->dimsData[offset][0] = metric.width * 2;
427 mutfont->dimsData[offset][1] = metric.height * 2;
428 mutfont->transformData[0][offset][0] = w * 0.5f / metric.width;
429 mutfont->transformData[0][offset][1] = 0.0f;
430 mutfont->transformData[1][offset][0] = 0.0f;
431 mutfont->transformData[1][offset][1] = h * 0.5f / metric.height;
432 mutfont->colorData[offset][0] = (color & 0xFF) / 255.0f;
433 mutfont->colorData[offset][1] = ((color >> 8) & 0xFF) / 255.0f;
434 mutfont->colorData[offset][2] = ((color >> 16) & 0xFF) / 255.0f;
435 mutfont->colorData[offset][3] = ((color >> 24) & 0xFF) / 255.0f;
436
437 ++mutfont->currentGlyph;
438}
439
440void GUIFontDrawSubmit(struct GUIFont* font) {
441 glUseProgram(font->program);
442 glBindVertexArray(font->vao);
443 glActiveTexture(GL_TEXTURE0);
444 glBindTexture(GL_TEXTURE_2D, font->font);
445
446 glEnable(GL_BLEND);
447 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
448
449 glUniform1i(font->texLocation, 0);
450
451 glBindBuffer(GL_ARRAY_BUFFER, font->originVbo);
452 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * MAX_GLYPHS, NULL, GL_STREAM_DRAW);
453 glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * 3 * font->currentGlyph, font->originData);
454
455 glBindBuffer(GL_ARRAY_BUFFER, font->glyphVbo);
456 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 2 * MAX_GLYPHS, NULL, GL_STREAM_DRAW);
457 glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * 2 * font->currentGlyph, font->glyphData);
458
459 glBindBuffer(GL_ARRAY_BUFFER, font->dimsVbo);
460 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 2 * MAX_GLYPHS, NULL, GL_STREAM_DRAW);
461 glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * 2 * font->currentGlyph, font->dimsData);
462
463 glBindBuffer(GL_ARRAY_BUFFER, font->transformVbo[0]);
464 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 2 * MAX_GLYPHS, NULL, GL_STREAM_DRAW);
465 glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * 2 * font->currentGlyph, font->transformData[0]);
466
467 glBindBuffer(GL_ARRAY_BUFFER, font->transformVbo[1]);
468 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 2 * MAX_GLYPHS, NULL, GL_STREAM_DRAW);
469 glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * 2 * font->currentGlyph, font->transformData[1]);
470
471 glBindBuffer(GL_ARRAY_BUFFER, font->colorVbo);
472 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 4 * MAX_GLYPHS, NULL, GL_STREAM_DRAW);
473 glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * 4 * font->currentGlyph, font->colorData);
474
475 glUniform1f(font->cutoffLocation, 0.1f);
476 glUniform3f(font->colorModulusLocation, 0.f, 0.f, 0.f);
477 glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, font->currentGlyph);
478
479 glUniform1f(font->cutoffLocation, 0.7f);
480 glUniform3f(font->colorModulusLocation, 1.f, 1.f, 1.f);
481 glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, font->currentGlyph);
482
483 font->currentGlyph = 0;
484
485 glBindVertexArray(0);
486 glUseProgram(0);
487}