Various improvements to SengokuRanceFont/fnl

This commit is contained in:
Nunuhara Cabbage
2020-07-30 20:55:17 -07:00
parent a8b2e91053
commit c2aae50dca
6 changed files with 224 additions and 130 deletions
+16 -1
View File
@@ -183,7 +183,22 @@ struct fnl_font_inst {
int outline_size;
};
struct text_style {
unsigned font_type;
float size;
float bold_width;
SDL_Color color;
float edge_width;
SDL_Color edge_color;
float scale_x;
float space_scale_x;
float font_spacing;
struct fnl_font_size *font_size;
};
struct fnl;
struct fnl_font_face;
int fnl_draw_text(struct fnl_font_inst *font, Texture *dst, int x, int y, char *text);
int fnl_draw_text(struct fnl *fnl, struct text_style *ts, Texture *dst, int x, int y, char *text);
#endif /* SYSTEM4_SDL_CORE_H */
+17 -3
View File
@@ -18,39 +18,53 @@
#define SYSTEM4_FNL_H
#include <stdint.h>
#include <stdio.h>
struct fnl {
uint8_t *data;
uint32_t filesize;
uint32_t uk;
uint32_t data_offset;
uint32_t nr_fonts;
struct fnl_font *fonts;
};
struct fnl_font {
struct fnl *fnl;
uint32_t nr_faces;
struct fnl_font_face *faces;
uint32_t nr_sizes;
struct fnl_font_size *sizes;
};
struct fnl_font_size {
unsigned face;
float size;
unsigned denominator;
};
struct fnl_font_face {
struct fnl_font *font;
uint32_t height;
uint32_t uk;
uint32_t nr_glyphs;
struct fnl_glyph *glyphs;
float _sizes[11];
};
struct fnl_glyph {
uint32_t height;
uint16_t real_width;
uint32_t data_pos;
uint32_t data_compsize;
// uncompressed data
unsigned long data_size;
uint8_t *data;
};
struct fnl *fnl_open(const char *path);
void fnl_free(struct fnl *fnl);
struct fnl_glyph *fnl_get_glyph(struct fnl_font_face *font, uint16_t code);
uint8_t *fnl_glyph_data(struct fnl *fnl, struct fnl_glyph *g, unsigned long *size);
struct fnl_font_size *fnl_get_font_size(struct fnl_font *font, float size);
#endif /* SYSTEM4_FNL_H */
+54 -17
View File
@@ -16,6 +16,7 @@
#include <stdint.h>
#include <stdlib.h>
#include <math.h>
#include <zlib.h>
#include "system4.h"
#include "system4/buffer.h"
@@ -54,28 +55,47 @@ struct fnl_glyph *fnl_get_glyph(struct fnl_font_face *font, uint16_t code)
if (index >= font->nr_glyphs)
index = 0;
if (!font->glyphs[index].data_size)
if (!font->glyphs[index].data_pos)
index = 0;
return &font->glyphs[index];
}
/*
* Get the closest font size.
*/
struct fnl_font_size *fnl_get_font_size(struct fnl_font *font, float size)
{
float min_diff = 9999;
struct fnl_font_size *closest = &font->sizes[0];
for (unsigned i = 0; i < font->nr_sizes; i++) {
float diff = fabsf(font->sizes[i].size - size);
if (diff < min_diff) {
min_diff = diff;
closest = &font->sizes[i];
}
}
return closest;
}
static void fnl_read_glyph(struct buffer *r, uint32_t height, struct fnl_glyph *dst)
{
dst->height = height;
dst->real_width = buffer_read_u16(r);
dst->data_pos = buffer_read_int32(r);
dst->data_compsize = buffer_read_int32(r);
}
if (!dst->data_pos) {
dst->data_size = 0;
dst->data = NULL;
return;
uint8_t *fnl_glyph_data(struct fnl *fnl, struct fnl_glyph *g, unsigned long *size)
{
if (!g->data_pos) {
*size = 0;
return NULL;
}
// uncompress...
dst->data_size = height * height * 4; // FIXME: determine real bound
dst->data = xmalloc(dst->data_size);
int rv = uncompress(dst->data, &dst->data_size, r->buf + dst->data_pos, dst->data_compsize);
*size = g->height * g->height * 4; // FIXME: determine real bound
uint8_t *data = xmalloc(*size);
int rv = uncompress(data, size, fnl->data + g->data_pos, g->data_compsize);
if (rv != Z_OK) {
if (rv == Z_BUF_ERROR)
ERROR("uncompress failed: Z_BUF_ERROR");
@@ -84,6 +104,7 @@ static void fnl_read_glyph(struct buffer *r, uint32_t height, struct fnl_glyph *
else if (rv == Z_DATA_ERROR)
ERROR("uncompress failed: Z_DATA_ERROR");
}
return data;
}
static void fnl_read_font_face(struct buffer *r, struct fnl_font_face *dst)
@@ -96,6 +117,10 @@ static void fnl_read_font_face(struct buffer *r, struct fnl_font_face *dst)
for (size_t i = 0; i < dst->nr_glyphs; i++) {
fnl_read_glyph(r, dst->height, &dst->glyphs[i]);
}
for (unsigned i = 0; i < 11; i++) {
dst->_sizes[i] = dst->height / (float)(i+2);
}
}
static void fnl_read_font(struct buffer *r, struct fnl_font *dst)
@@ -104,21 +129,35 @@ static void fnl_read_font(struct buffer *r, struct fnl_font *dst)
dst->faces = xcalloc(dst->nr_faces, sizeof(struct fnl_font_face));
for (size_t i = 0; i < dst->nr_faces; i++) {
dst->faces[i].font = dst;
fnl_read_font_face(r, &dst->faces[i]);
}
dst->nr_sizes = dst->nr_faces * 11;
dst->sizes = xcalloc(dst->nr_sizes, sizeof(struct fnl_font_size));
for (unsigned face = 0, s = 0; face < dst->nr_faces; face++) {
for (unsigned i = 0; i < 11; i++) {
dst->sizes[s++] = (struct fnl_font_size) {
.face = face,
.size = dst->faces[face]._sizes[i],
.denominator = i+2
};
}
}
}
struct fnl *fnl_open(const char *path)
{
size_t filesize;
struct fnl *fnl = xcalloc(1, sizeof(struct fnl));
uint8_t *data = file_read(path, &filesize);
fnl->data = file_read(path, &filesize);
if (data[0] != 'F' || data[1] != 'N' || data[2] != 'A' || data[3] != '\0')
if (fnl->data[0] != 'F' || fnl->data[1] != 'N' || fnl->data[2] != 'A' || fnl->data[3] != '\0')
goto err;
struct buffer r;
buffer_init(&r, data, filesize);
buffer_init(&r, fnl->data, filesize);
buffer_skip(&r, 4);
fnl->uk = buffer_read_int32(&r);
if (fnl->uk != 0)
@@ -130,13 +169,13 @@ struct fnl *fnl_open(const char *path)
fnl->fonts = xcalloc(fnl->nr_fonts, sizeof(struct fnl_font));
for (size_t i = 0; i < fnl->nr_fonts; i++) {
fnl->fonts[i].fnl = fnl;
fnl_read_font(&r, &fnl->fonts[i]);
}
free(data);
return fnl;
err:
free(fnl->data);
free(fnl);
free(data);
return NULL;
}
@@ -144,13 +183,11 @@ void fnl_free(struct fnl *fnl)
{
for (size_t font = 0; font < fnl->nr_fonts; font++) {
for (size_t face = 0; face < fnl->fonts[font].nr_faces; face++) {
for (size_t glyph = 0; glyph < fnl->fonts[font].faces[face].nr_glyphs; glyph++) {
free(fnl->fonts[font].faces[face].glyphs[glyph].data);
}
free(fnl->fonts[font].faces[face].glyphs);
}
free(fnl->fonts[font].faces);
}
free(fnl->fonts);
free(fnl->data);
free(fnl);
}
+30 -22
View File
@@ -31,68 +31,73 @@ static void set_pixel(uint8_t *p, int w, int h, int x, int y, SDL_Color c)
p[y*w*4 + x*4 + 3] = 255;
}
static void render_outline(struct fnl_font_inst *font, uint8_t *surf, struct fnl_glyph *glyph, int x, int w, int h)
static void render_outline(struct text_style *ts, uint8_t *surf, uint8_t *data, unsigned long data_size, int x, int w, int h)
{
const int span = w * 4;
const int glyph_w = (glyph->data_size*8) / h;
const int size = 1.0 / font->scale * font->outline_size;
const int glyph_w = (data_size*8) / h;
const int size = ts->font_size->denominator * ts->edge_width;
uint32_t p = 0;
for (int row = h - 1; row >= 0; row--) {
uint8_t *pos = surf + row*span + x*4;
for (int col = 0; col < glyph_w; col++, p++, pos += 4) {
bool on = glyph->data[p/8] & (1 << (7 - p % 8));
bool on = data[p/8] & (1 << (7 - p % 8));
if (!on)
continue;
int start_x = x + col - size;
int start_y = row - size;
for (int ol_row = 0; ol_row < size*2; ol_row++) {
for (int ol_col = 0; ol_col < size*2; ol_col++) {
set_pixel(surf, w, h, start_x + ol_col, start_y + ol_row, font->outline_color);
set_pixel(surf, w, h, start_x + ol_col, start_y + ol_row, ts->edge_color);
}
}
}
}
}
static void render_glyph(struct fnl_font_inst *font, uint8_t *surf, struct fnl_glyph *glyph, int x, int w, int h)
// TODO: cache glyph textures, render directly to dst texture
static void render_glyph(struct fnl_font_face *font, struct text_style *ts, uint8_t *surf, struct fnl_glyph *glyph, int x, int w, int h)
{
const int span = w * 4;
const int glyph_w = (glyph->data_size*8) / h;
unsigned long data_size;
uint8_t *data = fnl_glyph_data(font->font->fnl, glyph, &data_size);
if (font->outline_size)
render_outline(font, surf, glyph, x, w, h);
const int span = w * 4;
const int glyph_w = (data_size*8) / h;
if (ts->edge_width)
render_outline(ts, surf, data, data_size, x, w, h);
uint32_t p = 0;
for (int row = h - 1; row >= 0; row--) {
uint8_t *pos = surf + row*span + x*4;
for (int col = 0; col < glyph_w; col++, p++) {
bool on = glyph->data[p/8] & (1 << (7 - p % 8));
bool on = data[p/8] & (1 << (7 - p % 8));
if (on) {
set_pixel(surf, w, h, x+col, row, font->color);
set_pixel(surf, w, h, x+col, row, ts->color);
}
pos += 4;
}
}
free(data);
}
static void render_text(struct fnl_font_inst *font, Texture *dst, char *text)
static void render_text(struct fnl_font_face *font, struct text_style *ts, Texture *dst, char *text)
{
// create array of fnl_glyph
const int height = font->font->height;
const int height = font->height;
int len = sjis_count_char(text);
struct fnl_glyph **glyphs = xcalloc(len, sizeof(struct fnl_glyph*));
for (int i = 0; i < len; i++) {
glyphs[i] = fnl_get_glyph(font->font, sjis_code(text));
glyphs[i] = fnl_get_glyph(font, sjis_code(text));
text = sjis_skip_char(text);
}
// calculate width/height of text surface
int width = 0;
for (int i = 0; i < len; i++) {
//width += glyphs[i]->real_width;
width += (glyphs[i]->data_size*8)/height;
width += glyphs[i]->real_width;
}
// allocate memory for surface
@@ -101,12 +106,14 @@ static void render_text(struct fnl_font_inst *font, Texture *dst, char *text)
// render glyphs to surface
int x = 0;
for (int i = 0; i < len; i++) {
render_glyph(font, surf, glyphs[i], x, width, height);
render_glyph(font, ts, surf, glyphs[i], x, width, height);
x += glyphs[i]->real_width;
}
// create texture from surface
gfx_init_texture_with_pixels(dst, width, height, surf, GL_RGBA);
free(surf);
free(glyphs);
}
static void draw_text(Texture *dst, Texture *glyph, int x, int y, float scale)
@@ -138,16 +145,17 @@ static void draw_text(Texture *dst, Texture *glyph, int x, int y, float scale)
gfx_reset_framebuffer(GL_DRAW_FRAMEBUFFER, fbo);
}
int fnl_draw_text(struct fnl_font_inst *font, Texture *dst, int x, int y, char *text)
int fnl_draw_text(struct fnl *fnl, struct text_style *ts, Texture *dst, int x, int y, char *text)
{
Texture rendered;
render_text(font, &rendered, text);
struct fnl_font_face *font = &fnl->fonts[ts->font_type].faces[ts->font_size->face];
render_text(font, ts, &rendered, text);
//glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE);
draw_text(dst, &rendered, x, y, font->scale);
draw_text(dst, &rendered, x, y, 1.0 / ts->font_size->denominator);
//glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
int w = rendered.w;
int w = rendered.w / (float)ts->font_size->denominator;
gfx_delete_texture(&rendered);
return w;
}
+100 -84
View File
@@ -25,7 +25,6 @@
#include "gfx/gfx.h"
#include "vm/page.h"
#include "sact.h"
#include "kvec.h"
#include "hll.h"
#include "xsystem4.h"
@@ -43,14 +42,49 @@ struct text_metrics sr_tm = {
.outline_down = 1
};
kvec_t(struct text_metrics) sp_tm;
struct text_style *sp_ts;
int nr_ts = 0;
static struct text_metrics *get_sp_metrics(int sp_no)
static struct text_style *get_sp_metrics(int sp_no)
{
(void)kv_a(struct text_metrics, sp_tm, sp_no);
return &kv_A(sp_tm, sp_no);
if (sp_no > nr_ts) {
nr_ts = sp_no + 256;
sp_ts = xrealloc(sp_ts, nr_ts * sizeof(struct text_style));
}
return sp_ts + sp_no;
}
static unsigned get_font_type(int type)
{
unsigned font = type - 256;
if (font >= fontlib->nr_fonts) {
WARNING("Invalid font type: %d", type);
// XXX: Really not sure why this is how it works...
font = type == 1 ? 2 : 1;
}
return font;
}
/*
static unsigned get_font_face(int font, float height)
{
float min_diff = 9999;
unsigned closest = 0;
// FIXME: this isn't right; see comment for GetActualFontSize
for (unsigned i = 0; i < fontlib->fonts[font].nr_faces; i++) {
if (fontlib->fonts[font].faces[i].height == height)
return i;
float diff = fabsf(height - fontlib->fonts[font].faces[i].height);
if (diff < min_diff) {
min_diff = diff;
closest = i;
}
}
return closest;
}
*/
void SengokuRanceFont_AlphaComposite(int spriteNumberDest, int destX, int destY, int spriteNumberSrc, int srcX, int srcY, int w, int h);
void SengokuRanceFont_SetFontType(int type)
@@ -154,98 +188,76 @@ void SengokuRanceFont_SP_TextNewLine(int spriteNumber, float fontSize);
void SengokuRanceFont_SP_SetTextMetricsClassic(int sp_no, struct page *page)
{
struct text_metrics *tm = get_sp_metrics(sp_no);
tm->color.r = page->values[0].i;
tm->color.g = page->values[1].i;
tm->color.b = page->values[2].i;
tm->color.a = 255;
tm->size = page->values[3].i;
tm->weight = page->values[4].i;
//tm->face = page->values[5].i;
tm->face = FONT_MINCHO;
tm->outline_left = page->values[6].i;
tm->outline_up = page->values[7].i;
tm->outline_right = page->values[8].i;
tm->outline_down = page->values[9].i;
tm->outline_color.r = page->values[10].i;
tm->outline_color.g = page->values[11].i;
tm->outline_color.b = page->values[12].i;
tm->outline_color.a = 255;
struct text_style *ts = get_sp_metrics(sp_no);
ts->font_type = get_font_type(256);
ts->color.r = page->values[0].i;
ts->color.g = page->values[1].i;
ts->color.b = page->values[2].i;
ts->color.a = 255;
ts->size = page->values[3].i;
//ts->weight = page->values[4].i;
//ts->face = page->values[5].i;
ts->edge_width = max(page->values[6].i, max(page->values[7].i, max(page->values[8].i, page->values[9].i)));
ts->edge_color.r = page->values[10].i;
ts->edge_color.g = page->values[11].i;
ts->edge_color.b = page->values[12].i;
ts->edge_color.a = 255;
ts->bold_width = 0; // FIXME: use weight?
ts->scale_x = 1;
ts->space_scale_x = 1;
ts->font_spacing = 1;
ts->font_size = fnl_get_font_size(&fontlib->fonts[ts->font_type], ts->size);
}
static float boldwidth = 0;
void SengokuRanceFont_SP_SetTextStyle(int sp_no, struct page *ts)
void SengokuRanceFont_SP_SetTextStyle(int sp_no, struct page *page)
{
struct text_metrics *tm = get_sp_metrics(sp_no);
// FontType
tm->weight = FW_NORMAL;
tm->face = FONT_MINCHO;
tm->size = ts->values[1].f;
// BoldWidth
boldwidth = ts->values[2].f;
tm->color.r = ts->values[3].i;
tm->color.g = ts->values[4].i;
tm->color.b = ts->values[5].i;
tm->color.a = 255;
tm->outline_left = ts->values[6].f;
tm->outline_up = ts->values[6].f;
tm->outline_right = ts->values[6].f;
tm->outline_down = ts->values[6].f;
tm->outline_color.r = ts->values[7].i;
tm->outline_color.g = ts->values[8].i;
tm->outline_color.b = ts->values[9].i;
tm->outline_color.a = 255;
// ScaleX
// SpaceScaleX
// FontSpacing
}
struct text_style *ts = get_sp_metrics(sp_no);
ts->font_type = get_font_type(page->values[0].i < 0 ? 256 : page->values[0].i);
ts->size = page->values[1].f;
ts->bold_width = page->values[2].f < 0 ? 0 : page->values[2].f;
ts->color.r = page->values[3].i < 0 ? 255 : page->values[3].i;
ts->color.g = page->values[4].i < 0 ? 255 : page->values[4].i;
ts->color.b = page->values[5].i < 0 ? 255 : page->values[5].i;
ts->color.a = 255;
ts->edge_width = page->values[6].f < 0 ? 0 : page->values[6].f;
ts->edge_color.r = page->values[7].i < 0 ? 0 : page->values[7].i;
ts->edge_color.g = page->values[8].i < 0 ? 0 : page->values[8].i;
ts->edge_color.b = page->values[9].i < 0 ? 0 : page->values[9].i;
ts->edge_color.a = 255;
ts->scale_x = page->values[10].f < 0 ? 1 : page->values[10].f;
ts->space_scale_x = page->values[11].f < 0 ? 1 : page->values[11].f;
ts->font_spacing = page->values[12].f < 0 ? 1 : page->values[12].f;
ts->font_size = fnl_get_font_size(&fontlib->fonts[ts->font_type], ts->size);
void SengokuRanceFont_SP_TextDrawClassic(int sp_no, struct string *text)
{
_sact_SP_TextDraw(sp_no, text, &sr_tm);
}
static struct fnl_font_face *get_font(uint32_t height)
{
int min_diff = 9999;
struct fnl_font_face *closest = NULL;
for (uint32_t i = 0; i < fontlib->fonts[0].nr_faces; i++) {
if (fontlib->fonts[0].faces[i].height == height)
return &fontlib->fonts[0].faces[i];
int diff = abs((int)height - (int)fontlib->fonts[0].faces[i].height);
if (diff < min_diff) {
min_diff = diff;
closest = &fontlib->fonts[0].faces[i];
}
if (ts->scale_x != 1.0) {
WARNING("X SCALE = %f", ts->scale_x);
}
return closest;
}
void SengokuRanceFont_SP_TextDraw(int sp_no, struct string *text)
{
struct sact_sprite *sp = sact_get_sprite(sp_no);
struct text_metrics *tm = get_sp_metrics(sp_no);
struct fnl_font_face *font = get_font(tm->size);
if (!font) {
WARNING("No font for size %u", tm->size);
return;
struct text_style *ts = get_sp_metrics(sp_no);
// in case of uninitialized text style...
if (ts->font_type >= fontlib->nr_fonts) {
ts->font_type = 0;
}
if (ts->size < 8) {
ts->size = 8;
}
if (!ts->font_size) {
ts->font_size = fnl_get_font_size(&fontlib->fonts[ts->font_type], ts->size);
}
if (!sp->text.texture.handle) {
SDL_Color c = tm->color;
SDL_Color c = ts->color;
c.a = 0;
gfx_init_texture_with_color(&sp->text.texture, sp->rect.w, sp->rect.h, c);
}
struct fnl_font_inst inst = {
.font = font,
.scale = 1.0 / (font->height / tm->size),
.color = tm->color,
.outline_color = tm->outline_color,
.outline_size = tm->outline_left
};
sp->text.pos.x += fnl_draw_text(&inst, &sp->text.texture, sp->text.pos.x, sp->text.pos.y, text->text);
sp->text.pos.x += fnl_draw_text(fontlib, ts, &sp->text.texture, sp->text.pos.x, sp->text.pos.y, text->text);
}
void SengokuRanceFont_SP_TextDrawPreload(int spriteNumber, struct string *text);
@@ -266,9 +278,10 @@ float SengokuRanceFont_SP_GetEdgeWidth(int spriteNumber);
float SengokuRanceFont_SP_GetScaleX(int spriteNumber);
float SengokuRanceFont_SP_GetSpaceScaleX(int spriteNumber);
float SengokuRanceFont_GetActualFontSize(int fontType, float fontSize)
float SengokuRanceFont_GetActualFontSize(int font_type, float font_size)
{
return fontSize;
font_type = get_font_type(font_type);
return fnl_get_font_size(&fontlib->fonts[font_type], font_size)->size;
}
float SengokuRanceFont_GetActualFontSizeRoundDown(int fontType, float fontSize);
@@ -278,7 +291,7 @@ int SengokuRanceFont_SP_GetReduceDescender(int spriteNumber);
void SengokuRanceFont_SP_SetReduceDescender(int spriteNumber, int reduceDescender)
{
WARNING("SengokuRanceFont.SP_SetReduceDescender(%d, %d)", spriteNumber, reduceDescender);
}
void SengokuRanceFont_ModuleInit(void)
@@ -300,6 +313,8 @@ void SengokuRanceFont_ModuleInit(void)
fontlib = fnl_open(path);
if (!(fontlib = fnl_open(path)))
ERROR("Error opening font library '%s'", path);
if (fontlib->nr_fonts < 1)
ERROR("Font library doesn't contain any fonts");
break;
}
@@ -344,7 +359,8 @@ HLL_LIBRARY(SengokuRanceFont,
//HLL_EXPORT(SP_TextNewLine, SengokuRanceFont_SP_TextNewLine),
HLL_EXPORT(SP_SetTextMetricsClassic, SengokuRanceFont_SP_SetTextMetricsClassic),
HLL_EXPORT(SP_SetTextStyle, SengokuRanceFont_SP_SetTextStyle),
HLL_EXPORT(SP_TextDrawClassic, SengokuRanceFont_SP_TextDrawClassic),
// XXX: I'm really not sure what the difference is supposed to be between these two
HLL_EXPORT(SP_TextDrawClassic, SengokuRanceFont_SP_TextDraw),
HLL_EXPORT(SP_TextDraw, SengokuRanceFont_SP_TextDraw),
//HLL_EXPORT(SP_TextDrawPreload, SengokuRanceFont_SP_TextDrawPreload),
//HLL_EXPORT(SP_SetFontSize, SengokuRanceFont_SP_SetFontSize),
+7 -3
View File
@@ -120,7 +120,7 @@ int main(int argc, char *argv[])
// extract glyphs
for (size_t g = 0; g < font_face->nr_glyphs; g++) {
struct fnl_glyph *glyph = &font_face->glyphs[g];
if (!glyph->data)
if (!glyph->data_pos)
continue;
sprintf(path, "%s/font_%u/face_%u/glyph_%u.webp", output_dir, (unsigned)font, (unsigned)face, (unsigned)g);
@@ -129,9 +129,13 @@ int main(int argc, char *argv[])
ERROR("fopen failed: %s", strerror(errno));
}
unsigned long data_size;
uint8_t *data = fnl_glyph_data(fnl, glyph, &data_size);
uint32_t height = font_face->height;
uint32_t width = (glyph->data_size*8) / height;
write_bitmap(f, width, height, glyph->data);
uint32_t width = (data_size*8) / height;
write_bitmap(f, width, height, data);
free(data);
fclose(f);
}
}