diff --git a/include/gfx/gfx.h b/include/gfx/gfx.h index bbbb72c..114a4d9 100644 --- a/include/gfx/gfx.h +++ b/include/gfx/gfx.h @@ -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 */ diff --git a/include/sprite.h b/include/sprite.h index 50525ce..286c24c 100644 --- a/include/sprite.h +++ b/include/sprite.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 */ diff --git a/src/fnl_render.c b/src/fnl_render.c index 4641ef3..b351c31 100644 --- a/src/fnl_render.c +++ b/src/fnl_render.c @@ -14,6 +14,8 @@ * along with this program; if not, see . */ +#include +#include #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); +} diff --git a/src/hll/SengokuRanceFont.c b/src/hll/SengokuRanceFont.c index 006461b..928a9e7 100644 --- a/src/hll/SengokuRanceFont.c +++ b/src/hll/SengokuRanceFont.c @@ -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, diff --git a/src/sprite.c b/src/sprite.c index 1482e29..cf1499c 100644 --- a/src/sprite.c +++ b/src/sprite.c @@ -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"); +} diff --git a/src/video.c b/src/video.c index 246d69e..c434b7e 100644 --- a/src/video.c +++ b/src/video.c @@ -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; +} diff --git a/subprojects/libsys4 b/subprojects/libsys4 index f3d1cd8..7a7a378 160000 --- a/subprojects/libsys4 +++ b/subprojects/libsys4 @@ -1 +1 @@ -Subproject commit f3d1cd853f32dec6a3bb26e31a6f319a1bf6186d +Subproject commit 7a7a378e072259bcb5734d84c1d935f66dc7c79b