Refactor graphics code

Sprites are now rendered directly, rather than being blitted to a master
surface which is then copied to a texture. Every sprite now carries an
SDL_Texture in addition to an SDL_Surface (we need both, since
SDL_Texture doesn't give read-access to the pixel data).

All of the image-manipulation code is now in sdl_draw.c, and the
DrawGraph_* functions are now wrappers. Some games (Daibanchou at least)
use an API other than SACT2/DrawGraph, so these operations should be
generic to avoid duplication.
This commit is contained in:
Nunuhara Cabbage
2019-11-29 23:16:40 -08:00
parent ae6b558813
commit 4eb5a256fd
11 changed files with 437 additions and 371 deletions
+9 -9
View File
@@ -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;
}
+2
View File
@@ -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 {
+7 -18
View File
@@ -22,30 +22,19 @@
#include <stdint.h>
#include <SDL.h>
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 */
+20 -6
View File
@@ -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;
}
+1 -1
View File
@@ -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__ */
+1
View File
@@ -34,6 +34,7 @@ struct sact_sprite {
struct {
struct string *str;
SDL_Surface *surf;
SDL_Texture *t;
Point home;
Point pos;
int char_space;
+51 -255
View File
@@ -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);
}
+24 -4
View File
@@ -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,
+261 -41
View File
@@ -1,5 +1,4 @@
/* Copyright (C) 2000- Fumihiko Murata <fmurata@p1.tcnet.ne.jp>
* 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
/* 2019 Nunuhara Cabbage <nunuhara@haniwa.technology>
*
* 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);
});
}
+2 -9
View File
@@ -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;
+59 -28
View File
@@ -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);
}