diff --git a/src/sdl_fader.c b/src/sdl_fader.c index 6e5d043..e40ceb9 100644 --- a/src/sdl_fader.c +++ b/src/sdl_fader.c @@ -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); }