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
18static const GLfloat _offsets[] = {
19 0.f, 0.f,
20 1.f, 0.f,
21 1.f, 1.f,
22 0.f, 1.f,
23};
24
25static const GLchar* const _gles2Header =
26 "#version 100\n"
27 "precision mediump float;\n";
28
29static const char* const _vertexShader =
30 "attribute vec2 offset;\n"
31 "uniform vec3 origin;\n"
32 "uniform vec2 glyph;\n"
33 "uniform vec2 dims;\n"
34 "uniform mat2 transform;\n"
35 "varying vec2 texCoord;\n"
36
37 "void main() {\n"
38 " texCoord = (glyph + offset * dims) / 512.0;\n"
39 " vec2 scaledOffset = (transform * (offset * 2.0 - vec2(1.0)) + vec2(1.0)) / 2.0 * dims;\n"
40 " gl_Position = vec4((origin.x + scaledOffset.x) / 640.0 - 1.0, -(origin.y + scaledOffset.y) / 360.0 + 1.0, origin.z, 1.0);\n"
41 "}";
42
43static const char* const _fragmentShader =
44 "varying vec2 texCoord;\n"
45 "uniform sampler2D tex;\n"
46 "uniform vec4 color;\n"
47 "uniform float cutoff;\n"
48
49 "void main() {\n"
50 " vec4 texColor = texture2D(tex, texCoord);\n"
51 " texColor.a = clamp((texColor.a - cutoff) / (1.0 - cutoff), 0.0, 1.0);\n"
52 " texColor.rgb = color.rgb;\n"
53 " texColor.a *= color.a;\n"
54 " gl_FragColor = texColor;\n"
55 "}";
56
57struct GUIFont {
58 GLuint font;
59 GLuint program;
60 GLuint vbo;
61 GLuint vao;
62 GLuint texLocation;
63 GLuint dimsLocation;
64 GLuint transformLocation;
65 GLuint colorLocation;
66 GLuint originLocation;
67 GLuint glyphLocation;
68 GLuint cutoffLocation;
69};
70
71static bool _loadTexture(const char* path) {
72 struct VFile* vf = VFileOpen(path, O_RDONLY);
73 if (!vf) {
74 return false;
75 }
76 png_structp png = PNGReadOpen(vf, 0);
77 png_infop info = png_create_info_struct(png);
78 png_infop end = png_create_info_struct(png);
79 bool success = false;
80 if (png && info && end) {
81 success = PNGReadHeader(png, info);
82 }
83 void* pixels = NULL;
84 if (success) {
85 unsigned height = png_get_image_height(png, info);
86 unsigned width = png_get_image_width(png, info);
87 pixels = malloc(width * height);
88 if (pixels) {
89 success = PNGReadPixels8(png, info, pixels, width, height, width);
90 success = success && PNGReadFooter(png, end);
91 } else {
92 success = false;
93 }
94 if (success) {
95 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, pixels);
96 }
97 }
98 PNGReadClose(png, info, end);
99 if (pixels) {
100 free(pixels);
101 }
102 vf->close(vf);
103 return success;
104}
105
106struct GUIFont* GUIFontCreate(void) {
107 struct GUIFont* font = malloc(sizeof(struct GUIFont));
108 if (!font) {
109 return NULL;
110 }
111 glGenTextures(1, &font->font);
112 glActiveTexture(GL_TEXTURE0);
113 glBindTexture(GL_TEXTURE_2D, font->font);
114 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
115 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
116 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
117 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
118 if (!_loadTexture("romfs:/font-new.png")) {
119 GUIFontDestroy(font);
120 return NULL;
121 }
122
123 font->program = glCreateProgram();
124 GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
125 GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
126 const GLchar* shaderBuffer[2];
127
128 shaderBuffer[0] = _gles2Header;
129
130 shaderBuffer[1] = _vertexShader;
131 glShaderSource(vertexShader, 2, shaderBuffer, NULL);
132
133 shaderBuffer[1] = _fragmentShader;
134 glShaderSource(fragmentShader, 2, shaderBuffer, NULL);
135
136 glAttachShader(font->program, vertexShader);
137 glAttachShader(font->program, fragmentShader);
138
139 glCompileShader(fragmentShader);
140
141 GLint success;
142 glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
143 if (!success) {
144 GLchar msg[512];
145 glGetShaderInfoLog(fragmentShader, sizeof(msg), NULL, msg);
146 puts(msg);
147 }
148
149 glCompileShader(vertexShader);
150
151 glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
152 if (!success) {
153 GLchar msg[512];
154 glGetShaderInfoLog(vertexShader, sizeof(msg), NULL, msg);
155 puts(msg);
156 }
157 glLinkProgram(font->program);
158
159 glDeleteShader(vertexShader);
160 glDeleteShader(fragmentShader);
161
162 font->texLocation = glGetUniformLocation(font->program, "tex");
163 font->colorLocation = glGetUniformLocation(font->program, "color");
164 font->dimsLocation = glGetUniformLocation(font->program, "dims");
165 font->transformLocation = glGetUniformLocation(font->program, "transform");
166 font->originLocation = glGetUniformLocation(font->program, "origin");
167 font->glyphLocation = glGetUniformLocation(font->program, "glyph");
168 font->cutoffLocation = glGetUniformLocation(font->program, "cutoff");
169 GLuint offsetLocation = glGetAttribLocation(font->program, "offset");
170
171 glGenBuffers(1, &font->vbo);
172 glGenVertexArrays(1, &font->vao);
173 glBindVertexArray(font->vao);
174 glBindBuffer(GL_ARRAY_BUFFER, font->vbo);
175 glBufferData(GL_ARRAY_BUFFER, sizeof(_offsets), _offsets, GL_STATIC_DRAW);
176 glVertexAttribPointer(offsetLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
177 glEnableVertexAttribArray(offsetLocation);
178 glBindVertexArray(0);
179
180 return font;
181}
182
183void GUIFontDestroy(struct GUIFont* font) {
184 glDeleteBuffers(1, &font->vbo);
185 glDeleteProgram(font->program);
186 glDeleteTextures(1, &font->font);
187 glDeleteVertexArrays(1, &font->vao);
188 free(font);
189}
190
191unsigned GUIFontHeight(const struct GUIFont* font) {
192 UNUSED(font);
193 return GLYPH_HEIGHT;
194}
195
196unsigned GUIFontGlyphWidth(const struct GUIFont* font, uint32_t glyph) {
197 UNUSED(font);
198 if (glyph > 0x7F) {
199 glyph = '?';
200 }
201 return defaultFontMetrics[glyph].width * 2;
202}
203
204void GUIFontIconMetrics(const struct GUIFont* font, enum GUIIcon icon, unsigned* w, unsigned* h) {
205 UNUSED(font);
206 if (icon >= GUI_ICON_MAX) {
207 if (w) {
208 *w = 0;
209 }
210 if (h) {
211 *h = 0;
212 }
213 } else {
214 if (w) {
215 *w = defaultIconMetrics[icon].width * 2;
216 }
217 if (h) {
218 *h = defaultIconMetrics[icon].height * 2;
219 }
220 }
221}
222
223void GUIFontDrawGlyph(const struct GUIFont* font, int x, int y, uint32_t color, uint32_t glyph) {
224 if (glyph > 0x7F) {
225 glyph = '?';
226 }
227 struct GUIFontGlyphMetric metric = defaultFontMetrics[glyph];
228
229 glUseProgram(font->program);
230 glBindVertexArray(font->vao);
231 glActiveTexture(GL_TEXTURE0);
232 glBindTexture(GL_TEXTURE_2D, font->font);
233
234 glEnable(GL_BLEND);
235 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
236
237 glUniform1i(font->texLocation, 0);
238 glUniform2f(font->glyphLocation, (glyph & 15) * CELL_WIDTH + metric.padding.left * 2, (glyph >> 4) * CELL_HEIGHT + metric.padding.top * 2);
239 glUniform2f(font->dimsLocation, CELL_WIDTH - (metric.padding.left + metric.padding.right) * 2, CELL_HEIGHT - (metric.padding.top + metric.padding.bottom) * 2);
240 glUniform3f(font->originLocation, x, y - GLYPH_HEIGHT + metric.padding.top * 2, 0);
241 glUniformMatrix2fv(font->transformLocation, 1, GL_FALSE, (float[4]) {1.0, 0.0, 0.0, 1.0});
242
243 glUniform1f(font->cutoffLocation, 0.1f);
244 glUniform4f(font->colorLocation, 0.0, 0.0, 0.0, ((color >> 24) & 0xFF) / 128.0f);
245 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
246
247 glUniform1f(font->cutoffLocation, 0.7f);
248 glUniform4f(font->colorLocation, (color & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f, ((color >> 16) & 0xFF) / 255.0f, ((color >> 24) & 0xFF) / 255.0f);
249 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
250
251 glBindVertexArray(0);
252 glUseProgram(0);
253}
254
255void GUIFontDrawIcon(const struct GUIFont* font, int x, int y, enum GUIAlignment align, enum GUIOrientation orient, uint32_t color, enum GUIIcon icon) {
256 if (icon >= GUI_ICON_MAX) {
257 return;
258 }
259 struct GUIIconMetric metric = defaultIconMetrics[icon];
260
261 float hFlip = 1.0f;
262 float vFlip = 1.0f;
263 switch (align & GUI_ALIGN_HCENTER) {
264 case GUI_ALIGN_HCENTER:
265 x -= metric.width;
266 break;
267 case GUI_ALIGN_RIGHT:
268 x -= metric.width * 2;
269 break;
270 }
271 switch (align & GUI_ALIGN_VCENTER) {
272 case GUI_ALIGN_VCENTER:
273 y -= metric.height;
274 break;
275 case GUI_ALIGN_BOTTOM:
276 y -= metric.height * 2;
277 break;
278 }
279
280 glUseProgram(font->program);
281 switch (orient) {
282 case GUI_ORIENT_HMIRROR:
283 hFlip = -1.0;
284 break;
285 case GUI_ORIENT_VMIRROR:
286 vFlip = -1.0;
287 break;
288 case GUI_ORIENT_0:
289 default:
290 // TODO: Rotate
291 break;
292 }
293
294 glUseProgram(font->program);
295 glBindVertexArray(font->vao);
296 glActiveTexture(GL_TEXTURE0);
297 glBindTexture(GL_TEXTURE_2D, font->font);
298
299 glEnable(GL_BLEND);
300 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
301
302 glUniform1i(font->texLocation, 0);
303 glUniform2f(font->glyphLocation, metric.x * 2, metric.y * 2 + 256);
304 glUniform2f(font->dimsLocation, metric.width * 2, metric.height * 2);
305 glUniform3f(font->originLocation, x, y, 0);
306 glUniformMatrix2fv(font->transformLocation, 1, GL_FALSE, (float[4]) {hFlip, 0.0, 0.0, vFlip});
307
308 glUniform1f(font->cutoffLocation, 0.1f);
309 glUniform4f(font->colorLocation, 0.0, 0.0, 0.0, ((color >> 24) & 0xFF) / 128.0f);
310 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
311
312 glUniform1f(font->cutoffLocation, 0.7f);
313 glUniform4f(font->colorLocation, (color & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f, ((color >> 16) & 0xFF) / 255.0f, ((color >> 24) & 0xFF) / 255.0f);
314 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
315
316 glBindVertexArray(0);
317 glUseProgram(0);
318}
319
320void GUIFontDrawIconSize(const struct GUIFont* font, int x, int y, int w, int h, uint32_t color, enum GUIIcon icon) {
321 if (icon >= GUI_ICON_MAX) {
322 return;
323 }
324 struct GUIIconMetric metric = defaultIconMetrics[icon];
325
326 if (!w) {
327 w = metric.width * 2;
328 }
329 if (!h) {
330 h = metric.height * 2;
331 }
332
333 glUseProgram(font->program);
334 glBindVertexArray(font->vao);
335 glActiveTexture(GL_TEXTURE0);
336 glBindTexture(GL_TEXTURE_2D, font->font);
337
338 glEnable(GL_BLEND);
339 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
340
341 glUniform1i(font->texLocation, 0);
342 glUniform2f(font->glyphLocation, metric.x * 2, metric.y * 2 + 256);
343 glUniform2f(font->dimsLocation, metric.width * 2, metric.height * 2);
344 glUniform3f(font->originLocation, x + w / 2 - metric.width, y + h / 2 - metric.height, 0);
345 glUniformMatrix2fv(font->transformLocation, 1, GL_FALSE, (float[4]) {w * 0.5f / metric.width, 0.0, 0.0, h * 0.5f / metric.height});
346
347 glUniform1f(font->cutoffLocation, 0.1f);
348 glUniform4f(font->colorLocation, 0.0, 0.0, 0.0, ((color >> 24) & 0xFF) / 128.0f);
349 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
350
351 glUniform1f(font->cutoffLocation, 0.7f);
352 glUniform4f(font->colorLocation, ((color >> 16) & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f, (color & 0xFF) / 255.0f, ((color >> 24) & 0xFF) / 255.0f);
353 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
354
355 glBindVertexArray(0);
356 glUseProgram(0);
357}