diff --git a/cg.c b/cg.c index dfed495..a2dbd97 100644 --- a/cg.c +++ b/cg.c @@ -39,7 +39,6 @@ static enum cg_type check_cgformat(uint8_t *data) if (qnt_checkfmt(data)) { return ALCG_QNT; } - WARNING("Unknown CG type"); return ALCG_UNKNOWN; } @@ -55,6 +54,7 @@ bool cg_get_metrics(int no, struct cg_metrics *dst) qnt_get_metrics(dfile->data, dst); break; default: + WARNING("Unknown CG type (CG %d)", no); return false; } return true; @@ -89,7 +89,7 @@ void cg_free(struct cg *cg) bool cg_load(struct cg *cg, int no) { struct archive_data *dfile; - int type, possibly_unused size = 0; + int type; if (!(dfile = ald_get(ald[ALDFILE_CG], no))) { WARNING("Failed to load CG %d", no); @@ -100,32 +100,32 @@ bool cg_load(struct cg *cg, int no) type = check_cgformat(dfile->data); // extract cg - // size is only pixel data size switch(type) { case ALCG_QNT: - cg->type = ALCG_QNT; - cg->s = qnt_extract(dfile->data); - cg->pixel_alloc = true; + qnt_extract(dfile->data, cg); break; case ALCG_AJP: - // TODO + WARNING("Unimplemented CG type: AJP"); break; case ALCG_PNG: - // TODO + WARNING("Unimplemented CG type: PNG"); break; default: + WARNING("Unknown CG type (CG %d)", no); break; } // ok to free ald_free_data(dfile); - return true; + return !!cg->s; } void _cg_init(struct cg *cg) { + cg->type = ALCG_UNKNOWN; cg->pixel_alloc = false; + cg->has_alpha = false; cg->s = NULL; } diff --git a/cg.h b/cg.h index 208ec43..7464a66 100644 --- a/cg.h +++ b/cg.h @@ -40,7 +40,9 @@ enum cg_type { struct cg { enum cg_type type; // cg format type bool pixel_alloc; + bool has_alpha; SDL_Surface *s; + SDL_Texture *t; }; struct cg_metrics { diff --git a/graphics.h b/graphics.h index 1390e13..9dfda10 100644 --- a/graphics.h +++ b/graphics.h @@ -22,30 +22,19 @@ #include #include -typedef struct { - uint8_t red[256]; - uint8_t green[256]; - uint8_t blue[256]; -} Pallet256; - -typedef struct { - uint8_t r,g,b; - uint32_t pixel; -} Pallet; - typedef SDL_Point Point; +#define POINT(_x, _y) ((Point){.x=_x, .y=_y}) + typedef struct { - int width; - int height; + int w; + int h; } Dimension; +#define DIM(_w, _h) ((Dimension){.w=_w, .h=_h}) + typedef SDL_Rect Rectangle; -typedef struct { - int width; - int height; - int depth; -} DispInfo; +#define RECT(_x, _y, _w, _h) ((Rectangle){.x=_x, .y=_y, .w=_w, .h=_h}) #endif /* SYSTEM4_GRAPHICS_H */ diff --git a/qnt.c b/qnt.c index 47bb463..7455fdc 100644 --- a/qnt.c +++ b/qnt.c @@ -39,6 +39,8 @@ #include "cg.h" #include "qnt.h" #include "system4.h" +#include "sdl_private.h" +#include "sdl_core.h" /* zlib の展開バッファで、幅×高さ×3に、さらにどれくらい余裕をとるか @@ -249,17 +251,27 @@ bool qnt_get_metrics(uint8_t *data, struct cg_metrics *dst) * * return: extracted image data and information */ -SDL_Surface *qnt_extract(uint8_t *data) +void qnt_extract(uint8_t *data, struct cg *cg) { struct qnt_header qnt; extract_header(data, &qnt); uint8_t *pixels = malloc(sizeof(uint8_t) * ((qnt.width+10) * (qnt.height+10) * 3)); extract_pixel(&qnt, pixels, data + qnt.hdr_size); - if (!qnt.alpha_size) - return SDL_CreateRGBSurfaceWithFormatFrom(pixels, qnt.width, qnt.height, 24, - qnt.width * 3, SDL_PIXELFORMAT_RGB24); + cg->type = ALCG_QNT; + cg->has_alpha = qnt.alpha_size > 0; + if (!qnt.alpha_size) { + // convert RGB -> ARGB + SDL_Surface *tmp = SDL_CreateRGBSurfaceWithFormatFrom(pixels, qnt.width, qnt.height, 24, + qnt.width * 3, SDL_PIXELFORMAT_RGB24); + cg->s = SDL_ConvertSurface(tmp, sdl.format, 0); + cg->t = sdl_surface_to_texture(cg->s); + cg->pixel_alloc = false; + SDL_FreeSurface(tmp); + free(pixels); + return; + } // combine color/alpha data uint8_t *alpha = malloc(sizeof(uint8_t) * ((qnt.width+10) * (qnt.height+10))); uint8_t *tmp = malloc((qnt.width+10) * (qnt.height+10) * 4); @@ -274,6 +286,8 @@ SDL_Surface *qnt_extract(uint8_t *data) free(pixels); pixels = tmp; - return SDL_CreateRGBSurfaceWithFormatFrom(pixels, qnt.width, qnt.height, 32, - qnt.width * 4, SDL_PIXELFORMAT_ARGB32); + cg->s = SDL_CreateRGBSurfaceWithFormatFrom(pixels, qnt.width, qnt.height, 32, + qnt.width * 4, SDL_PIXELFORMAT_ARGB32); + cg->t = sdl_surface_to_texture(cg->s); + cg->pixel_alloc = true; } diff --git a/qnt.h b/qnt.h index 54cdb68..c786fce 100644 --- a/qnt.h +++ b/qnt.h @@ -45,6 +45,6 @@ struct qnt_header { bool qnt_checkfmt(uint8_t *data); bool qnt_get_metrics(uint8_t *data, struct cg_metrics *dst); -SDL_Surface *qnt_extract(uint8_t *data); +void qnt_extract(uint8_t *data, struct cg *cg); #endif /* __QNT_H__ */ diff --git a/sact.h b/sact.h index 7f8d425..09e0921 100644 --- a/sact.h +++ b/sact.h @@ -34,6 +34,7 @@ struct sact_sprite { struct { struct string *str; SDL_Surface *surf; + SDL_Texture *t; Point home; Point pos; int char_space; diff --git a/sact_graphics.c b/sact_graphics.c index 703213e..4af967b 100644 --- a/sact_graphics.c +++ b/sact_graphics.c @@ -70,17 +70,21 @@ int sact_Init(void) int sact_Update(void) { handle_events(); + + sdl_render_clear(); struct sact_sprite *p; TAILQ_FOREACH(p, &sprite_list, entry) { if (!p->show) continue; - if (!p->cg->s) - p->cg->s = sdl_make_rectangle(p->rect.w, p->rect.h, &p->color); - sdl_draw_surface(p->cg->s, &p->rect); - if (p->text.surf) - sdl_draw_surface(p->text.surf, &p->rect); + if (!p->cg->s) { + p->cg->s = sdl_create_surface(p->rect.w, p->rect.h, &p->color); + p->cg->t = sdl_surface_to_texture(p->cg->s); + } + sdl_render_texture(p->cg->t, &p->rect); + if (p->text.t) + sdl_render_texture(p->text.t, &p->rect); } - sdl_update_screen(); + sdl_render_present(); return 1; } @@ -253,7 +257,7 @@ static struct sact_sprite *create_sprite(int sp_no, int width, int height, int r VM_ERROR("Invalid sprite number: %d", sp_no); if (!(sp = sact_get_sprite(sp_no))) sp = alloc_sprite(sp_no); - sp->color = (SDL_Color) { .r = r, .g = g, .b = b, .a = a }; + sp->color = (SDL_Color) { .r = r, .g = g, .b = b, .a = (a >= 0 ? a : 255) }; sp->rect.w = width; sp->rect.h = height; if (sp->cg) { @@ -262,6 +266,7 @@ static struct sact_sprite *create_sprite(int sp_no, int width, int height, int r sp->cg = cg_init(); sprite_register(sp); } + sp->cg->has_alpha = a >= 0; return sp; } @@ -273,7 +278,7 @@ int sact_SP_Create(int sp_no, int width, int height, int r, int g, int b, int a) int sact_SP_CreatePixelOnly(int sp_no, int width, int height) { - create_sprite(sp_no, width, height, 0, 0, 0, 255); + create_sprite(sp_no, width, height, 0, 0, 0, -1); return 1; } @@ -303,46 +308,31 @@ int sact_SP_ExistsAlpha(int sp_no) { struct sact_sprite *sp = sact_get_sprite(sp_no); if (!sp) return 0; - if (!sp->cg->s) return 1; - return !!sp->cg->s->format->Amask; + return sp->cg->has_alpha; } -#define tm_ColorR(tm) (tm[0].i) -#define tm_ColorG(tm) (tm[1].i) -#define tm_ColorB(tm) (tm[2].i) -#define tm_Size(tm) (tm[3].i) -#define tm_Weight(tm) (tm[4].i) -#define tm_Face(tm) (tm[5].i) -#define tm_ShadowPixelL(tm) (tm[6].i) -#define tm_ShadowPixelU(tm) (tm[7].i) -#define tm_ShadowPixelR(tm) (tm[8].i) -#define tm_ShadowPixelD(tm) (tm[9].i) -#define tm_ShadowColorR(tm) (tm[10].i) -#define tm_ShadowColorG(tm) (tm[11].i) -#define tm_ShadowColorB(tm) (tm[12].i) - static void init_text_metrics(struct text_metrics *tm, union vm_value *_tm) { *tm = (struct text_metrics) { .color = { - .r = tm_ColorR(_tm), - .g = tm_ColorG(_tm), - .b = tm_ColorB(_tm), + .r = _tm[0].i, + .g = _tm[1].i, + .b = _tm[2].i, .a = 255 }, .outline_color = { - .r = tm_ShadowColorR(_tm), - .g = tm_ShadowColorG(_tm), - .b = tm_ShadowColorB(_tm), + .r = _tm[10].i, + .g = _tm[11].i, + .b = _tm[12].i, .a = 255 }, - .size = tm_Size(_tm), - .weight = tm_Weight(_tm), - .face = tm_Face(_tm), - .outline_left = tm_ShadowPixelL(_tm), - .outline_up = tm_ShadowPixelU(_tm), - .outline_right = tm_ShadowPixelR(_tm), - .outline_down = tm_ShadowPixelD(_tm), + .size = _tm[3].i, + .weight = _tm[4].i, + .face = _tm[5].i, + .outline_left = _tm[6].i, + .outline_up = _tm[7].i, + .outline_right = _tm[8].i, + .outline_down = _tm[9].i, }; } @@ -354,11 +344,13 @@ int sact_SP_TextDraw(int sp_no, struct string *text, union vm_value *_tm) if (!sp->text.surf) { SDL_Color c = { 0, 0, 0, 0 }; - sp->text.surf = sdl_make_rectangle(sp->rect.w, sp->rect.h, &c); + sp->text.surf = sdl_create_surface(sp->rect.w, sp->rect.h, &c); + sp->text.t = sdl_create_texture(sp->rect.w, sp->rect.h); } init_text_metrics(&tm, _tm); sp->text.pos.x += sdl_render_text(sp->text.surf, sp->text.pos, text->text, &tm) + sp->text.char_space; + sdl_update_texture(sp->text.t, sp->text.surf); return 1; } @@ -366,7 +358,9 @@ static void clear_text(struct sact_sprite *sp) { if (sp->text.surf) { SDL_FreeSurface(sp->text.surf); + SDL_DestroyTexture(sp->text.t); sp->text.surf = NULL; + sp->text.t = NULL; } } @@ -395,9 +389,11 @@ int sact_SP_TextCopy(int dno, int sno) clear_text(dsp); if (ssp->text.surf) { SDL_Color c = { 0, 0, 0, 0 }; - Rectangle rect = { .x = 0, .y = 0, .w = dsp->rect.w, .h = dsp->rect.h }; - dsp->text.surf = sdl_make_rectangle(dsp->rect.w, dsp->rect.h, &c); + Rectangle rect = RECT(0, 0, dsp->rect.w, dsp->rect.h); + dsp->text.surf = sdl_create_surface(dsp->rect.w, dsp->rect.h, &c); + dsp->text.t = sdl_create_texture(dsp->rect.w, dsp->rect.h); SDL_BlitSurface(ssp->text.surf, NULL, dsp->text.surf, &rect); + sdl_update_texture(dsp->text.t, dsp->text.surf); } dsp->text.home = ssp->text.home; dsp->text.pos = ssp->text.pos; @@ -425,269 +421,69 @@ int sact_CG_GetMetrics(int cg_no, union vm_value *cgm) // --- DrawGraph.dll --- // -struct sact_sprite *DrawGraph_get_sprite(int no) +struct sact_sprite *get_surface(int no) { struct sact_sprite *sp = sact_get_sprite(no); if (!sp) ERROR("Invalid sprite number: %d", no); if (!sp->cg->s) - sp->cg->s = sdl_make_rectangle(sp->rect.w, sp->rect.h, &sp->color); + sp->cg->s = sdl_create_surface(sp->rect.w, sp->rect.h, &sp->color); + if (!sp->cg->t) + sp->cg->t = sdl_surface_to_texture(sp->cg->s); return sp; } -static void DrawGraph_Blit(SDL_Surface *ds, int dx, int dy, SDL_Surface *ss, int sx, int sy, int w, int h) -{ - Rectangle src_rect = { .x = sx, .y = sy, .w = w, .h = h }; - Rectangle dst_rect = { .x = dx, .y = dy, .w = w, .h = h }; - SDL_BlitSurface(ss, &src_rect, ds, &dst_rect); -} - void DrawGraph_Copy(int dno, int dx, int dy, int sno, int sx, int sy, int w, int h) { - SDL_BlendMode mode; - SDL_Surface *ds = DrawGraph_get_sprite(dno)->cg->s; - SDL_Surface *ss = DrawGraph_get_sprite(sno)->cg->s; - - SDL_GetSurfaceBlendMode(ss, &mode); - SDL_SetSurfaceBlendMode(ss, SDL_BLENDMODE_NONE); - - DrawGraph_Blit(ds, dx, dy, ss, sx, sy, w, h); - - SDL_SetSurfaceBlendMode(ss, mode); + sdl_copy(get_surface(dno)->cg, dx, dy, get_surface(sno)->cg, sx, sy, w, h); } void DrawGraph_CopyBright(int dno, int dx, int dy, int sno, int sx, int sy, int w, int h, int rate) { - SDL_BlendMode mode; - SDL_Surface *ds = DrawGraph_get_sprite(dno)->cg->s; - SDL_Surface *ss = DrawGraph_get_sprite(sno)->cg->s; - - SDL_GetSurfaceBlendMode(ss, &mode); - SDL_SetSurfaceBlendMode(ss, SDL_BLENDMODE_NONE); - SDL_SetSurfaceColorMod(ss, rate, rate, rate); - - DrawGraph_Blit(ds, dx, dy, ss, sx, sy, w, h); - - SDL_SetSurfaceColorMod(ss, 255, 255, 255); - SDL_SetSurfaceBlendMode(ss, mode); + sdl_copy_bright(get_surface(dno)->cg, dx, dy, get_surface(sno)->cg, sx, sy, w, h, rate); } -// Clip src/dst rectangles in preparation for a copy. -static void clip_rects(SDL_Surface *ds, int *dx, int *dy, SDL_Surface *ss, int *sx, int *sy, int *w, int *h) -{ - if (*sx < 0) { - *dx -= *sx; - *w += *sx; - *sx = 0; - } - if (*sy < 0) { - *dy -= *sy; - *h += *sy; - *sy = 0; - } - if (*dx < 0) { - *sx -= *dx; - *w += *dx; - *dx = 0; - } - if (*dy < 0) { - *sy -= *dy; - *w += *dy; - *dy = 0; - } - *w = min(*w, min(ds->w - *dx, ss->w - *sx)); - *h = min(*h, min(ds->h - *dy, ss->h - *sy)); -} - -static inline uint32_t *get_pixel(SDL_Surface *s, int x, int y) -{ - return (uint32_t*)(((uint8_t*)s->pixels) + s->pitch*y + s->format->BytesPerPixel*x); -} - -static inline void next_pixel(SDL_Surface *s, uint32_t **p) -{ - *p = (uint32_t*)((uint8_t*)*p + s->format->BytesPerPixel); -} - -// iterator for copy operations -#define copy_for_each_pixel(ds, dx, dy, ss, sx, sy, w, h, dp, sp, expr) \ - do { \ - clip_rects(ds, &dx, &dy, ss, &sx, &sy, &w, &h); \ - SDL_LockSurface(ds); \ - SDL_LockSurface(ss); \ - for (int _row = 0; _row < h; _row++) { \ - uint32_t *dp = get_pixel(ds, dx, _row+dy); \ - uint32_t *sp = get_pixel(ss, sx, _row+sy); \ - for (int _col = 0; _col < w; _col++, next_pixel(ds, &dp), next_pixel(ss, &sp)) \ - expr; \ - } \ - SDL_UnlockSurface(ss); \ - SDL_UnlockSurface(ds); \ - } while (0) - -#define fill_for_each_pixel(s, x, y, w, h, p, expr) \ - do { \ - SDL_LockSurface(s); \ - for (int _row = 0; _row < h; _row++) { \ - uint32_t *p = get_pixel(s, x, _row+y); \ - for (int _col = 0; _col < w; _col++, next_pixel(s, &p)) \ - expr; \ - } \ - SDL_UnlockSurface(s); \ - } while(0); - void DrawGraph_CopyAMap(int dno, int dx, int dy, int sno, int sx, int sy, int w, int h) { - SDL_Surface *ds = DrawGraph_get_sprite(dno)->cg->s; - SDL_Surface *ss = DrawGraph_get_sprite(sno)->cg->s; - - if (!ds->format->Amask || !ss->format->Amask) - return; - - copy_for_each_pixel(ds, dx, dy, ss, sx, sy, w, h, dp, sp, { - uint32_t a = (*sp & ss->format->Amask) >> ss->format->Ashift; - *dp &= ~ds->format->Amask; - *dp |= (a << ds->format->Ashift); - }); + sdl_copy_amap(get_surface(dno)->cg, dx, dy, get_surface(sno)->cg, sx, sy, w, h); } void DrawGraph_CopySprite(int dno, int dx, int dy, int sno, int sx, int sy, int w, int h, int r, int g, int b) { - SDL_BlendMode mode; - SDL_Surface *ds = DrawGraph_get_sprite(dno)->cg->s; - SDL_Surface *ss = DrawGraph_get_sprite(sno)->cg->s; - - SDL_GetSurfaceBlendMode(ss, &mode); - SDL_SetSurfaceBlendMode(ss, SDL_BLENDMODE_NONE); - SDL_SetColorKey(ss, SDL_TRUE, SDL_MapRGB(ss->format, r, g, b)); - - DrawGraph_Blit(ds, dx, dy, ss, sx, sy, w, h); - - SDL_SetColorKey(ss, SDL_FALSE, 0); - SDL_SetSurfaceBlendMode(ss, mode); + sdl_copy_sprite(get_surface(dno)->cg, dx, dy, get_surface(sno)->cg, sx, sy, w, h, r, g, b); } void DrawGraph_CopyUseAMapUnder(int dno, int dx, int dy, int sno, int sx, int sy, int w, int h, int a_threshold) { - SDL_Surface *ds = DrawGraph_get_sprite(dno)->cg->s; - SDL_Surface *ss = DrawGraph_get_sprite(sno)->cg->s; - - if (!ss->format->Amask) - return; - - uint8_t r, g, b, a; - copy_for_each_pixel(ds, dx, dy, ss, sx, sy, w, h, dp, sp, { - SDL_GetRGBA(*sp, ss->format, &r, &g, &b, &a); - if (a <= a_threshold) { - *dp &= ~(ds->format->Rmask | ds->format->Gmask | ds->format->Bmask); - *dp |= (uint32_t)r << ds->format->Rshift; - *dp |= (uint32_t)g << ds->format->Gshift; - *dp |= (uint32_t)b << ds->format->Bshift; - } - }); + sdl_copy_use_amap_under(get_surface(dno)->cg, dx, dy, get_surface(sno)->cg, sx, sy, w, h, a_threshold); } void DrawGraph_CopyUseAMapBorder(int dno, int dx, int dy, int sno, int sx, int sy, int w, int h, int a_threshold) { - SDL_Surface *ds = DrawGraph_get_sprite(dno)->cg->s; - SDL_Surface *ss = DrawGraph_get_sprite(sno)->cg->s; - - if (!ss->format->Amask) - return; - - uint8_t r, g, b, a; - copy_for_each_pixel(ds, dx, dy, ss, sx, sy, w, h, dp, sp, { - SDL_GetRGBA(*sp, ss->format, &r, &g, &b, &a); - if (a >= a_threshold) { - *dp &= ~(ds->format->Rmask | ds->format->Gmask | ds->format->Bmask); - *dp |= (uint32_t)r << ds->format->Rshift; - *dp |= (uint32_t)g << ds->format->Gshift; - *dp |= (uint32_t)b << ds->format->Bshift; - } - }); + sdl_copy_use_amap_border(get_surface(dno)->cg, dx, dy, get_surface(sno)->cg, sx, sy, w, h, a_threshold); } void DrawGraph_CopyAMapMax(int dno, int dx, int dy, int sno, int sx, int sy, int w, int h) { - SDL_Surface *ds = DrawGraph_get_sprite(dno)->cg->s; - SDL_Surface *ss = DrawGraph_get_sprite(sno)->cg->s; - - if (!ss->format->Amask || !ds->format->Amask) - return; - - copy_for_each_pixel(ds, dx, dy, ss, sx, sy, w, h, dp, sp, { - uint32_t d_a = (*dp & ds->format->Amask) >> ds->format->Ashift; - uint32_t s_a = (*sp & ss->format->Amask) >> ss->format->Ashift; - if (s_a > d_a) { - *dp &= ~ds->format->Amask; - *dp |= s_a << ds->format->Ashift; - } - }); + sdl_copy_amap_max(get_surface(dno)->cg, dx, dy, get_surface(sno)->cg, sx, sy, w, h); } void DrawGraph_CopyAMapMin(int dno, int dx, int dy, int sno, int sx, int sy, int w, int h) { - SDL_Surface *ds = DrawGraph_get_sprite(dno)->cg->s; - SDL_Surface *ss = DrawGraph_get_sprite(sno)->cg->s; - - if (!ss->format->Amask || !ds->format->Amask) - return; - - copy_for_each_pixel(ds, dx, dy, ss, sx, sy, w, h, dp, sp, { - uint32_t d_a = (*dp & ds->format->Amask) >> ds->format->Ashift; - uint32_t s_a = (*sp & ss->format->Amask) >> ss->format->Ashift; - if (s_a < d_a) { - *dp &= ~ds->format->Amask; - *dp |= s_a << ds->format->Ashift; - } - }); -} - -static void blend_pixels(SDL_Color *dst, SDL_Color src) -{ - uint32_t alpha = src.a + 1; - uint32_t inv_alpha = 256 - src.a; - dst->r = (uint8_t)((alpha * src.r + inv_alpha * dst->r) >> 8); - dst->g = (uint8_t)((alpha * src.g + inv_alpha * dst->g) >> 8); - dst->b = (uint8_t)((alpha * src.b + inv_alpha * dst->b) >> 8); + sdl_copy_amap_min(get_surface(dno)->cg, dx, dy, get_surface(sno)->cg, sx, sy, w, h); } void DrawGraph_Fill(int sp_no, int x, int y, int w, int h, int r, int g, int b) { - SDL_Surface *s = DrawGraph_get_sprite(sp_no)->cg->s; - - fill_for_each_pixel(s, x, y, w, h, p, { - *p &= ~(s->format->Rmask | s->format->Gmask | s->format->Bmask); - *p |= (uint32_t)r << s->format->Rshift; - *p |= (uint32_t)g << s->format->Gshift; - *p |= (uint32_t)b << s->format->Bshift; - }); + sdl_fill(get_surface(sp_no)->cg, x, y, w, h, r, g, b); } -// XXX: Despite being called a fill operation, this function actually performs blending. void DrawGraph_FillAlphaColor(int sp_no, int x, int y, int w, int h, int r, int g, int b, int a) { - SDL_Surface *s = DrawGraph_get_sprite(sp_no)->cg->s; - - SDL_Color dst; - SDL_Color src = { .r = r, .g = g, .b = b, .a = a }; - fill_for_each_pixel(s, x, y, w, h, p, { - SDL_GetRGBA(*p, s->format, &dst.r, &dst.g, &dst.b, &dst.a); - blend_pixels(&dst, src); - *p &= ~(s->format->Rmask | s->format->Gmask | s->format->Bmask | s->format->Amask); - *p |= (uint32_t)dst.r << s->format->Rshift; - *p |= (uint32_t)dst.g << s->format->Gshift; - *p |= (uint32_t)dst.b << s->format->Bshift; - *p |= (uint32_t)dst.a << s->format->Ashift; - }); + sdl_fill_alpha_color(get_surface(sp_no)->cg, x, y, w, h, r, g, b, a); } void DrawGraph_FillAMap(int sp_no, int x, int y, int w, int h, int a) { - SDL_Surface *s = DrawGraph_get_sprite(sp_no)->cg->s; - - fill_for_each_pixel(s, x, y, w, h, p, { - *p &= ~s->format->Amask; - *p |= (uint32_t)a << s->format->Ashift; - }); + sdl_fill_amap(get_surface(sp_no)->cg, x, y, w, h, a); } diff --git a/sdl_core.h b/sdl_core.h index f8e6075..c6d1b7f 100644 --- a/sdl_core.h +++ b/sdl_core.h @@ -28,12 +28,32 @@ struct cg; int sdl_initialize(void); void sdl_remove(void); -void sdl_set_window_size(int x, int y, int w, int h); +void sdl_set_window_size(int w, int h); void sdl_fullscreen(bool on); -void sdl_update_screen(void); -void sdl_draw_surface(SDL_Surface *s, Rectangle *dst); -SDL_Surface *sdl_make_rectangle(int w, int h, SDL_Color *color); +// surface/texture creation +SDL_Texture *sdl_create_texture(int w, int h); +void sdl_update_texture(SDL_Texture *t, SDL_Surface *s); +SDL_Texture *sdl_surface_to_texture(SDL_Surface *s); +SDL_Surface *sdl_create_surface(int w, int h, SDL_Color *color); + +// rendering +void sdl_render_clear(void); +void sdl_render_present(void); +void sdl_render_texture(SDL_Texture *t, Rectangle *r); + +// drawing +void sdl_copy(struct cg *dst, int dx, int dy, struct cg *src, int sx, int sy, int w, int h); +void sdl_copy_bright(struct cg *dst, int dx, int dy, struct cg *src, int sx, int sy, int w, int h, int rate); +void sdl_copy_amap(struct cg *dst, int dx, int dy, struct cg *src, int sx, int sy, int w, int h); +void sdl_copy_sprite(struct cg *dst, int dx, int dy, struct cg *src, int sx, int sy, int w, int h, int r, int g, int b); +void sdl_copy_use_amap_under(struct cg *dst, int dx, int dy, struct cg *src, int sx, int sy, int w, int h, int a_threshold); +void sdl_copy_use_amap_border(struct cg *dst, int dx, int dy, struct cg *src, int sx, int sy, int w, int h, int a_threshold); +void sdl_copy_amap_max(struct cg *dst, int dx, int dy, struct cg *src, int sx, int sy, int w, int h); +void sdl_copy_amap_min(struct cg *dst, int dx, int dy, struct cg *src, int sx, int sy, int w, int h); +void sdl_fill(struct cg *dst, int x, int y, int w, int h, int r, int g, int b); +void sdl_fill_alpha_color(struct cg *dst, int x, int y, int w, int h, int r, int g, int b, int a); +void sdl_fill_amap(struct cg *dst, int x, int y, int w, int h, int a); enum font_weight { FW_NORMAL = 400, diff --git a/sdl_draw.c b/sdl_draw.c index 40a10c1..0a4c48e 100644 --- a/sdl_draw.c +++ b/sdl_draw.c @@ -1,5 +1,4 @@ -/* Copyright (C) 2000- Fumihiko Murata - * 2019 Nunuhara Cabbage +/* 2019 Nunuhara Cabbage * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,55 +22,276 @@ #include "sdl_private.h" #include "cg.h" -bool sdl_dirty = false; +// FIXME: these can be constants (but depend on endianness) +#define R_MASK (sdl.format->Rmask) +#define G_MASK (sdl.format->Gmask) +#define B_MASK (sdl.format->Bmask) +#define A_MASK (sdl.format->Amask) -void sdl_update_screen(void) +#define R_SHIFT (sdl.format->Rshift) +#define G_SHIFT (sdl.format->Gshift) +#define B_SHIFT (sdl.format->Bshift) +#define A_SHIFT (sdl.format->Ashift) + +#define PIXEL_R(p) ((p & R_MASK) >> R_SHIFT) +#define PIXEL_G(p) ((p & G_MASK) >> G_SHIFT) +#define PIXEL_B(p) ((p & B_MASK) >> B_SHIFT) +#define PIXEL_A(p) ((p & A_MASK) >> A_SHIFT) + +static inline void PIXEL_SET_R(uint32_t *p, uint8_t r) { - if (!sdl.dirty) + *p = (*p & ~R_MASK) | (r << R_SHIFT); +} + +static inline void PIXEL_SET_G(uint32_t *p, uint8_t g) +{ + *p = (*p & ~G_MASK) | (g << G_SHIFT); +} + +static inline void PIXEL_SET_B(uint32_t *p, uint8_t b) +{ + *p = (*p & ~B_MASK) | (b << B_SHIFT); +} + +static inline void PIXEL_SET_A(uint32_t *p, uint8_t a) +{ + *p = (*p & ~A_MASK) | (a << A_SHIFT); +} + +static inline void PIXEL_SET_RGB(uint32_t *p, uint8_t r, uint8_t g, uint8_t b) +{ + *p = (*p & A_MASK) | (r << R_SHIFT) | (g << G_SHIFT) | (b << B_SHIFT); +} + +static inline void PIXEL_SET_RGBA(uint32_t *p, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + *p = (r << R_SHIFT) | (g << G_SHIFT) | (b << B_SHIFT) | (a << A_SHIFT); +} + +// Clip src/dst rectangles in preparation for a copy. +static void copy_clip_rects(int dw, int dh, int *dx, int *dy, int sw, int sh, int *sx, int *sy, int *w, int *h) +{ + if (*sx < 0) { + *dx -= *sx; + *w += *sx; + *sx = 0; + } + if (*sy < 0) { + *dy -= *sy; + *h += *sy; + *sy = 0; + } + if (*dx < 0) { + *sx -= *dx; + *w += *dx; + *dx = 0; + } + if (*dy < 0) { + *sy -= *dy; + *w += *dy; + *dy = 0; + } + *w = min(*w, min(dw - *dx, sw - *sx)); + *h = min(*h, min(dh - *dy, sh - *sy)); +} + +static void fill_clip_rect(int *dx, int *dy, int dw, int dh, int *w, int *h) +{ + if (*dx > dw || *dy > dh) { + *dx = *dy = *w = *h = 0; return; - SDL_UpdateTexture(sdl.texture, NULL, sdl.display->pixels, sdl.display->pitch); - SDL_RenderClear(sdl.renderer); - SDL_RenderCopy(sdl.renderer, sdl.texture, NULL, NULL); - SDL_RenderPresent(sdl.renderer); - sdl.dirty = false; + } + + if (*dx < 0) { + *w += *dx; + *dx = 0; + } + if (*dy < 0) { + *h += *dy; + *dy = 0; + } + *w = min(*w, dw - *dx); + *h = min(*h, dh - *dy); } -SDL_Surface *sdl_make_rectangle(int w, int h, SDL_Color *color) +static inline uint32_t *get_pixel(SDL_Surface *s, int x, int y) { - Rectangle rect = { .x = 0, .y = 0, .w = w, .h = w }; - SDL_Surface *s = SDL_CreateRGBSurfaceWithFormat(0, w, h, 32, SDL_PIXELFORMAT_ARGB32); - uint32_t c = SDL_MapRGBA(s->format, color->r, color->g, color->b, color->a); - SDL_FillRect(s, &rect, c); - return s; + return (uint32_t*)(((uint8_t*)s->pixels) + s->pitch*y + s->format->BytesPerPixel*x); } -void sdl_draw_rectangle(Rectangle *_rect, int pixels, uint8_t r, uint8_t g, uint8_t b) +// iterator for copy operations +// FIXME: texture should be marked as dirty and updated later to avoid unnecessary copying +#define copy_for_each_pixel(dcg, dx, dy, scg, sx, sy, w, h, dp, sp, expr) \ + do { \ + copy_clip_rects(dcg->s->w, dcg->s->h, &dx, &dy, scg->s->w, scg->s->h, &sx, &sy, &w, &h); \ + SDL_LockSurface(dcg->s); \ + SDL_LockSurface(scg->s); \ + for (int _row = 0; _row < h; _row++) { \ + uint32_t *dp = get_pixel(dcg->s, dx, _row+dy); \ + uint32_t *sp = get_pixel(scg->s, sx, _row+sy); \ + for (int _col = 0; _col < w; _col++, dp++, sp++) \ + expr; \ + } \ + SDL_UpdateTexture(dcg->t, NULL, dcg->s->pixels, dcg->s->pitch); \ + SDL_UnlockSurface(scg->s); \ + SDL_UnlockSurface(dcg->s); \ + } while (0) + +// iterator for fill operations +#define fill_for_each_pixel(cg, x, y, w, h, p, expr) \ + do { \ + fill_clip_rect(&x, &y, cg->s->w, cg->s->h, &w, &h); \ + SDL_LockSurface(cg->s); \ + for (int _row = 0; _row < h; _row++) { \ + uint32_t *p = get_pixel(cg->s, x, _row+y); \ + for (int _col = 0; _col < w; _col++, p++) \ + expr; \ + } \ + SDL_UpdateTexture(cg->t, NULL, cg->s->pixels, cg->s->pitch); \ + SDL_UnlockSurface(cg->s); \ + } while(0); + +static void sdl_blit(SDL_Surface *ds, int dx, int dy, SDL_Surface *ss, int sx, int sy, int w, int h) { - SDL_Rect rect[4]; - - // top - rect[0] = *_rect; - rect[0].h = pixels; - // bot - rect[1] = *_rect; - rect[1].y += _rect->h - pixels; - rect[1].h = pixels; - // left - rect[2] = *_rect; - rect[2].w = pixels; - // right - rect[3] = *_rect; - rect[3].x += _rect->w - pixels; - rect[3].w = pixels; - - uint32_t c = SDL_MapRGB(sdl.display->format, r, g, b); - SDL_FillRects(sdl.display, rect, 4, c); - sdl.dirty = true; + Rectangle src_rect = RECT(sx, sy, w, h); + Rectangle dst_rect = RECT(dx, dy, w, h); + SDL_BlitSurface(ss, &src_rect, ds, &dst_rect); } -void sdl_draw_surface(SDL_Surface *s, Rectangle *dst) +void sdl_copy(struct cg *dst, int dx, int dy, struct cg *src, int sx, int sy, int w, int h) { - Rectangle src = { .x = 0, .y = 0, .w = s->w, .h = s->h }; - SDL_BlitSurface(s, &src, sdl.display, dst); - sdl.dirty = true; + SDL_BlendMode mode; + SDL_GetSurfaceBlendMode(src->s, &mode); + SDL_SetSurfaceBlendMode(src->s, SDL_BLENDMODE_NONE); + sdl_blit(dst->s, dx, dy, src->s, sx, sy, w, h); + + SDL_SetSurfaceBlendMode(src->s, mode); +} + +void sdl_copy_bright(struct cg *dst, int dx, int dy, struct cg *src, int sx, int sy, int w, int h, int rate) +{ + SDL_BlendMode mode; + SDL_GetSurfaceBlendMode(src->s, &mode); + SDL_SetSurfaceBlendMode(src->s, SDL_BLENDMODE_NONE); + SDL_SetSurfaceColorMod(src->s, rate, rate, rate); + + sdl_blit(dst->s, dx, dy, src->s, sx, sy, w, h); + + SDL_SetSurfaceColorMod(src->s, 255, 255, 255); + SDL_SetSurfaceBlendMode(src->s, mode); +} + +void sdl_copy_amap(struct cg *dst, int dx, int dy, struct cg *src, int sx, int sy, int w, int h) +{ + if (!dst->has_alpha || !src->has_alpha) + return; + + copy_for_each_pixel(dst, dx, dy, src, sx, sy, w, h, dp, sp, { + PIXEL_SET_A(dp, PIXEL_A(*sp)); + }); +} + +void sdl_copy_sprite(struct cg *dst, int dx, int dy, struct cg *src, int sx, int sy, int w, int h, int r, int g, int b) +{ + SDL_BlendMode mode; + + SDL_GetSurfaceBlendMode(src->s, &mode); + SDL_SetSurfaceBlendMode(src->s, SDL_BLENDMODE_NONE); + SDL_SetColorKey(src->s, SDL_TRUE, SDL_MapRGB(sdl.format, r, g, b)); + + sdl_blit(dst->s, dx, dy, src->s, sx, sy, w, h); + + SDL_SetColorKey(src->s, SDL_FALSE, 0); + SDL_SetSurfaceBlendMode(src->s, mode); +} + +void sdl_copy_use_amap_under(struct cg *dst, int dx, int dy, struct cg *src, int sx, int sy, int w, int h, int a_threshold) +{ + if (!src->has_alpha) + return; + + uint8_t r, g, b, a; + copy_for_each_pixel(dst, dx, dy, src, sx, sy, w, h, dp, sp, { + SDL_GetRGBA(*sp, sdl.format, &r, &g, &b, &a); + if (a <= a_threshold) { + PIXEL_SET_RGB(dp, r, g, b); + } + }); +} + +void sdl_copy_use_amap_border(struct cg *dst, int dx, int dy, struct cg *src, int sx, int sy, int w, int h, int a_threshold) +{ + if (!src->has_alpha) + return; + + uint8_t r, g, b, a; + copy_for_each_pixel(dst, dx, dy, src, sx, sy, w, h, dp, sp, { + SDL_GetRGBA(*sp, sdl.format, &r, &g, &b, &a); + if (a >= a_threshold) { + PIXEL_SET_RGB(dp, r, g, b); + } + }); +} + +void sdl_copy_amap_max(struct cg *dst, int dx, int dy, struct cg *src, int sx, int sy, int w, int h) +{ + if (!src->has_alpha || !dst->has_alpha) + return; + + copy_for_each_pixel(dst, dx, dy, src, sx, sy, w, h, dp, sp, { + uint32_t d_a = (*dp & sdl.format->Amask) >> sdl.format->Ashift; + uint32_t s_a = (*sp & sdl.format->Amask) >> sdl.format->Ashift; + if (s_a > d_a) { + PIXEL_SET_A(dp, s_a); + } + }); +} + +void sdl_copy_amap_min(struct cg *dst, int dx, int dy, struct cg *src, int sx, int sy, int w, int h) +{ + if (!src->has_alpha || !dst->has_alpha) + return; + + copy_for_each_pixel(dst, dx, dy, src, sx, sy, w, h, dp, sp, { + uint32_t d_a = (*dp & A_MASK) >> A_SHIFT; + uint32_t s_a = (*sp & A_MASK) >> A_SHIFT; + if (s_a < d_a) { + PIXEL_SET_A(dp, s_a); + } + }); +} + +static void blend_pixels(SDL_Color *dst, SDL_Color src) +{ + uint32_t alpha = src.a + 1; + uint32_t inv_alpha = 256 - src.a; + dst->r = (uint8_t)((alpha * src.r + inv_alpha * dst->r) >> 8); + dst->g = (uint8_t)((alpha * src.g + inv_alpha * dst->g) >> 8); + dst->b = (uint8_t)((alpha * src.b + inv_alpha * dst->b) >> 8); +} + +void sdl_fill(struct cg *dst, int x, int y, int w, int h, int r, int g, int b) +{ + fill_for_each_pixel(dst, x, y, w, h, p, { + PIXEL_SET_RGBA(p, r, g, b, PIXEL_A(*p)); + }); +} + +// XXX: Despite being called a fill operation, this function actually performs blending. +void sdl_fill_alpha_color(struct cg *cg, int x, int y, int w, int h, int r, int g, int b, int a) +{ + SDL_Color dst; + SDL_Color src = { .r = r, .g = g, .b = b, .a = a }; + fill_for_each_pixel(cg, x, y, w, h, p, { + SDL_GetRGBA(*p, sdl.format, &dst.r, &dst.g, &dst.b, &dst.a); + blend_pixels(&dst, src); + PIXEL_SET_RGBA(p, dst.r, dst.g, dst.b, dst.a); + }); +} + +void sdl_fill_amap(struct cg *dst, int x, int y, int w, int h, int a) +{ + fill_for_each_pixel(dst, x, y, w, h, p, { + PIXEL_SET_A(p, a); + }); } diff --git a/sdl_private.h b/sdl_private.h index bf3f3a6..eb4057f 100644 --- a/sdl_private.h +++ b/sdl_private.h @@ -29,19 +29,12 @@ struct sdl_private_data { SDL_Window *window; SDL_Renderer *renderer; - SDL_Texture *texture; - SDL_Surface *display; /* toplevel surface */ - SDL_Surface *dib; /* offscreen surface */ - SDL_Color col[256]; /* color pallet */ - unsigned long white; /* white pixel */ - //agsurface_t *cimg; - SDL_Rect view; + SDL_PixelFormat *format; + int w, h; //FONT *font; bool dirty; bool ms_active; /* mouse is active */ bool fs_on; - int winoffset_x; /* draw offset in Window x */ - int winoffset_y; /* y */ }; struct sdl_private_data sdl; diff --git a/sdl_video.c b/sdl_video.c index 064ab73..dd0faf7 100644 --- a/sdl_video.c +++ b/sdl_video.c @@ -21,31 +21,24 @@ #include "sdl_core.h" #include "sdl_private.h" +#define PIXEL_FORMAT SDL_PIXELFORMAT_ARGB32 + int sdl_initialize(void) { memset(&sdl, 0, sizeof(struct sdl_private_data)); // make toplevel window SDL_Init(SDL_INIT_VIDEO); + sdl.format = SDL_AllocFormat(PIXEL_FORMAT); sdl.window = SDL_CreateWindow("XSystem4", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, config.view_width, config.view_height, SDL_WINDOW_RESIZABLE); + sdl.renderer = SDL_CreateRenderer(sdl.window, -1, 0); - - // offscreen pixmap - //makeDIB(config.view_width, config.view_height, SYS4_DEFAULT_DEPTH); - - // init cursor - //sdl_cursor_init(); - - sdl_set_window_size(0, 0, config.view_width, config.view_height); - - //sdl_shadow_init(); - - //joy_open(); + sdl_set_window_size(config.view_width, config.view_height); return 0; } @@ -54,9 +47,7 @@ void sdl_remove(void) if (!sdl.window) return; - //SDL_FreeSurface(sdl.dib); SDL_DestroyRenderer(sdl.renderer); - //joy_close(); SDL_Quit(); } @@ -71,24 +62,64 @@ void sdl_fullscreen(bool on) } } -void sdl_set_window_size(int x, int y, int w, int h) +void sdl_set_window_size(int w, int h) { - sdl.view.x = x; - sdl.view.y = y; - - if (w == sdl.view.w && h == sdl.view.h) + if (w == sdl.w && h == sdl.h) return; - sdl.view.w = w; - sdl.view.h = h; + sdl.w = w; + sdl.h = h; SDL_SetWindowSize(sdl.window, w, h); SDL_RenderSetLogicalSize(sdl.renderer, w, h); - if (sdl.display) - SDL_FreeSurface(sdl.display); - if (sdl.texture) - SDL_DestroyTexture(sdl.texture); - sdl.display = SDL_CreateRGBSurface(0, w, h, 32, 0xFF00, 0xFF0000, 0xFF000000, 0xFF); - sdl.texture = SDL_CreateTexture(sdl.renderer, sdl.display->format->format, - SDL_TEXTUREACCESS_STATIC, w, h); +} + +SDL_Texture *sdl_create_texture(int w, int h) +{ + SDL_Texture *t = SDL_CreateTexture(sdl.renderer, PIXEL_FORMAT, SDL_TEXTUREACCESS_STATIC, w, h); + SDL_SetTextureBlendMode(t, SDL_BLENDMODE_BLEND); + return t; +} + +SDL_Surface *sdl_create_surface(int w, int h, SDL_Color *color) +{ + Rectangle rect = { .x = 0, .y = 0, .w = w, .h = w }; + SDL_Surface *s = SDL_CreateRGBSurfaceWithFormat(0, w, h, 32, SDL_PIXELFORMAT_ARGB32); + if (color) { + uint32_t c = SDL_MapRGBA(s->format, color->r, color->g, color->b, color->a); + SDL_FillRect(s, &rect, c); + } + return s; +} + +void sdl_update_texture(SDL_Texture *t, SDL_Surface *s) +{ + if (SDL_MUSTLOCK(s)) + SDL_LockSurface(s); + SDL_UpdateTexture(t, NULL, s->pixels, s->pitch); + if (SDL_MUSTLOCK(s)) + SDL_UnlockSurface(s); +} + +SDL_Texture *sdl_surface_to_texture(SDL_Surface *s) +{ + SDL_Texture *t = SDL_CreateTexture(sdl.renderer, PIXEL_FORMAT, SDL_TEXTUREACCESS_STATIC, s->w, s->h); + SDL_SetTextureBlendMode(t, SDL_BLENDMODE_BLEND); + SDL_UpdateTexture(t, NULL, s->pixels, s->pitch); + return t; +} + +void sdl_render_clear(void) +{ + SDL_RenderClear(sdl.renderer); +} + +void sdl_render_present(void) +{ + SDL_RenderPresent(sdl.renderer); +} + +void sdl_render_texture(SDL_Texture *t, Rectangle *r) +{ + SDL_RenderCopy(sdl.renderer, t, NULL, r); }