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 <GLES2/gl2.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 offsetLocation;
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_NEAREST);
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 font->offsetLocation = glGetAttribLocation(font->program, "offset");
170
171 glGenBuffers(1, &font->vbo);
172 glBindBuffer(GL_ARRAY_BUFFER, font->vbo);
173 glBufferData(GL_ARRAY_BUFFER, sizeof(_offsets), _offsets, GL_STATIC_DRAW);
174 glBindBuffer(GL_ARRAY_BUFFER, 0);
175
176 return font;
177}
178
179void GUIFontDestroy(struct GUIFont* font) {
180 glDeleteBuffers(1, &font->vbo);
181 glDeleteProgram(font->program);
182 glDeleteTextures(1, &font->font);
183 free(font);
184}
185
186unsigned GUIFontHeight(const struct GUIFont* font) {
187 UNUSED(font);
188 return GLYPH_HEIGHT;
189}
190
191unsigned GUIFontGlyphWidth(const struct GUIFont* font, uint32_t glyph) {
192 UNUSED(font);
193 if (glyph > 0x7F) {
194 glyph = '?';
195 }
196 return defaultFontMetrics[glyph].width * 2;
197}
198
199void GUIFontIconMetrics(const struct GUIFont* font, enum GUIIcon icon, unsigned* w, unsigned* h) {
200 UNUSED(font);
201 if (icon >= GUI_ICON_MAX) {
202 if (w) {
203 *w = 0;
204 }
205 if (h) {
206 *h = 0;
207 }
208 } else {
209 if (w) {
210 *w = defaultIconMetrics[icon].width * 2;
211 }
212 if (h) {
213 *h = defaultIconMetrics[icon].height * 2;
214 }
215 }
216}
217
218void GUIFontDrawGlyph(const struct GUIFont* font, int x, int y, uint32_t color, uint32_t glyph) {
219 if (glyph > 0x7F) {
220 glyph = '?';
221 }
222 struct GUIFontGlyphMetric metric = defaultFontMetrics[glyph];
223
224 glUseProgram(font->program);
225 glActiveTexture(GL_TEXTURE0);
226 glBindTexture(GL_TEXTURE_2D, font->font);
227 glBindBuffer(GL_ARRAY_BUFFER, font->vbo);
228
229 glEnable(GL_BLEND);
230 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
231
232 glUniform1i(font->texLocation, 0);
233 glUniform2f(font->glyphLocation, (glyph & 15) * CELL_WIDTH + metric.padding.left * 2, (glyph >> 4) * CELL_HEIGHT + metric.padding.top * 2);
234 glUniform2f(font->dimsLocation, CELL_WIDTH - (metric.padding.left + metric.padding.right) * 2, CELL_HEIGHT - (metric.padding.top + metric.padding.bottom) * 2);
235 glUniform3f(font->originLocation, x, y - GLYPH_HEIGHT + metric.padding.top * 2, 0);
236 glUniformMatrix2fv(font->transformLocation, 1, GL_FALSE, (float[4]) {1.0, 0.0, 0.0, 1.0});
237
238 glVertexAttribPointer(font->offsetLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
239 glEnableVertexAttribArray(font->offsetLocation);
240
241 glUniform1f(font->cutoffLocation, 0.1f);
242 glUniform4f(font->colorLocation, 0.0, 0.0, 0.0, ((color >> 24) & 0xFF) / 128.0f);
243 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
244
245 glUniform1f(font->cutoffLocation, 0.7f);
246 glUniform4f(font->colorLocation, ((color >> 16) & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f, (color & 0xFF) / 255.0f, ((color >> 24) & 0xFF) / 255.0f);
247 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
248
249 glDisableVertexAttribArray(font->offsetLocation);
250 glBindBuffer(GL_ARRAY_BUFFER, 0);
251 glUseProgram(0);
252}
253
254void GUIFontDrawIcon(const struct GUIFont* font, int x, int y, enum GUIAlignment align, enum GUIOrientation orient, uint32_t color, enum GUIIcon icon) {
255 if (icon >= GUI_ICON_MAX) {
256 return;
257 }
258 struct GUIIconMetric metric = defaultIconMetrics[icon];
259
260 float hFlip = 1.0f;
261 float vFlip = 1.0f;
262 switch (align & GUI_ALIGN_HCENTER) {
263 case GUI_ALIGN_HCENTER:
264 x -= metric.width;
265 break;
266 case GUI_ALIGN_RIGHT:
267 x -= metric.width * 2;
268 break;
269 }
270 switch (align & GUI_ALIGN_VCENTER) {
271 case GUI_ALIGN_VCENTER:
272 y -= metric.height;
273 break;
274 case GUI_ALIGN_BOTTOM:
275 y -= metric.height * 2;
276 break;
277 }
278
279 glUseProgram(font->program);
280 switch (orient) {
281 case GUI_ORIENT_HMIRROR:
282 hFlip = -1.0;
283 break;
284 case GUI_ORIENT_VMIRROR:
285 vFlip = -1.0;
286 break;
287 case GUI_ORIENT_0:
288 default:
289 // TODO: Rotate
290 break;
291 }
292
293 glUseProgram(font->program);
294 glActiveTexture(GL_TEXTURE0);
295 glBindTexture(GL_TEXTURE_2D, font->font);
296 glBindBuffer(GL_ARRAY_BUFFER, font->vbo);
297
298 glEnable(GL_BLEND);
299 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
300
301 glUniform1i(font->texLocation, 0);
302 glUniform2f(font->glyphLocation, metric.x * 2, metric.y * 2 + 256);
303 glUniform2f(font->dimsLocation, metric.width * 2, metric.height * 2);
304 glUniform3f(font->originLocation, x, y, 0);
305 glUniformMatrix2fv(font->transformLocation, 1, GL_FALSE, (float[4]) {hFlip, 0.0, 0.0, vFlip});
306
307 glVertexAttribPointer(font->offsetLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
308 glEnableVertexAttribArray(font->offsetLocation);
309
310 glUniform1f(font->cutoffLocation, 0.1f);
311 glUniform4f(font->colorLocation, 0.0, 0.0, 0.0, ((color >> 24) & 0xFF) / 128.0f);
312 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
313
314 glUniform1f(font->cutoffLocation, 0.7f);
315 glUniform4f(font->colorLocation, ((color >> 16) & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f, (color & 0xFF) / 255.0f, ((color >> 24) & 0xFF) / 255.0f);
316 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
317
318 glDisableVertexAttribArray(font->offsetLocation);
319 glBindBuffer(GL_ARRAY_BUFFER, 0);
320 glUseProgram(0);
321}
322
323void GUIFontDrawIconSize(const struct GUIFont* font, int x, int y, int w, int h, uint32_t color, enum GUIIcon icon) {
324 if (icon >= GUI_ICON_MAX) {
325 return;
326 }
327 struct GUIIconMetric metric = defaultIconMetrics[icon];
328
329 if (!w) {
330 w = metric.width * 2;
331 }
332 if (!h) {
333 h = metric.height * 2;
334 }
335
336 glUseProgram(font->program);
337 glActiveTexture(GL_TEXTURE0);
338 glBindTexture(GL_TEXTURE_2D, font->font);
339 glBindBuffer(GL_ARRAY_BUFFER, font->vbo);
340
341 glEnable(GL_BLEND);
342 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
343
344 glUniform1i(font->texLocation, 0);
345 glUniform2f(font->glyphLocation, metric.x * 2, metric.y * 2 + 256);
346 glUniform2f(font->dimsLocation, metric.width * 2, metric.height * 2);
347 glUniform3f(font->originLocation, x + w / 2 - metric.width, y + h / 2 - metric.height, 0);
348 glUniformMatrix2fv(font->transformLocation, 1, GL_FALSE, (float[4]) {w * 0.5f / metric.width, 0.0, 0.0, h * 0.5f / metric.height});
349
350 glVertexAttribPointer(font->offsetLocation, 2, GL_FLOAT, GL_FALSE, 0, NULL);
351 glEnableVertexAttribArray(font->offsetLocation);
352
353 glUniform1f(font->cutoffLocation, 0.1f);
354 glUniform4f(font->colorLocation, 0.0, 0.0, 0.0, ((color >> 24) & 0xFF) / 128.0f);
355 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
356
357 glUniform1f(font->cutoffLocation, 0.7f);
358 glUniform4f(font->colorLocation, ((color >> 16) & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f, (color & 0xFF) / 255.0f, ((color >> 24) & 0xFF) / 255.0f);
359 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
360
361 glDisableVertexAttribArray(font->offsetLocation);
362 glBindBuffer(GL_ARRAY_BUFFER, 0);
363 glUseProgram(0);
364}