Refactor sdl_fader.c

Restructured the code in an object-oriented style to make it easier to
add new kinds of faders.
This commit is contained in:
kichikuou
2022-01-04 17:21:01 +09:00
parent 5fefd74d4a
commit 0a1d0a5a76
+139 -74
View File
@@ -27,11 +27,7 @@
#include "sdl_core.h"
#include "sdl_private.h"
struct sdl_fader {
SDL_Texture *tx_old, *tx_new, *tx_tmp;
SDL_Rect dst_rect;
enum sdl_effect effect;
};
#define HAS_SDL_RenderGeometry SDL_VERSION_ATLEAST(2, 0, 18)
enum sdl_effect from_nact_effect(enum nact_effect effect) {
switch (effect) {
@@ -61,7 +57,102 @@ enum sdl_effect from_sact_effect(enum sact_effect effect) {
}
}
#define HAS_SDL_RenderGeometry SDL_VERSION_ATLEAST(2, 0, 18)
struct sdl_fader {
void (*step)(struct sdl_fader *this, int step);
void (*finish)(struct sdl_fader *this);
enum sdl_effect effect;
SDL_Rect dst_rect;
SDL_Texture *tx_old, *tx_new;
};
static void fader_init(struct sdl_fader *fader, int sx, int sy, int w, int h, int dx, int dy, enum sdl_effect effect) {
fader->effect = effect;
fader->dst_rect.x = dx;
fader->dst_rect.y = dy;
fader->dst_rect.w = w;
fader->dst_rect.h = h;
SDL_Surface *sf_old = SDL_CreateRGBSurfaceWithFormat(0, w, h, 32, SDL_PIXELFORMAT_RGB888);
SDL_Surface *sf_new = SDL_CreateRGBSurfaceWithFormat(0, w, h, 32, SDL_PIXELFORMAT_RGB888);
SDL_Rect src_rect = { sx, sy, w, h };
SDL_BlitSurface(sdl_display, &fader->dst_rect, sf_old, NULL);
SDL_BlitSurface(sdl_dib, &src_rect, sf_new, NULL);
fader->tx_old = SDL_CreateTextureFromSurface(sdl_renderer, sf_old);
fader->tx_new = SDL_CreateTextureFromSurface(sdl_renderer, sf_new);
SDL_FreeSurface(sf_old);
SDL_FreeSurface(sf_new);
sdl_copyArea(sx, sy, w, h, dx, dy);
}
static void fader_finish(struct sdl_fader *fader) {
SDL_RenderCopy(sdl_renderer, fader->tx_new, NULL, &fader->dst_rect);
SDL_RenderPresent(sdl_renderer);
SDL_DestroyTexture(fader->tx_old);
SDL_DestroyTexture(fader->tx_new);
}
// EFFECT_CROSSFADE
static void crossfade_step(struct sdl_fader *fader, int step);
static void crossfade_free(struct sdl_fader *fader);
static struct sdl_fader *crossfade_new(int sx, int sy, int w, int h, int dx, int dy) {
struct sdl_fader *fader = calloc(1, sizeof(struct sdl_fader));
if (!fader)
NOMEMERR();
fader_init(fader, sx, sy, w, h, dx, dy, EFFECT_CROSSFADE);
fader->step = crossfade_step;
fader->finish = crossfade_free;
return fader;
}
static void crossfade_step(struct sdl_fader *fader, int step) {
SDL_RenderCopy(sdl_renderer, fader->tx_old, NULL, &fader->dst_rect);
SDL_SetTextureBlendMode(fader->tx_new, SDL_BLENDMODE_BLEND);
SDL_SetTextureAlphaMod(fader->tx_new, step * 255 / SDL_FADER_MAXSTEP);
SDL_RenderCopy(sdl_renderer, fader->tx_new, NULL, &fader->dst_rect);
SDL_SetTextureBlendMode(fader->tx_new, SDL_BLENDMODE_NONE);
SDL_RenderPresent(sdl_renderer);
}
static void crossfade_free(struct sdl_fader *fader) {
fader_finish(fader);
free(fader);
}
// EFFECT_PENTAGRAM_*, EFFECT_HEXAGRAM_*, EFFECT_WINDMILL*
struct polygon_mask_fader {
struct sdl_fader f;
SDL_Texture *tx_tmp;
};
static void polygon_mask_step(struct sdl_fader *fader, int step);
static void polygon_mask_free(struct sdl_fader *fader);
static struct sdl_fader *polygon_mask_new(int sx, int sy, int w, int h, int dx, int dy, enum sdl_effect effect) {
#if HAS_SDL_RenderGeometry
if (SDL_RenderTargetSupported(sdl_renderer)) {
struct polygon_mask_fader *pmf = calloc(1, sizeof(struct polygon_mask_fader));
if (!pmf)
NOMEMERR();
fader_init(&pmf->f, sx, sy, w, h, dx, dy, effect);
pmf->tx_tmp = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, w, h);
pmf->f.step = polygon_mask_step;
pmf->f.finish = polygon_mask_free;
return &pmf->f;
}
#endif // HAS_SDL_RenderGeometry
WARNING("Effect %d is not supported in this system. Falling back to crossfade.\n", effect);
return crossfade_new(sx, sy, w, h, dx, dy);
}
#if HAS_SDL_RenderGeometry
static void draw_pentagram(int center_x, int center_y, double radius, double rotate) {
@@ -248,11 +339,12 @@ static void draw_windmill_360(int w, int h, double theta) {
SDL_RenderGeometry(sdl_renderer, NULL, v, 10, indices, i);
}
static void step_polygon_mask(struct sdl_fader *fader, int step) {
static void polygon_mask_step(struct sdl_fader *fader, int step) {
struct polygon_mask_fader *this = (struct polygon_mask_fader *)fader;
int w = fader->dst_rect.w;
int h = fader->dst_rect.h;
double t = (double)step / SDL_FADER_MAXSTEP;
SDL_SetRenderTarget(sdl_renderer, fader->tx_tmp);
SDL_SetRenderTarget(sdl_renderer, this->tx_tmp);
SDL_RenderCopy(sdl_renderer, fader->tx_old, NULL, NULL);
switch (fader->effect) {
case EFFECT_PENTAGRAM_IN_OUT:
@@ -281,23 +373,36 @@ static void step_polygon_mask(struct sdl_fader *fader, int step) {
}
SDL_SetRenderTarget(sdl_renderer, NULL);
SDL_RenderCopy(sdl_renderer, fader->tx_new, NULL, &fader->dst_rect);
SDL_SetTextureBlendMode(fader->tx_tmp, SDL_BLENDMODE_BLEND);
SDL_RenderCopy(sdl_renderer, fader->tx_tmp, NULL, &fader->dst_rect);
SDL_SetTextureBlendMode(this->tx_tmp, SDL_BLENDMODE_BLEND);
SDL_RenderCopy(sdl_renderer, this->tx_tmp, NULL, &fader->dst_rect);
SDL_RenderPresent(sdl_renderer);
}
static void polygon_mask_free(struct sdl_fader *fader) {
struct polygon_mask_fader *this = (struct polygon_mask_fader *)fader;
SDL_DestroyTexture(this->tx_tmp);
fader_finish(&this->f);
free(this);
}
#endif // HAS_SDL_RenderGeometry
static void step_crossfade(struct sdl_fader *fader, int step) {
SDL_RenderCopy(sdl_renderer, fader->tx_old, NULL, &fader->dst_rect);
SDL_SetTextureBlendMode(fader->tx_new, SDL_BLENDMODE_BLEND);
SDL_SetTextureAlphaMod(fader->tx_new, step * 255 / SDL_FADER_MAXSTEP);
SDL_RenderCopy(sdl_renderer, fader->tx_new, NULL, &fader->dst_rect);
SDL_SetTextureBlendMode(fader->tx_new, SDL_BLENDMODE_NONE);
SDL_RenderPresent(sdl_renderer);
// EFFECT_ROTATE_*
static void rotate_step(struct sdl_fader *fader, int step);
static void rotate_free(struct sdl_fader *fader);
static struct sdl_fader *rotate_new(int sx, int sy, int w, int h, int dx, int dy, enum sdl_effect effect) {
struct sdl_fader *fader = calloc(1, sizeof(struct sdl_fader));
if (!fader)
NOMEMERR();
fader_init(fader, sx, sy, w, h, dx, dy, effect);
fader->step = rotate_step;
fader->finish = rotate_free;
return fader;
}
static void step_rotate(struct sdl_fader *fader, int step) {
static void rotate_step(struct sdl_fader *fader, int step) {
SDL_Texture *bg_texture, *fg_texture;
double angle = step * 360.0 / SDL_FADER_MAXSTEP;
double scale = step / (double)SDL_FADER_MAXSTEP;
@@ -335,49 +440,16 @@ static void step_rotate(struct sdl_fader *fader, int step) {
SDL_RenderPresent(sdl_renderer);
}
struct sdl_fader *sdl_fader_init(int sx, int sy, int w, int h, int dx, int dy, enum sdl_effect effect) {
if (EFFECT_PENTAGRAM_IN_OUT <= effect && effect <= EFFECT_WINDMILL_360 &&
(!SDL_RenderTargetSupported(sdl_renderer) || !HAS_SDL_RenderGeometry)) {
WARNING("Effect %d is not supported in this system. Falling back to crossfade.\n", effect);
effect = EFFECT_CROSSFADE;
}
struct sdl_fader *fader = calloc(1, sizeof(struct sdl_fader));
if (!fader)
NOMEMERR();
fader->dst_rect.x = dx;
fader->dst_rect.y = dy;
fader->dst_rect.w = w;
fader->dst_rect.h = h;
fader->effect = effect;
SDL_Surface *sf_old = SDL_CreateRGBSurfaceWithFormat(0, w, h, 32, SDL_PIXELFORMAT_RGB888);
SDL_Surface *sf_new = SDL_CreateRGBSurfaceWithFormat(0, w, h, 32, SDL_PIXELFORMAT_RGB888);
SDL_Rect src_rect = { sx, sy, w, h };
SDL_BlitSurface(sdl_display, &fader->dst_rect, sf_old, NULL);
SDL_BlitSurface(sdl_dib, &src_rect, sf_new, NULL);
fader->tx_old = SDL_CreateTextureFromSurface(sdl_renderer, sf_old);
fader->tx_new = SDL_CreateTextureFromSurface(sdl_renderer, sf_new);
SDL_FreeSurface(sf_old);
SDL_FreeSurface(sf_new);
if (effect != EFFECT_CROSSFADE)
fader->tx_tmp = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, w, h);
sdl_copyArea(sx, sy, w, h, dx, dy);
return fader;
static void rotate_free(struct sdl_fader *fader) {
fader_finish(fader);
free(fader);
}
void sdl_fader_step(struct sdl_fader *fader, int step) {
switch (fader->effect) {
struct sdl_fader *sdl_fader_init(int sx, int sy, int w, int h, int dx, int dy, enum sdl_effect effect) {
switch (effect) {
case EFFECT_CROSSFADE:
step_crossfade(fader, step);
break;
#if HAS_SDL_RenderGeometry
return crossfade_new(sx, sy, w, h, dx, dy);
case EFFECT_PENTAGRAM_IN_OUT:
case EFFECT_PENTAGRAM_OUT_IN:
case EFFECT_HEXAGRAM_IN_OUT:
@@ -385,29 +457,22 @@ void sdl_fader_step(struct sdl_fader *fader, int step) {
case EFFECT_WINDMILL:
case EFFECT_WINDMILL_180:
case EFFECT_WINDMILL_360:
step_polygon_mask(fader, step);
break;
#endif
return polygon_mask_new(sx, sy, w, h, dx, dy, effect);
case EFFECT_ROTATE_OUT:
case EFFECT_ROTATE_IN:
case EFFECT_ROTATE_OUT_CW:
case EFFECT_ROTATE_IN_CW:
step_rotate(fader, step);
break;
return rotate_new(sx, sy, w, h, dx, dy, effect);
default:
if (step == 0)
WARNING("Unknown effect %d\n", fader->effect);
break;
WARNING("Unknown effect %d\n", effect);
return crossfade_new(sx, sy, w, h, dx, dy);
}
}
void sdl_fader_step(struct sdl_fader *fader, int step) {
fader->step(fader, step);
}
void sdl_fader_finish(struct sdl_fader *fader) {
SDL_RenderCopy(sdl_renderer, fader->tx_new, NULL, &fader->dst_rect);
SDL_RenderPresent(sdl_renderer);
SDL_DestroyTexture(fader->tx_old);
SDL_DestroyTexture(fader->tx_new);
if (fader->tx_tmp)
SDL_DestroyTexture(fader->tx_tmp);
free(fader);
fader->finish(fader);
}