view nuklear_ui/font_android.c @ 1971:80920c21bb52

Add an event log soft flush and call it twice per frame in between hard flushes to netplay latency when there are insufficient hardware updates to flush packets in the middle of a frame
author Michael Pavone <pavone@retrodev.com>
date Fri, 08 May 2020 11:40:30 -0700
parents 78abbabfd58d
children 13abdc98379e c649bcc18487
line wrap: on
line source

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <limits.h>
#include "../util.h"
#include "../paths.h"
#include "sfnt.h"

typedef enum {
	STATE_DEFAULT,
	STATE_DECL,
	STATE_COMMENT,
	STATE_TAG,
	STATE_PRE_ATTRIB,
	STATE_ATTRIB,
	STATE_PRE_VALUE,
	STATE_VALUE
} parse_state;

#define DEFAULT_WEIGHT 400

char *default_font_path(void)
{
	//Would probably be better to call into Java for this, but this should do for now
	FILE *f = fopen("/system/etc/fonts.xml", "rb");
	if (!f) {
		return NULL;
	}
	long size = file_size(f);
	char *font_xml = malloc(size+1);
	if (size != fread(font_xml, 1, size, f)) {
		free(font_xml);
		fclose(f);
		return NULL;
	}
	fclose(f);
	font_xml[size] = 0;
	
	char *last_tag = NULL, *last_attrib = NULL, *last_value = NULL;
	uint8_t last_style_was_normal = 0;
	char *capture_best = NULL;
	char *best = NULL;
	int best_weight_diff = INT_MAX;
	int last_weight = INT_MAX;
	parse_state state = STATE_DEFAULT;
	for(char *cur = font_xml; *cur; ++cur) {
		switch (state)
		{
		case STATE_DEFAULT:
			if (*cur == '<' && cur[1]) {
				cur++;
				switch(*cur)
				{
				case '?':
					state = STATE_DECL;
					break;
				case '!':
					if (cur[1] == '-' && cur[2] == '-') {
						state = STATE_COMMENT;
						cur++;
					} else {
						debug_message("Invalid comment\n");
						cur = font_xml + size - 1;
					}
					break;
				default:
					if (capture_best) {
						cur[-1] = 0;
						best = strip_ws(capture_best);
						capture_best = NULL;
						best_weight_diff = abs(last_weight - DEFAULT_WEIGHT);
						debug_message("Found candidate %s with weight %d\n", best, last_weight);
					}
					state = STATE_TAG;
					last_tag = cur;
					last_attrib = NULL;
					last_value = NULL;
					last_weight = INT_MAX;
					break;
				}
			}
			break;
		case STATE_DECL:
			if (*cur == '?' && cur[1] == '>') {
				cur++;
				state = STATE_DEFAULT;
			}
			break;
		case STATE_COMMENT:
			if (*cur == '-' && cur[1] == '-' && cur[2] == '>') {
				cur += 2;
				state = STATE_DEFAULT;
			}
			break;
		case STATE_TAG:
			if (*cur == ' ' || *cur == '\t' || *cur == '\n' || *cur == '\r') {
				*cur = 0;
				state = STATE_PRE_ATTRIB;
			} else if (*cur == '>') {
				*cur = 0;
				state = STATE_DEFAULT;
			}
			break;
		case STATE_PRE_ATTRIB:
			if (!(*cur == ' ' || *cur == '\t' || *cur == '\n' || *cur == '\r')) {
				if (*cur == '>') {
					state = STATE_DEFAULT;
					if (last_style_was_normal && abs(last_weight - DEFAULT_WEIGHT) < best_weight_diff) {
						capture_best = cur + 1;
					} else if (best && !strcmp("/family", last_tag)) {
						debug_message("found family close tag, stopping search\n");
						cur = font_xml + size - 1;
					}
				} else {
					last_attrib = cur;
					state = STATE_ATTRIB;
				}
			}
			break;
		case STATE_ATTRIB:
			if (*cur == '=') {
				*cur = 0;
				state = STATE_PRE_VALUE;
			} else if (*cur == ' ' || *cur == '\t' || *cur == '\n' || *cur == '\r') {
				*cur = 0;
			}
			break;
		case STATE_PRE_VALUE:
			if (*cur == '"') {
				state = STATE_VALUE;
				last_value = cur + 1;
			}
			break;
		case STATE_VALUE:
			if (*cur == '"') {
				*cur = 0;
				state = STATE_PRE_ATTRIB;
				if (!strcmp("weight", last_attrib)) {
					last_weight = atoi(last_value);
				} else if (!strcmp("style", last_attrib)) {
					last_style_was_normal = !strcmp("normal", last_value);
				}
			}
			break;
		}
	}
	if (best) {
		best = path_append("/system/fonts", best);
	}
	free(font_xml);
	return best;
}

static uint8_t *try_load_font(char *path, uint32_t *size_out)
{
	debug_message("Trying to load font %s\n", path);
	FILE *f = fopen(path, "rb");
	free(path);
	if (!f) {
		return NULL;
	}
	long size = file_size(f);
	uint8_t *buffer = malloc(size);
	if (size != fread(buffer, 1, size, f)) {
		fclose(f);
		return NULL;
	}
	fclose(f);
	sfnt_container *sfnt = load_sfnt(buffer, size);
	if (!sfnt) {
		free(buffer);
		return NULL;
	}
	return sfnt_flatten(sfnt->tables, size_out);
}

uint8_t *default_font(uint32_t *size_out)
{
	char *path = default_font_path();
	if (!path) {
		goto error;
	}
	uint8_t *ret = try_load_font(path, size_out);
	if (ret) {
		return ret;
	}
error:
	//try some likely suspects if we failed to parse fonts.xml or failed to find the indicated font
	ret = try_load_font("/system/fonts/Roboto-Regular.ttf", size_out);
	if (!ret) {
		ret = try_load_font("/system/fonts/DroidSans.ttf", size_out);
	}
	return ret;
}