all repos — mgba @ cf61eb52a40f5258ae6afae6e4acb01650a9d5c9

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 <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 & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f, ((color >> 16) & 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 & 0xFF) / 255.0f, ((color >> 8) & 0xFF) / 255.0f, ((color >> 16) & 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}