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