mirror of
https://github.com/nunuhara/xsystem4.git
synced 2026-09-22 23:18:01 +03:00
SengokuRanceFont: anti-aliasing and glyph caching
This improves the look and (especially) the performance of text rendering in the MangaGamer version of Sengoku Rance. There are still some bugs/unimplemented features, but the game should be more or less bearable to play in this state.
This commit is contained in:
+5
-1
@@ -25,6 +25,7 @@
|
||||
|
||||
struct cg;
|
||||
struct text_metrics;
|
||||
enum cg_type;
|
||||
|
||||
enum draw_method {
|
||||
DRAW_METHOD_NORMAL,
|
||||
@@ -88,12 +89,14 @@ void gfx_init_texture_with_cg(struct texture *t, struct cg *cg);
|
||||
void gfx_init_texture_rgba(struct texture *t, int w, int h, SDL_Color color);
|
||||
void gfx_init_texture_rgb(struct texture *t, int w, int h, SDL_Color color);
|
||||
void gfx_init_texture_with_pixels(struct texture *t, int w, int h, void *pixels);
|
||||
void gfx_init_texture_amap(struct texture *t, int w, int h, uint8_t *amap, SDL_Color color);
|
||||
void gfx_copy_main_surface(struct texture *dst);
|
||||
void gfx_delete_texture(struct texture *t);
|
||||
GLuint gfx_set_framebuffer(GLenum target, Texture *t, int x, int y, int w, int h);
|
||||
void gfx_reset_framebuffer(GLenum target, GLuint fbo);
|
||||
SDL_Color gfx_get_pixel(Texture *t, int x, int y);
|
||||
void *gfx_get_pixels(Texture *t);
|
||||
int gfx_save_texture(Texture *t, const char *path, enum cg_type);
|
||||
|
||||
// drawing
|
||||
void gfx_draw_init(void);
|
||||
@@ -224,7 +227,8 @@ struct text_style {
|
||||
|
||||
struct fnl;
|
||||
struct fnl_font_face;
|
||||
int fnl_draw_text(struct fnl *fnl, struct text_style *ts, Texture *dst, int x, int y, char *text);
|
||||
float fnl_draw_text(struct fnl *fnl, struct text_style *ts, Texture *dst, float x, int y, char *text);
|
||||
float fnl_size_text(struct fnl *fnl, struct text_style *ts, char *text);
|
||||
void fnl_renderer_free(struct fnl *fnl);
|
||||
|
||||
#endif /* SYSTEM4_SDL_CORE_H */
|
||||
|
||||
@@ -104,4 +104,6 @@ bool sprite_is_point_in_rect(struct sact_sprite *sp, int x, int y);
|
||||
int sprite_get_amap_value(struct sact_sprite *sp, int x, int y);
|
||||
void sprite_get_pixel_value(struct sact_sprite *sp, int x, int y, int *r, int *g, int *b);
|
||||
|
||||
void print_sprite(struct sact_sprite *sp);
|
||||
|
||||
#endif /* SYSTEM4_SPRITE_H */
|
||||
|
||||
+90
-114
@@ -14,6 +14,8 @@
|
||||
* along with this program; if not, see <http://gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "gfx/gfx.h"
|
||||
#include "gfx/types.h"
|
||||
#include "system4.h"
|
||||
@@ -21,110 +23,16 @@
|
||||
#include "system4/utfsjis.h"
|
||||
#include "gfx/gfx.h"
|
||||
|
||||
static void set_pixel(uint8_t *p, int w, int h, int x, int y, SDL_Color c)
|
||||
static void draw_glyph(Texture *dst, float x, int y, Texture *glyph, float scale_x, float scale_y)
|
||||
{
|
||||
if (x >= w || y >= h)
|
||||
return;
|
||||
p[y*w*4 + x*4 + 0] = c.r;
|
||||
p[y*w*4 + x*4 + 1] = c.g;
|
||||
p[y*w*4 + x*4 + 2] = c.b;
|
||||
p[y*w*4 + x*4 + 3] = 255;
|
||||
}
|
||||
|
||||
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 = (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 = 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, ts->edge_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
unsigned long data_size;
|
||||
uint8_t *data = fnl_glyph_data(font->font->fnl, glyph, &data_size);
|
||||
|
||||
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 = data[p/8] & (1 << (7 - p % 8));
|
||||
if (on) {
|
||||
set_pixel(surf, w, h, x+col, row, ts->color);
|
||||
}
|
||||
pos += 4;
|
||||
}
|
||||
}
|
||||
|
||||
free(data);
|
||||
}
|
||||
|
||||
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->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, 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;
|
||||
}
|
||||
|
||||
// allocate memory for surface
|
||||
uint8_t *surf = xcalloc(4, width * height);
|
||||
|
||||
// render glyphs to surface
|
||||
int x = 0;
|
||||
for (int i = 0; i < len; i++) {
|
||||
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);
|
||||
free(surf);
|
||||
free(glyphs);
|
||||
}
|
||||
|
||||
static void draw_text(Texture *dst, Texture *glyph, int x, int y, float scale_x, float scale_y)
|
||||
{
|
||||
GLuint fbo = gfx_set_framebuffer(GL_DRAW_FRAMEBUFFER, dst, x, y, glyph->w, glyph->h);
|
||||
GLuint fbo = gfx_set_framebuffer(GL_DRAW_FRAMEBUFFER, dst, roundf(x), y, glyph->w, glyph->h);
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
mat4 mw_transform = MAT4(
|
||||
glyph->w * scale_x, 0, 0, 0,
|
||||
0, glyph->h * scale_y, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1);
|
||||
glyph->w * scale_x, 0, 0, 0,
|
||||
0, glyph->h * scale_y, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1);
|
||||
mat4 wv_transform = WV_TRANSFORM(glyph->w, glyph->h);
|
||||
struct gfx_render_job job = {
|
||||
.texture = glyph->handle,
|
||||
@@ -134,29 +42,84 @@ static void draw_text(Texture *dst, Texture *glyph, int x, int y, float scale_x,
|
||||
};
|
||||
gfx_render(&job);
|
||||
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
|
||||
gfx_reset_framebuffer(GL_DRAW_FRAMEBUFFER, fbo);
|
||||
}
|
||||
|
||||
int fnl_draw_text(struct fnl *fnl, struct text_style *ts, Texture *dst, int x, int y, char *text)
|
||||
struct fnl_glyph_texture {
|
||||
Texture t;
|
||||
SDL_Color c;
|
||||
};
|
||||
|
||||
enum glyph_texture_type {
|
||||
FONT_TEXTURE = 0,
|
||||
EDGE_TEXTURE = 1
|
||||
};
|
||||
|
||||
static Texture *get_glyph_texture(struct fnl_rendered_glyph *glyph, SDL_Color *c,
|
||||
enum glyph_texture_type type)
|
||||
{
|
||||
Texture rendered;
|
||||
struct fnl_font_face *font = &fnl->fonts[ts->font_type].faces[ts->font_size->face];
|
||||
render_text(font, ts, &rendered, text);
|
||||
if (!glyph->data) {
|
||||
glyph->data = xcalloc(2, sizeof(struct fnl_glyph_texture));
|
||||
}
|
||||
|
||||
glBlendEquationSeparate(GL_FUNC_ADD, GL_MAX);
|
||||
float scale_y = 1.0 / ts->font_size->denominator;
|
||||
float scale_x = scale_y * ts->scale_x;
|
||||
draw_text(dst, &rendered, x, y, scale_x, scale_y);
|
||||
glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
|
||||
struct fnl_glyph_texture *g = glyph->data;
|
||||
g += type;
|
||||
|
||||
int w = rendered.w * scale_x;
|
||||
gfx_delete_texture(&rendered);
|
||||
return w;
|
||||
if (!g->t.handle) {
|
||||
gfx_init_texture_amap(&g->t, glyph->width, glyph->height, glyph->pixels, *c);
|
||||
g->c = *c;
|
||||
return &g->t;
|
||||
}
|
||||
|
||||
if (g->c.r != c->r || g->c.g != c->g || g->c.b != c->b) {
|
||||
gfx_fill(&g->t, 0, 0, g->t.w, g->t.h, c->r, c->g, c->b);
|
||||
g->c = *c;
|
||||
}
|
||||
return &g->t;
|
||||
}
|
||||
|
||||
static void draw_glyph_outline(Texture *dst, float x, int y, Texture *glyph, struct text_style *ts)
|
||||
{
|
||||
float edge_width = roundf(ts->edge_width);
|
||||
draw_glyph(dst, x-edge_width, y, glyph, ts->scale_x, 1.0);
|
||||
draw_glyph(dst, x+edge_width, y, glyph, ts->scale_x, 1.0);
|
||||
draw_glyph(dst, x, y-edge_width, glyph, ts->scale_x, 1.0);
|
||||
draw_glyph(dst, x, y+edge_width, glyph, ts->scale_x, 1.0);
|
||||
}
|
||||
|
||||
float fnl_draw_text(struct fnl *fnl, struct text_style *ts, Texture *dst, float x, int y, char *text)
|
||||
{
|
||||
int len = sjis_count_char(text);
|
||||
struct fnl_rendered_glyph **glyphs = xmalloc(len * sizeof(struct fnl_rendered_glyph*));
|
||||
for (int i = 0; i < len; i++) {
|
||||
glyphs[i] = fnl_render_glyph(ts->font_size, sjis_code(text));
|
||||
text = sjis_skip_char(text);
|
||||
}
|
||||
|
||||
float original_x = x;
|
||||
if (ts->edge_width > 0.5) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
Texture *t = get_glyph_texture(glyphs[i], &ts->edge_color, EDGE_TEXTURE);
|
||||
draw_glyph_outline(dst, x, y, t, ts);
|
||||
x += glyphs[i]->advance * ts->scale_x;
|
||||
}
|
||||
}
|
||||
|
||||
x = original_x;
|
||||
for (int i = 0; i < len; i++) {
|
||||
Texture *t = get_glyph_texture(glyphs[i], &ts->color, FONT_TEXTURE);
|
||||
draw_glyph(dst, x, y, t, ts->scale_x, 1.0);
|
||||
x += glyphs[i]->advance * ts->scale_x;
|
||||
}
|
||||
|
||||
free(glyphs);
|
||||
return x - original_x;
|
||||
}
|
||||
|
||||
float fnl_size_text(struct fnl *fnl, struct text_style *ts, char *text)
|
||||
{
|
||||
struct fnl_font_face *font = &fnl->fonts[ts->font_type].faces[ts->font_size->face];
|
||||
struct fnl_font_face *font = ts->font_size->face;
|
||||
int width = 0;
|
||||
int len = sjis_count_char(text);
|
||||
for (int i = 0; i < len; i++) {
|
||||
@@ -166,3 +129,16 @@ float fnl_size_text(struct fnl *fnl, struct text_style *ts, char *text)
|
||||
|
||||
return ((float)width / (float)ts->font_size->denominator) * ts->scale_x;
|
||||
}
|
||||
|
||||
void free_textures(void *data)
|
||||
{
|
||||
struct fnl_glyph_texture *textures = data;
|
||||
gfx_delete_texture(&textures[FONT_TEXTURE].t);
|
||||
gfx_delete_texture(&textures[EDGE_TEXTURE].t);
|
||||
free(textures);
|
||||
}
|
||||
|
||||
void fnl_renderer_free(struct fnl *fnl)
|
||||
{
|
||||
fnl_free(fnl, free_textures);
|
||||
}
|
||||
|
||||
@@ -384,8 +384,6 @@ static void SengokuRanceFont_SP_TextDrawClassic(int sp_no, struct string *text)
|
||||
struct sr_text_properties *p = get_sp_properties(sp_no);
|
||||
int y = p->y - p->ts.size * 0.1875;
|
||||
p->x += sp_text_draw(sp, &p->ts, text, p->x, y);
|
||||
|
||||
WARNING("\"%s\"", text->text);
|
||||
}
|
||||
|
||||
HLL_WARN_UNIMPLEMENTED( , void, SengokuRanceFont, SP_TextDrawPreload, int sp_no, struct string *text);
|
||||
@@ -537,7 +535,7 @@ static void SengokuRanceFont_ModuleInit(void)
|
||||
|
||||
static void SengokuRanceFont_ModuleFini(void)
|
||||
{
|
||||
fnl_free(fontlib);
|
||||
fnl_renderer_free(fontlib);
|
||||
}
|
||||
|
||||
HLL_LIBRARY(SengokuRanceFont,
|
||||
|
||||
+64
-25
@@ -148,31 +148,7 @@ int sprite_set_cg_from_file(struct sact_sprite *sp, const char *path)
|
||||
|
||||
int sprite_save_cg(struct sact_sprite *sp, const char *path)
|
||||
{
|
||||
void *pixels = gfx_get_pixels(sprite_get_texture(sp));
|
||||
|
||||
struct cg cg = {
|
||||
.type = ALCG_UNKNOWN,
|
||||
.metrics = {
|
||||
.w = sp->rect.w,
|
||||
.h = sp->rect.h,
|
||||
.bpp = 24,
|
||||
.has_pixel = sp->sp.has_pixel,
|
||||
.has_alpha = sp->sp.has_alpha,
|
||||
.pixel_pitch = sp->rect.w * 3,
|
||||
.alpha_pitch = 1
|
||||
},
|
||||
.pixels = pixels
|
||||
};
|
||||
FILE *fp = file_open_utf8(path, "wb");
|
||||
if (!fp) {
|
||||
WARNING("Failed to open %s: %s", display_utf0(path), strerror(errno));
|
||||
free(pixels);
|
||||
return 0;
|
||||
}
|
||||
int r = cg_write(&cg, ALCG_QNT, fp);
|
||||
fclose(fp);
|
||||
free(pixels);
|
||||
return r;
|
||||
return gfx_save_texture(sprite_get_texture(sp), path, ALCG_QNT);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -337,3 +313,66 @@ void sprite_get_pixel_value(struct sact_sprite *sp, int x, int y, int *r, int *g
|
||||
*g = c.g;
|
||||
*b = c.b;
|
||||
}
|
||||
|
||||
static void print_color(SDL_Color *c)
|
||||
{
|
||||
printf("(%d,%d,%d,%d)", c->r, c->g, c->b, c->a);
|
||||
}
|
||||
|
||||
static void print_rectangle(Rectangle *r)
|
||||
{
|
||||
printf("{x=%d,y=%d,w=%d,h=%d}", r->x, r->y, r->w, r->h);
|
||||
}
|
||||
|
||||
static void print_point(Point *p)
|
||||
{
|
||||
printf("{x=%d,y=%d}", p->x, p->y);
|
||||
}
|
||||
|
||||
static void print_texture(struct texture *t, int indent)
|
||||
{
|
||||
puts("{");
|
||||
for (int i = 0; i < indent+1; i++) putchar('\t');
|
||||
printf("initialized = %s,\n", t->handle ? "true" : "false");
|
||||
for (int i = 0; i < indent+1; i++) putchar('\t');
|
||||
printf("size = (%d,%d),\n", t->w, t->h);
|
||||
for (int i = 0; i < indent+1; i++) putchar('\t');
|
||||
printf("has_alpha = %s,\n", t->has_alpha ? "true" : "false");
|
||||
for (int i = 0; i < indent+1; i++) putchar('\t');
|
||||
printf("alpha_mod = %d,\n", t->alpha_mod);
|
||||
for (int i = 0; i < indent+1; i++) putchar('\t');
|
||||
switch (t->draw_method) {
|
||||
case DRAW_METHOD_NORMAL: printf("draw_method = normal\n"); break;
|
||||
case DRAW_METHOD_SCREEN: printf("draw_method = screen\n"); break;
|
||||
case DRAW_METHOD_MULTIPLY: printf("draw_method = multiply\n"); break;
|
||||
case DRAW_METHOD_ADDITIVE: printf("draw_method = additive\n"); break;
|
||||
default: printf("draw_method = unknown\n"); break;
|
||||
}
|
||||
for (int i = 0; i < indent; i++) putchar('\t');
|
||||
putchar('}');
|
||||
}
|
||||
|
||||
void print_sprite(struct sact_sprite *sp)
|
||||
{
|
||||
printf("sprite %d = {\n", sp->no);
|
||||
printf("\tsp = {\n");
|
||||
printf("\t\tz = (%d,%d),\n", sp->sp.z, sp->sp.z2);
|
||||
printf("\t\thas_pixel = %s,\n", sp->sp.has_pixel ? "true" : "false");
|
||||
printf("\t\thas_alpha = %s,\n", sp->sp.has_alpha ? "true" : "false");
|
||||
printf("\t\thidden = %s,\n", sp->sp.hidden ? "true" : "false");
|
||||
printf("\t\tin_scene = %s,\n", sp->sp.in_scene ? "true" : "false");
|
||||
printf("\t},\n");
|
||||
printf("\ttexture = "); print_texture(&sp->texture, 1); printf(",\n");
|
||||
printf("\tcolor = "); print_color(&sp->color); printf(",\n");
|
||||
printf("\trect = "); print_rectangle(&sp->rect); printf(",\n");
|
||||
printf("\ttext = {\n");
|
||||
//printf("\t\tstring = \"%s\",\n", display_sjis0(sp->text.str->text));
|
||||
printf("\t\ttexture = "); print_texture(&sp->text.texture, 2); printf(",\n");
|
||||
printf("\t\thome = "); print_point(&sp->text.home); printf(",\n");
|
||||
printf("\t\tpos = "); print_point(&sp->text.pos); printf(",\n");
|
||||
printf("\t\tchar_space = %d,\n", sp->text.char_space);
|
||||
printf("\t\tline_space = %d,\n", sp->text.line_space);
|
||||
printf("\t},\n");
|
||||
printf("\tcg_no = %d\n", sp->cg_no);
|
||||
printf("}\n");
|
||||
}
|
||||
|
||||
+40
@@ -516,6 +516,18 @@ void gfx_init_texture_rgb(struct texture *t, int w, int h, SDL_Color color)
|
||||
free(pixels);
|
||||
}
|
||||
|
||||
void gfx_init_texture_amap(struct texture *t, int w, int h, uint8_t *amap, SDL_Color color)
|
||||
{
|
||||
uint32_t *pixels = xmalloc(sizeof(uint32_t) * w * h);
|
||||
for (int i = 0; i < w*h; i++) {
|
||||
uint32_t c = SDL_MapRGBA(sdl.format, color.r, color.g, color.b, amap[i]);
|
||||
pixels[i] = c;
|
||||
}
|
||||
|
||||
gfx_init_texture_with_pixels(t, w, h, pixels);
|
||||
free(pixels);
|
||||
}
|
||||
|
||||
void gfx_init_texture_blank(struct texture *t, int w, int h)
|
||||
{
|
||||
gfx_init_texture_with_pixels(t, w, h, NULL);
|
||||
@@ -579,3 +591,31 @@ void *gfx_get_pixels(Texture *t)
|
||||
gfx_reset_framebuffer(GL_READ_FRAMEBUFFER, fbo);
|
||||
return pixels;
|
||||
}
|
||||
|
||||
int gfx_save_texture(Texture *t, const char *path, enum cg_type format)
|
||||
{
|
||||
void *pixels = gfx_get_pixels(t);
|
||||
struct cg cg = {
|
||||
.type = ALCG_UNKNOWN,
|
||||
.metrics = {
|
||||
.w = t->w,
|
||||
.h = t->h,
|
||||
.bpp = 24,
|
||||
.has_pixel = true,
|
||||
.has_alpha = t->has_alpha,
|
||||
.pixel_pitch = t->w * 3,
|
||||
.alpha_pitch = 1
|
||||
},
|
||||
.pixels = pixels
|
||||
};
|
||||
FILE *fp = file_open_utf8(path, "wb");
|
||||
if (!fp) {
|
||||
WARNING("Failed to open %s: %s", display_utf0(path), strerror(errno));
|
||||
free(pixels);
|
||||
return 0;
|
||||
}
|
||||
int r = cg_write(&cg, format, fp);
|
||||
fclose(fp);
|
||||
free(pixels);
|
||||
return r;
|
||||
}
|
||||
|
||||
+1
-1
Submodule subprojects/libsys4 updated: f3d1cd853f...7a7a378e07
Reference in New Issue
Block a user