all repos — mgba @ f6755a6e1b7b0cf2b944cd8ca842746f11d6bf82

mGBA Game Boy Advance Emulator

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}