mirror of
https://github.com/kichikuou/xsystem35-sdl2.git
synced 2026-09-25 07:58:04 +03:00
1927 lines
59 KiB
C
1927 lines
59 KiB
C
/*
|
|
* Copyright (C) 2021 <KichikuouChrome@gmail.com>
|
|
*
|
|
* 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
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
#include <SDL.h>
|
|
|
|
#include "system.h"
|
|
#include "gfx.h"
|
|
#include "effect.h"
|
|
#include "gfx_private.h"
|
|
|
|
#define M_PIf ((float)M_PI)
|
|
#define HAS_SDL_RenderGeometry SDL_VERSION_ATLEAST(2, 0, 18)
|
|
|
|
enum effect_type from_nact_effect(enum nact_effect effect) {
|
|
switch (effect) {
|
|
case NACT_EFFECT_PAN_IN_DOWN: return EFFECT_PAN_IN_DOWN;
|
|
case NACT_EFFECT_PAN_IN_UP: return EFFECT_PAN_IN_UP;
|
|
case NACT_EFFECT_SKIP_LINE_UP_DOWN: return EFFECT_SKIP_LINE_UP_DOWN;
|
|
case NACT_EFFECT_SKIP_LINE_LR_RL: return EFFECT_SKIP_LINE_LR_RL;
|
|
case NACT_EFFECT_WIPE_IN: return EFFECT_WIPE_IN;
|
|
case NACT_EFFECT_WIPE_OUT: return EFFECT_WIPE_OUT;
|
|
case NACT_EFFECT_ZOOM_IN: return EFFECT_ZOOM_IN;
|
|
case NACT_EFFECT_BLIND_DOWN: return EFFECT_BLIND_DOWN;
|
|
case NACT_EFFECT_WIPE_LR: return EFFECT_WIPE_LR;
|
|
case NACT_EFFECT_WIPE_RL: return EFFECT_WIPE_RL;
|
|
case NACT_EFFECT_WIPE_DOWN: return EFFECT_WIPE_DOWN;
|
|
case NACT_EFFECT_WIPE_UP: return EFFECT_WIPE_UP;
|
|
case NACT_EFFECT_WIPE_OUT_V: return EFFECT_WIPE_OUT_V;
|
|
case NACT_EFFECT_WIPE_IN_V: return EFFECT_WIPE_IN_V;
|
|
case NACT_EFFECT_WIPE_OUT_H: return EFFECT_WIPE_OUT_H;
|
|
case NACT_EFFECT_WIPE_IN_H: return EFFECT_WIPE_IN_H;
|
|
case NACT_EFFECT_MOSAIC: return EFFECT_MOSAIC;
|
|
case NACT_EFFECT_CIRCLE_WIPE_OUT: return EFFECT_CIRCLE_WIPE_OUT;
|
|
case NACT_EFFECT_CIRCLE_WIPE_IN: return EFFECT_CIRCLE_WIPE_IN;
|
|
case NACT_EFFECT_FADEIN: return EFFECT_FADEIN;
|
|
case NACT_EFFECT_WHITEIN: return EFFECT_WHITEIN;
|
|
case NACT_EFFECT_FADEOUT: return EFFECT_FADEOUT_FROM_NEW;
|
|
case NACT_EFFECT_WHITEOUT: return EFFECT_WHITEOUT_FROM_NEW;
|
|
case NACT_EFFECT_CROSSFADE: return EFFECT_CROSSFADE;
|
|
case NACT_EFFECT_CROSSFADE_MOSAIC: return EFFECT_CROSSFADE_MOSAIC;
|
|
case NACT_EFFECT_BLIND_UP: return EFFECT_BLIND_UP;
|
|
case NACT_EFFECT_BLIND_UP_DOWN: return EFFECT_BLIND_UP_DOWN;
|
|
case NACT_EFFECT_CROSSFADE_DOWN: return EFFECT_CROSSFADE_DOWN;
|
|
case NACT_EFFECT_CROSSFADE_UP: return EFFECT_CROSSFADE_UP;
|
|
case NACT_EFFECT_CROSSFADE_LR: return EFFECT_CROSSFADE_LR;
|
|
case NACT_EFFECT_CROSSFADE_RL: return EFFECT_CROSSFADE_RL;
|
|
case NACT_EFFECT_BLEND_UP_DOWN: return EFFECT_BLEND_UP_DOWN;
|
|
case NACT_EFFECT_BLEND_LR_RL: return EFFECT_BLEND_LR_RL;
|
|
case NACT_EFFECT_CROSSFADE_LR_RL: return EFFECT_CROSSFADE_LR_RL;
|
|
case NACT_EFFECT_CROSSFADE_UP_DOWN: return EFFECT_CROSSFADE_UP_DOWN;
|
|
case NACT_EFFECT_MAGNIFY: return EFFECT_MAGNIFY;
|
|
case NACT_EFFECT_PENTAGRAM_IN_OUT: return EFFECT_PENTAGRAM_IN_OUT;
|
|
case NACT_EFFECT_PENTAGRAM_OUT_IN: return EFFECT_PENTAGRAM_OUT_IN;
|
|
case NACT_EFFECT_HEXAGRAM_IN_OUT: return EFFECT_HEXAGRAM_IN_OUT;
|
|
case NACT_EFFECT_HEXAGRAM_OUT_IN: return EFFECT_HEXAGRAM_OUT_IN;
|
|
case NACT_EFFECT_BLIND_LR: return EFFECT_BLIND_LR;
|
|
case NACT_EFFECT_BLIND_RL: return EFFECT_BLIND_RL;
|
|
case NACT_EFFECT_WINDMILL: return EFFECT_WINDMILL;
|
|
case NACT_EFFECT_WINDMILL_180: return EFFECT_WINDMILL_180;
|
|
case NACT_EFFECT_WINDMILL_360: return EFFECT_WINDMILL_360;
|
|
case NACT_EFFECT_LINEAR_BLUR: return EFFECT_LINEAR_BLUR;
|
|
default: return EFFECT_INVALID;
|
|
}
|
|
}
|
|
|
|
enum effect_type from_nact_sprite_effect(enum nact_effect effect) {
|
|
switch (effect) {
|
|
case NACT_EFFECT_PAN_IN_DOWN: return EFFECT_PAN_IN_DOWN;
|
|
case NACT_EFFECT_PAN_IN_UP: return EFFECT_PAN_IN_UP;
|
|
case NACT_EFFECT_SKIP_LINE_UP_DOWN: return EFFECT_SKIP_LINE_UP_DOWN;
|
|
case NACT_EFFECT_SKIP_LINE_LR_RL: return EFFECT_SKIP_LINE_LR_RL;
|
|
case NACT_SP_EFFECT_RASTER_BLEND: return EFFECT_RASTER_BLEND;
|
|
default: return EFFECT_INVALID;
|
|
}
|
|
}
|
|
|
|
enum effect_type from_sact_effect(enum sact_effect effect) {
|
|
switch (effect) {
|
|
case SACT_EFFECT_CROSSFADE: return EFFECT_CROSSFADE;
|
|
case SACT_EFFECT_FADEOUT: return EFFECT_FADEOUT;
|
|
case SACT_EFFECT_FADEIN: return EFFECT_FADEIN;
|
|
case SACT_EFFECT_WHITEOUT: return EFFECT_WHITEOUT;
|
|
case SACT_EFFECT_WHITEIN: return EFFECT_WHITEIN;
|
|
case SACT_EFFECT_CROSSFADE_MOSAIC: return EFFECT_CROSSFADE_MOSAIC;
|
|
case SACT_EFFECT_BLIND_DOWN: return EFFECT_BLIND_DOWN;
|
|
case SACT_EFFECT_BLIND_LR: return EFFECT_BLIND_LR;
|
|
case SACT_EFFECT_BLIND_DOWN_LR: return EFFECT_BLIND_DOWN_LR;
|
|
case SACT_EFFECT_ZOOM_BLEND_BLUR: return EFFECT_ZOOM_BLEND_BLUR;
|
|
case SACT_EFFECT_LINEAR_BLUR: return EFFECT_LINEAR_BLUR;
|
|
case SACT_EFFECT_CROSSFADE_DOWN: return EFFECT_CROSSFADE_DOWN;
|
|
case SACT_EFFECT_CROSSFADE_UP: return EFFECT_CROSSFADE_UP;
|
|
case SACT_EFFECT_PENTAGRAM_IN_OUT: return EFFECT_PENTAGRAM_IN_OUT;
|
|
case SACT_EFFECT_PENTAGRAM_OUT_IN: return EFFECT_PENTAGRAM_OUT_IN;
|
|
case SACT_EFFECT_HEXAGRAM_IN_OUT: return EFFECT_HEXAGRAM_IN_OUT;
|
|
case SACT_EFFECT_HEXAGRAM_OUT_IN: return EFFECT_HEXAGRAM_OUT_IN;
|
|
case SACT_EFFECT_LINEAR_BLUR_VERT: return EFFECT_LINEAR_BLUR_VERT;
|
|
case SACT_EFFECT_ROTATE_OUT: return EFFECT_ROTATE_OUT;
|
|
case SACT_EFFECT_ROTATE_IN: return EFFECT_ROTATE_IN;
|
|
case SACT_EFFECT_ROTATE_OUT_CW: return EFFECT_ROTATE_OUT_CW;
|
|
case SACT_EFFECT_ROTATE_IN_CW: return EFFECT_ROTATE_IN_CW;
|
|
case SACT_EFFECT_POLYGON_ROTATE_Y: return EFFECT_POLYGON_ROTATE_Y;
|
|
case SACT_EFFECT_POLYGON_ROTATE_Y_CW: return EFFECT_POLYGON_ROTATE_Y_CW;
|
|
case SACT_EFFECT_POLYGON_ROTATE_X: return EFFECT_POLYGON_ROTATE_X;
|
|
case SACT_EFFECT_POLYGON_ROTATE_X_CW: return EFFECT_POLYGON_ROTATE_X_CW;
|
|
case SACT_EFFECT_ZIGZAG_CROSSFADE: return EFFECT_ZIGZAG_CROSSFADE;
|
|
default: return EFFECT_INVALID;
|
|
}
|
|
}
|
|
|
|
typedef struct {
|
|
SDL_Texture *tx;
|
|
SDL_Rect rect;
|
|
} EffectTexture;
|
|
|
|
static EffectTexture *create_effect_texture(surface_t *as, int x, int y, int w, int h) {
|
|
EffectTexture *t = calloc(1, sizeof(EffectTexture));
|
|
if (!as) {
|
|
t->tx = gfx_texture;
|
|
t->rect = (SDL_Rect){ x, y, w, h };
|
|
} else {
|
|
SDL_Surface *sf = gfx_createSurfaceView(as->sdl_surface, x, y, w, h);
|
|
t->tx = SDL_CreateTextureFromSurface(gfx_renderer, sf);
|
|
SDL_FreeSurface(sf);
|
|
t->rect = (SDL_Rect){ 0, 0, w, h };
|
|
}
|
|
return t;
|
|
}
|
|
|
|
static EffectTexture *create_effect_texture_from_surface(SDL_Surface *sf) {
|
|
EffectTexture *t = calloc(1, sizeof(EffectTexture));
|
|
t->tx = SDL_CreateTextureFromSurface(gfx_renderer, sf);
|
|
t->rect = (SDL_Rect){ 0, 0, sf->w, sf->h };
|
|
return t;
|
|
}
|
|
|
|
static void destroy_effect_texture(EffectTexture *t) {
|
|
if (!t)
|
|
return;
|
|
if (t->tx && t->tx != gfx_texture)
|
|
SDL_DestroyTexture(t->tx);
|
|
free(t);
|
|
}
|
|
|
|
static int render_effect_texture(EffectTexture *t, const SDL_Rect* srcrect, const SDL_Rect* dstrect) {
|
|
SDL_Rect real_srcrect;
|
|
if (!srcrect) {
|
|
real_srcrect = t->rect;
|
|
} else {
|
|
real_srcrect = *srcrect;
|
|
real_srcrect.x += t->rect.x;
|
|
real_srcrect.y += t->rect.y;
|
|
}
|
|
return SDL_RenderCopy(gfx_renderer, t->tx, &real_srcrect, dstrect);
|
|
}
|
|
|
|
struct effect {
|
|
void (*step)(struct effect *this, float progress);
|
|
void (*finish)(struct effect *this);
|
|
enum effect_type type;
|
|
SDL_Rect dst_rect;
|
|
bool is_fullscreen;
|
|
EffectTexture *tx_old, *tx_new;
|
|
};
|
|
|
|
static void eff_init(struct effect *eff, SDL_Rect *rect, EffectTexture *old, EffectTexture *new, enum effect_type type) {
|
|
eff->type = type;
|
|
eff->dst_rect = *rect;
|
|
eff->is_fullscreen = rect->x == 0 && rect->y == 0
|
|
&& rect->w == view_w && rect->h == view_h;
|
|
eff->tx_old = old;
|
|
eff->tx_new = new;
|
|
|
|
if (!eff->is_fullscreen)
|
|
gfx_updateScreen(); // Flush pending display changes.
|
|
}
|
|
|
|
static void eff_finish(struct effect *eff, bool present) {
|
|
if (present) {
|
|
SDL_RenderClear(gfx_renderer);
|
|
if (!eff->is_fullscreen)
|
|
SDL_RenderCopy(gfx_renderer, gfx_texture, NULL, NULL);
|
|
render_effect_texture(eff->tx_new, NULL, &eff->dst_rect);
|
|
SDL_RenderPresent(gfx_renderer);
|
|
}
|
|
destroy_effect_texture(eff->tx_old);
|
|
destroy_effect_texture(eff->tx_new);
|
|
}
|
|
|
|
static inline void flip_rect_h(SDL_Rect *r, int w) {
|
|
r->x = w - r->x - r->w;
|
|
}
|
|
|
|
static inline void flip_rect_v(SDL_Rect *r, int h) {
|
|
r->y = h - r->y - r->h;
|
|
}
|
|
|
|
static void transpose_rect(SDL_Rect *r) {
|
|
int tx = r->x;
|
|
r->x = r->y;
|
|
r->y = tx;
|
|
int tw = r->w;
|
|
r->w = r->h;
|
|
r->h = tw;
|
|
}
|
|
|
|
static inline void move_rect(SDL_Rect *r, int off_x, int off_y) {
|
|
r->x += off_x;
|
|
r->y += off_y;
|
|
}
|
|
|
|
// EFFECT_CROSSFADE
|
|
|
|
static void crossfade_step(struct effect *eff, float progress);
|
|
static void crossfade_free(struct effect *eff);
|
|
|
|
static struct effect *crossfade_new(SDL_Rect *rect, EffectTexture *old, EffectTexture *new) {
|
|
struct effect *eff = calloc(1, sizeof(struct effect));
|
|
if (!eff)
|
|
NOMEMERR();
|
|
eff_init(eff, rect, old, new, EFFECT_CROSSFADE);
|
|
eff->step = crossfade_step;
|
|
eff->finish = crossfade_free;
|
|
return eff;
|
|
}
|
|
|
|
static void crossfade_step(struct effect *eff, float progress) {
|
|
render_effect_texture(eff->tx_old, NULL, &eff->dst_rect);
|
|
SDL_SetTextureBlendMode(eff->tx_new->tx, SDL_BLENDMODE_BLEND);
|
|
SDL_SetTextureAlphaMod(eff->tx_new->tx, progress * 255);
|
|
render_effect_texture(eff->tx_new, NULL, &eff->dst_rect);
|
|
SDL_SetTextureAlphaMod(eff->tx_new->tx, 255);
|
|
SDL_SetTextureBlendMode(eff->tx_new->tx, SDL_BLENDMODE_NONE);
|
|
SDL_RenderPresent(gfx_renderer);
|
|
}
|
|
|
|
static void crossfade_free(struct effect *eff) {
|
|
eff_finish(eff, true);
|
|
free(eff);
|
|
}
|
|
|
|
static struct effect *fallback_effect_new(SDL_Rect *rect, EffectTexture *old, EffectTexture *new, enum effect_type type) {
|
|
WARNING("Effect %d is not supported in this system. Falling back to crossfade.", type);
|
|
return crossfade_new(rect, old, new);
|
|
}
|
|
|
|
// EFFECT_CROSSFADE_{UP,DOWN,LR,RL}
|
|
|
|
static void crossfade_animation_step(struct effect *eff, float progress);
|
|
static void crossfade_animation_free(struct effect *eff);
|
|
|
|
static struct effect *crossfade_animation_new(SDL_Rect *rect, EffectTexture *old, EffectTexture *new, enum effect_type type) {
|
|
struct effect *eff = calloc(1, sizeof(struct effect));
|
|
if (!eff)
|
|
NOMEMERR();
|
|
eff_init(eff, rect, old, new, type);
|
|
eff->step = crossfade_animation_step;
|
|
eff->finish = crossfade_animation_free;
|
|
return eff;
|
|
}
|
|
|
|
static void crossfade_animation_step_sub(SDL_Rect *rect, EffectTexture *tx, int tx_x, int tx_y, float progress, int band_width, bool lr, bool flip) {
|
|
int maxstep = (lr ? rect->w : rect->h) + band_width;
|
|
int band_top = maxstep * progress - band_width;
|
|
if (band_top > 0) {
|
|
SDL_Rect sr = {0, 0, rect->w, rect->h};
|
|
*(lr ? &sr.w : &sr.h) = band_top;
|
|
if (flip) {
|
|
if (lr)
|
|
flip_rect_h(&sr, rect->w);
|
|
else
|
|
flip_rect_v(&sr, rect->h);
|
|
}
|
|
SDL_Rect dr = sr;
|
|
move_rect(&sr, tx_x, tx_y);
|
|
move_rect(&dr, rect->x, rect->y);
|
|
render_effect_texture(tx, &sr, &dr);
|
|
}
|
|
|
|
SDL_SetTextureBlendMode(tx->tx, SDL_BLENDMODE_BLEND);
|
|
for (int i = 0; i < band_width; i++) {
|
|
SDL_Rect sr;
|
|
if (lr) {
|
|
int x = band_top + i;
|
|
if (x < 0 || x >= rect->w)
|
|
continue;
|
|
sr = (SDL_Rect){x, 0, 1, rect->h};
|
|
if (flip)
|
|
flip_rect_h(&sr, rect->w);
|
|
} else {
|
|
int y = band_top + i;
|
|
if (y < 0 || y >= rect->h)
|
|
continue;
|
|
sr = (SDL_Rect){0, y, rect->w, 1};
|
|
if (flip)
|
|
flip_rect_v(&sr, rect->h);
|
|
}
|
|
SDL_Rect dr = sr;
|
|
move_rect(&sr, tx_x, tx_y);
|
|
move_rect(&dr, rect->x, rect->y);
|
|
SDL_SetTextureAlphaMod(tx->tx, 255 - (i + 1) * (256 / band_width));
|
|
render_effect_texture(tx, &sr, &dr);
|
|
}
|
|
SDL_SetTextureAlphaMod(tx->tx, 255);
|
|
SDL_SetTextureBlendMode(tx->tx, SDL_BLENDMODE_NONE);
|
|
}
|
|
|
|
static void crossfade_animation_step(struct effect *eff, float progress) {
|
|
render_effect_texture(eff->tx_old, NULL, &eff->dst_rect);
|
|
|
|
switch (eff->type) {
|
|
case EFFECT_CROSSFADE_DOWN:
|
|
crossfade_animation_step_sub(&eff->dst_rect, eff->tx_new, 0, 0, progress, 254, false, false);
|
|
break;
|
|
case EFFECT_CROSSFADE_UP:
|
|
crossfade_animation_step_sub(&eff->dst_rect, eff->tx_new, 0, 0, progress, 254, false, true);
|
|
break;
|
|
case EFFECT_CROSSFADE_LR:
|
|
crossfade_animation_step_sub(&eff->dst_rect, eff->tx_new, 0, 0, progress, 254, true, false);
|
|
break;
|
|
case EFFECT_CROSSFADE_RL:
|
|
crossfade_animation_step_sub(&eff->dst_rect, eff->tx_new, 0, 0, progress, 254, true, true);
|
|
break;
|
|
case EFFECT_CROSSFADE_UP_DOWN:
|
|
{
|
|
SDL_Rect dr = eff->dst_rect;
|
|
int th = dr.h /= 2;
|
|
dr.h = th;
|
|
crossfade_animation_step_sub(&dr, eff->tx_new, 0, 0, progress, 127, false, true);
|
|
dr.y += th;
|
|
dr.h = eff->dst_rect.h - th;
|
|
crossfade_animation_step_sub(&dr, eff->tx_new, 0, th, progress, 127, false, false);
|
|
}
|
|
break;
|
|
case EFFECT_CROSSFADE_LR_RL:
|
|
{
|
|
SDL_Rect dr = eff->dst_rect;
|
|
int lw = dr.w /= 2;
|
|
dr.w = lw;
|
|
crossfade_animation_step_sub(&dr, eff->tx_new, 0, 0, progress, 127, true, true);
|
|
dr.x += lw;
|
|
dr.w = eff->dst_rect.w - lw;
|
|
crossfade_animation_step_sub(&dr, eff->tx_new, lw, 0, progress, 127, true, false);
|
|
}
|
|
break;
|
|
default:
|
|
assert(!"Cannot happen");
|
|
}
|
|
|
|
SDL_RenderPresent(gfx_renderer);
|
|
}
|
|
|
|
static void crossfade_animation_free(struct effect *eff) {
|
|
eff_finish(eff, false);
|
|
free(eff);
|
|
}
|
|
|
|
// EFFECT_MOSAIC, EFFECT_CROSSFADE_MOSAIC
|
|
|
|
struct mosaic_effect {
|
|
struct effect eff;
|
|
SDL_Texture *tmp_old, *tmp_new, *tx_mosaic;
|
|
};
|
|
|
|
static void mosaic_step(struct effect *eff, float progress);
|
|
static void mosaic_free(struct effect *eff);
|
|
|
|
static struct effect *mosaic_new(SDL_Rect *rect, EffectTexture *old, EffectTexture *new, enum effect_type type) {
|
|
if (!SDL_RenderTargetSupported(gfx_renderer))
|
|
return fallback_effect_new(rect, old, new, type);
|
|
|
|
struct mosaic_effect *eff = calloc(1, sizeof(struct mosaic_effect));
|
|
if (!eff)
|
|
NOMEMERR();
|
|
eff_init(&eff->eff, rect, old, new, type);
|
|
eff->tmp_old = SDL_CreateTexture(gfx_renderer, SDL_PIXELFORMAT_RGB888, SDL_TEXTUREACCESS_TARGET, rect->w, rect->h);
|
|
eff->tmp_new = SDL_CreateTexture(gfx_renderer, SDL_PIXELFORMAT_RGB888, SDL_TEXTUREACCESS_TARGET, rect->w, rect->h);
|
|
eff->tx_mosaic = SDL_CreateTexture(gfx_renderer, SDL_PIXELFORMAT_RGB888, SDL_TEXTUREACCESS_TARGET, rect->w, rect->h);
|
|
eff->eff.step = mosaic_step;
|
|
eff->eff.finish = mosaic_free;
|
|
return &eff->eff;
|
|
}
|
|
|
|
static void mosaic(EffectTexture *src, SDL_Texture *tmp, SDL_Texture *dst, int w, int h, float scale) {
|
|
int cx = w / 2;
|
|
int cy = h / 2;
|
|
SDL_FRect fr = {
|
|
.x = cx - cx / scale,
|
|
.y = cy - cy / scale,
|
|
.w = w / scale,
|
|
.h = h / scale,
|
|
};
|
|
SDL_SetRenderTarget(gfx_renderer, tmp);
|
|
SDL_RenderCopyF(gfx_renderer, src->tx, &src->rect, &fr);
|
|
|
|
SDL_Rect dstr = {
|
|
.x = w * (1 - scale) / 2,
|
|
.y = h * (1 - scale) / 2,
|
|
.w = w * scale,
|
|
.h = h * scale,
|
|
};
|
|
SDL_SetRenderTarget(gfx_renderer, dst);
|
|
|
|
#if SDL_VERSION_ATLEAST(2, 0, 12)
|
|
SDL_SetTextureScaleMode(tmp, SDL_ScaleModeNearest);
|
|
#else
|
|
// Should be okay because nearest is the default scaling mode.
|
|
#endif
|
|
SDL_RenderCopy(gfx_renderer, tmp, NULL, &dstr);
|
|
SDL_SetRenderTarget(gfx_renderer, NULL);
|
|
}
|
|
|
|
static void mosaic_step(struct effect *eff, float progress) {
|
|
struct mosaic_effect *this = (struct mosaic_effect *)eff;
|
|
|
|
if (eff->type == EFFECT_MOSAIC) {
|
|
const int max_scale = 80;
|
|
int scale = (1.f - progress) * (max_scale - 1) + 1;
|
|
mosaic(eff->tx_new, this->tmp_new, this->tx_mosaic, eff->dst_rect.w, eff->dst_rect.h, scale);
|
|
SDL_RenderCopy(gfx_renderer, this->tx_mosaic, NULL, &eff->dst_rect);
|
|
} else if (eff->type == EFFECT_CROSSFADE_MOSAIC) {
|
|
const int max_scale = 96;
|
|
float scale = (progress < 0.5f ? progress : 1.f - progress) * 2 * (max_scale - 1) + 1;
|
|
mosaic(eff->tx_old, this->tmp_old, this->tx_mosaic, eff->dst_rect.w, eff->dst_rect.h, scale);
|
|
SDL_RenderCopy(gfx_renderer, this->tx_mosaic, NULL, &eff->dst_rect);
|
|
mosaic(eff->tx_new, this->tmp_new, this->tx_mosaic, eff->dst_rect.w, eff->dst_rect.h, scale);
|
|
SDL_SetTextureBlendMode(this->tx_mosaic, SDL_BLENDMODE_BLEND);
|
|
SDL_SetTextureAlphaMod(this->tx_mosaic, progress * 255);
|
|
SDL_RenderCopy(gfx_renderer, this->tx_mosaic, NULL, &eff->dst_rect);
|
|
SDL_SetTextureBlendMode(this->tx_mosaic, SDL_BLENDMODE_NONE);
|
|
} else {
|
|
assert(!"Cannot happen");
|
|
}
|
|
|
|
SDL_RenderPresent(gfx_renderer);
|
|
}
|
|
|
|
static void mosaic_free(struct effect *eff) {
|
|
struct mosaic_effect *this = (struct mosaic_effect *)eff;
|
|
SDL_DestroyTexture(this->tmp_old);
|
|
SDL_DestroyTexture(this->tmp_new);
|
|
SDL_DestroyTexture(this->tx_mosaic);
|
|
eff_finish(&this->eff, true);
|
|
free(this);
|
|
}
|
|
|
|
// EFFECT_{FADE,WHITE}{OUT,IN}
|
|
|
|
static void brightness_step(struct effect *eff, float progress);
|
|
static void brightness_free(struct effect *eff);
|
|
|
|
static struct effect *brightness_new(SDL_Rect *rect, EffectTexture *old, EffectTexture *new, enum effect_type type) {
|
|
struct effect *eff = calloc(1, sizeof(struct effect));
|
|
if (!eff)
|
|
NOMEMERR();
|
|
eff_init(eff, rect, old, new, type);
|
|
eff->step = brightness_step;
|
|
eff->finish = brightness_free;
|
|
return eff;
|
|
}
|
|
|
|
static void brightness_step(struct effect *eff, float progress) {
|
|
EffectTexture *texture;
|
|
int color;
|
|
int alpha;
|
|
switch (eff->type) {
|
|
case EFFECT_FADEOUT:
|
|
texture = eff->tx_old;
|
|
color = 0;
|
|
alpha = progress * 255;
|
|
break;
|
|
case EFFECT_FADEOUT_FROM_NEW:
|
|
texture = eff->tx_new;
|
|
color = 0;
|
|
alpha = progress * 255;
|
|
break;
|
|
case EFFECT_FADEIN:
|
|
texture = eff->tx_new;
|
|
color = 0;
|
|
alpha = (1.f - progress) * 255;
|
|
break;
|
|
case EFFECT_WHITEOUT:
|
|
texture = eff->tx_old;
|
|
color = 255;
|
|
alpha = progress * 255;
|
|
break;
|
|
case EFFECT_WHITEOUT_FROM_NEW:
|
|
texture = eff->tx_new;
|
|
color = 255;
|
|
alpha = progress * 255;
|
|
break;
|
|
case EFFECT_WHITEIN:
|
|
texture = eff->tx_new;
|
|
color = 255;
|
|
alpha = (1.f - progress) * 255;
|
|
break;
|
|
default:
|
|
assert(!"Cannot happen");
|
|
}
|
|
render_effect_texture(texture, NULL, &eff->dst_rect);
|
|
SDL_SetRenderDrawBlendMode(gfx_renderer, SDL_BLENDMODE_BLEND);
|
|
SDL_SetRenderDrawColor(gfx_renderer, color, color, color, alpha);
|
|
SDL_RenderFillRect(gfx_renderer, &eff->dst_rect);
|
|
SDL_SetRenderDrawBlendMode(gfx_renderer, SDL_BLENDMODE_NONE);
|
|
SDL_SetRenderDrawColor(gfx_renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
|
|
SDL_RenderPresent(gfx_renderer);
|
|
}
|
|
|
|
static void brightness_free(struct effect *eff) {
|
|
eff_finish(eff, false);
|
|
free(eff);
|
|
}
|
|
|
|
// EFFECT_DITHERING_{FADE,WHITE}{OUT,IN}
|
|
|
|
static void dithering_fade_step(struct effect *eff, float progress);
|
|
static void dithering_fade_free(struct effect *eff);
|
|
|
|
static EffectTexture *create_dither_pattern_texture(int w, int h, int val) {
|
|
SDL_PixelFormat *fmt = SDL_AllocFormat(SDL_PIXELFORMAT_ARGB8888);
|
|
Uint32 col = SDL_MapRGBA(fmt, val, val, val, SDL_ALPHA_OPAQUE);
|
|
SDL_FreeFormat(fmt);
|
|
|
|
Uint32 *pixels = calloc(w * h, sizeof(Uint32));
|
|
if (!pixels)
|
|
NOMEMERR();
|
|
for (int y = 0; y < h; y += 4) {
|
|
Uint32 *row = pixels + y * w;
|
|
for (int x = 0; x < w; x += 4)
|
|
row[x] = col;
|
|
}
|
|
|
|
EffectTexture *t = calloc(1, sizeof(EffectTexture));
|
|
t->tx = SDL_CreateTexture(gfx_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STATIC, w, h);
|
|
SDL_UpdateTexture(t->tx, NULL, pixels, w * 4);
|
|
free(pixels);
|
|
SDL_SetTextureBlendMode(t->tx, SDL_BLENDMODE_BLEND);
|
|
t->rect = (SDL_Rect){ 0, 0, w, h };
|
|
return t;
|
|
}
|
|
|
|
static struct effect *dithering_fade_new(SDL_Rect *rect, EffectTexture *old, EffectTexture *new, enum effect_type type) {
|
|
struct effect *eff = calloc(1, sizeof(struct effect));
|
|
if (!eff)
|
|
NOMEMERR();
|
|
switch (type) {
|
|
case EFFECT_DITHERING_FADEOUT:
|
|
destroy_effect_texture(new);
|
|
eff_init(eff, rect, old, NULL, type);
|
|
eff->tx_new = create_dither_pattern_texture(rect->w, rect->h, 0);
|
|
break;
|
|
case EFFECT_DITHERING_FADEIN:
|
|
destroy_effect_texture(old);
|
|
eff_init(eff, rect, new, NULL, type);
|
|
eff->tx_new = create_dither_pattern_texture(rect->w, rect->h, 0);
|
|
break;
|
|
case EFFECT_DITHERING_WHITEOUT:
|
|
destroy_effect_texture(new);
|
|
eff_init(eff, rect, old, NULL, type);
|
|
eff->tx_new = create_dither_pattern_texture(rect->w, rect->h, 255);
|
|
break;
|
|
case EFFECT_DITHERING_WHITEIN:
|
|
destroy_effect_texture(old);
|
|
eff_init(eff, rect, new, NULL, type);
|
|
eff->tx_new = create_dither_pattern_texture(rect->w, rect->h, 255);
|
|
break;
|
|
default:
|
|
assert(!"Cannot happen");
|
|
}
|
|
eff->step = dithering_fade_step;
|
|
eff->finish = dithering_fade_free;
|
|
return eff;
|
|
}
|
|
|
|
static void dithering_fade_step(struct effect *eff, float progress) {
|
|
static const int dither_x[16] = {0,2,2,0,1,3,3,1,1,3,3,1,0,2,2,0};
|
|
static const int dither_y[16] = {0,2,0,2,1,3,1,3,0,2,0,2,1,3,1,3};
|
|
|
|
render_effect_texture(eff->tx_old, NULL, &eff->dst_rect);
|
|
|
|
if (eff->type == EFFECT_DITHERING_FADEIN || eff->type == EFFECT_DITHERING_WHITEIN)
|
|
progress = 1.f - progress;
|
|
int level = roundf(progress * 16);
|
|
|
|
assert(eff->is_fullscreen);
|
|
SDL_Rect dr = eff->dst_rect;
|
|
for (int i = 0; i < level; i++) {
|
|
dr.x = eff->dst_rect.x + dither_x[i];
|
|
dr.y = eff->dst_rect.y + dither_y[i];
|
|
render_effect_texture(eff->tx_new, NULL, &dr);
|
|
}
|
|
|
|
SDL_RenderPresent(gfx_renderer);
|
|
}
|
|
|
|
static void dithering_fade_free(struct effect *eff) {
|
|
if (eff->type == EFFECT_DITHERING_FADEOUT) {
|
|
SDL_SetRenderDrawColor(gfx_renderer, 0, 0, 0, 255);
|
|
SDL_RenderFillRect(gfx_renderer, &eff->dst_rect);
|
|
} else if (eff->type == EFFECT_DITHERING_WHITEOUT) {
|
|
SDL_SetRenderDrawColor(gfx_renderer, 255, 255, 255, 255);
|
|
SDL_RenderFillRect(gfx_renderer, &eff->dst_rect);
|
|
}
|
|
eff_finish(eff, false);
|
|
free(eff);
|
|
}
|
|
|
|
// EFFECT_PAN_IN_*
|
|
|
|
static void pan_in_step(struct effect *eff, float progress);
|
|
static void pan_in_free(struct effect *eff);
|
|
|
|
static struct effect *pan_in_new(SDL_Rect *rect, EffectTexture *old, EffectTexture *new, enum effect_type type) {
|
|
struct effect *eff = calloc(1, sizeof(struct effect));
|
|
if (!eff)
|
|
NOMEMERR();
|
|
eff_init(eff, rect, old, new, type);
|
|
eff->step = pan_in_step;
|
|
eff->finish = pan_in_free;
|
|
return eff;
|
|
}
|
|
|
|
static void pan_in_step(struct effect *eff, float progress) {
|
|
render_effect_texture(eff->tx_old, NULL, &eff->dst_rect);
|
|
|
|
int h = eff->dst_rect.h * progress;
|
|
SDL_Rect sr = { 0, 0, eff->dst_rect.w, h };
|
|
SDL_Rect dr = eff->dst_rect;
|
|
dr.h = h;
|
|
if (eff->type == EFFECT_PAN_IN_DOWN) {
|
|
sr.y = eff->dst_rect.h - h;
|
|
} else {
|
|
dr.y += eff->dst_rect.h - h;
|
|
}
|
|
render_effect_texture(eff->tx_new, &sr, &dr);
|
|
|
|
SDL_RenderPresent(gfx_renderer);
|
|
}
|
|
|
|
static void pan_in_free(struct effect *eff) {
|
|
eff_finish(eff, true);
|
|
free(eff);
|
|
}
|
|
|
|
// EFFECT_SKIP_LINE_*
|
|
|
|
static void skip_line_step(struct effect *eff, float progress);
|
|
static void skip_line_free(struct effect *eff);
|
|
|
|
static struct effect *skip_line_new(SDL_Rect *rect, EffectTexture *old, EffectTexture *new, enum effect_type type) {
|
|
struct effect *eff = calloc(1, sizeof(struct effect));
|
|
if (!eff)
|
|
NOMEMERR();
|
|
eff_init(eff, rect, old, new, type);
|
|
eff->step = skip_line_step;
|
|
eff->finish = skip_line_free;
|
|
return eff;
|
|
}
|
|
|
|
static void skip_line_step(struct effect *eff, float progress) {
|
|
render_effect_texture(eff->tx_old, NULL, &eff->dst_rect);
|
|
|
|
if (eff->type == EFFECT_SKIP_LINE_UP_DOWN) {
|
|
int h = eff->dst_rect.h * progress;
|
|
SDL_Rect sr = { 0, 0, eff->dst_rect.w, 1 };
|
|
SDL_Rect dr = eff->dst_rect;
|
|
dr.h = 1;
|
|
|
|
int bottom = (eff->dst_rect.h - 1) | 1;
|
|
for (int y = 0; y < h; y += 2) {
|
|
sr.y = y;
|
|
dr.y = eff->dst_rect.y + y;
|
|
render_effect_texture(eff->tx_new, &sr, &dr);
|
|
|
|
if (y == 0 && eff->dst_rect.h & 1)
|
|
continue;
|
|
sr.y = bottom - y;
|
|
dr.y = eff->dst_rect.y + bottom - y;
|
|
render_effect_texture(eff->tx_new, &sr, &dr);
|
|
}
|
|
} else {
|
|
int w = eff->dst_rect.w * progress;
|
|
SDL_Rect sr = { 0, 0, 1, eff->dst_rect.h };
|
|
SDL_Rect dr = eff->dst_rect;
|
|
dr.w = 1;
|
|
|
|
int right = (eff->dst_rect.w - 1) | 1;
|
|
for (int x = 0; x < w; x += 2) {
|
|
sr.x = x;
|
|
dr.x = eff->dst_rect.x + x;
|
|
render_effect_texture(eff->tx_new, &sr, &dr);
|
|
|
|
if (x == 0 && eff->dst_rect.w & 1)
|
|
continue;
|
|
sr.x = right - x;
|
|
dr.x = eff->dst_rect.x + right - x;
|
|
render_effect_texture(eff->tx_new, &sr, &dr);
|
|
}
|
|
}
|
|
|
|
SDL_RenderPresent(gfx_renderer);
|
|
}
|
|
|
|
static void skip_line_free(struct effect *eff) {
|
|
eff_finish(eff, true);
|
|
free(eff);
|
|
}
|
|
|
|
// EFFECT_WIPE_*
|
|
|
|
static void wipe_step(struct effect *eff, float progress);
|
|
static void wipe_free(struct effect *eff);
|
|
|
|
static struct effect *wipe_new(SDL_Rect *rect, EffectTexture *old, EffectTexture *new, enum effect_type type) {
|
|
struct effect *eff = calloc(1, sizeof(struct effect));
|
|
if (!eff)
|
|
NOMEMERR();
|
|
eff_init(eff, rect, old, new, type);
|
|
eff->step = wipe_step;
|
|
eff->finish = wipe_free;
|
|
return eff;
|
|
}
|
|
|
|
static void wipe_step(struct effect *eff, float progress) {
|
|
EffectTexture *bg = eff->tx_old;
|
|
EffectTexture *fg = eff->tx_new;
|
|
if (eff->type == EFFECT_WIPE_RL ||
|
|
eff->type == EFFECT_WIPE_UP ||
|
|
eff->type == EFFECT_WIPE_IN ||
|
|
eff->type == EFFECT_WIPE_IN_V ||
|
|
eff->type == EFFECT_WIPE_IN_H) {
|
|
bg = eff->tx_new;
|
|
fg = eff->tx_old;
|
|
progress = 1.f - progress;
|
|
}
|
|
|
|
render_effect_texture(bg, NULL, &eff->dst_rect);
|
|
|
|
SDL_Rect sr = { 0, 0, eff->dst_rect.w, eff->dst_rect.h };
|
|
SDL_Rect dr = eff->dst_rect;
|
|
switch (eff->type) {
|
|
case EFFECT_WIPE_LR:
|
|
case EFFECT_WIPE_RL:
|
|
sr.w *= progress;
|
|
dr.w = sr.w;
|
|
break;
|
|
case EFFECT_WIPE_DOWN:
|
|
case EFFECT_WIPE_UP:
|
|
sr.h *= progress;
|
|
dr.h = sr.h;
|
|
break;
|
|
case EFFECT_WIPE_OUT:
|
|
case EFFECT_WIPE_IN:
|
|
case EFFECT_WIPE_OUT_V:
|
|
case EFFECT_WIPE_IN_V:
|
|
sr.w *= progress;
|
|
sr.x = (dr.w - sr.w) / 2;
|
|
dr.w = sr.w;
|
|
dr.x += sr.x;
|
|
if (eff->type == EFFECT_WIPE_OUT_V || eff->type == EFFECT_WIPE_IN_V)
|
|
break;
|
|
// fallthrough
|
|
case EFFECT_WIPE_OUT_H:
|
|
case EFFECT_WIPE_IN_H:
|
|
sr.h *= progress;
|
|
sr.y = (dr.h - sr.h) / 2;
|
|
dr.h = sr.h;
|
|
dr.y += sr.y;
|
|
break;
|
|
default:
|
|
assert(!"Cannot happen");
|
|
}
|
|
render_effect_texture(fg, &sr, &dr);
|
|
|
|
SDL_RenderPresent(gfx_renderer);
|
|
}
|
|
|
|
static void wipe_free(struct effect *eff) {
|
|
eff_finish(eff, true);
|
|
free(eff);
|
|
}
|
|
|
|
// EFFECT_CIRCLE_WIPE_*
|
|
|
|
static void circle_wipe_step(struct effect *eff, float progress);
|
|
static void circle_wipe_free(struct effect *eff);
|
|
|
|
static struct effect *circle_wipe_new(SDL_Rect *rect, EffectTexture *old, EffectTexture *new, enum effect_type type) {
|
|
struct effect *eff = calloc(1, sizeof(struct effect));
|
|
if (!eff)
|
|
NOMEMERR();
|
|
eff_init(eff, rect, old, new, type);
|
|
eff->step = circle_wipe_step;
|
|
eff->finish = circle_wipe_free;
|
|
return eff;
|
|
}
|
|
|
|
static void circle_wipe_step(struct effect *eff, float progress) {
|
|
EffectTexture *bg = eff->tx_old;
|
|
EffectTexture *fg = eff->tx_new;
|
|
if (eff->type == EFFECT_CIRCLE_WIPE_IN) {
|
|
bg = eff->tx_new;
|
|
fg = eff->tx_old;
|
|
progress = 1 - progress;
|
|
}
|
|
render_effect_texture(bg, NULL, &eff->dst_rect);
|
|
|
|
int hw = eff->dst_rect.w / 2;
|
|
int hh = eff->dst_rect.h / 2;
|
|
int r = sqrtf(hw*hw + hh*hh) * progress;
|
|
|
|
for (int y = -r; y < r; y++) {
|
|
if (y < -hh || y >= hh)
|
|
continue;
|
|
for (int x = max(-r, -hw); x < 0; x++) {
|
|
if (y*y + x*x <= r*r) {
|
|
SDL_Rect sr = {hw + x, hh + y, -2*x, 1};
|
|
SDL_Rect dr = {eff->dst_rect.x + hw + x, eff->dst_rect.y + hh + y, -2*x, 1};
|
|
render_effect_texture(fg, &sr, &dr);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
SDL_RenderPresent(gfx_renderer);
|
|
}
|
|
|
|
static void circle_wipe_free(struct effect *eff) {
|
|
eff_finish(eff, true);
|
|
free(eff);
|
|
}
|
|
|
|
// EFFECT_BLIND_*
|
|
|
|
static void blind_step(struct effect *eff, float progress);
|
|
static void blind_free(struct effect *eff);
|
|
|
|
static struct effect *blind_new(SDL_Rect *rect, EffectTexture *old, EffectTexture *new, enum effect_type type) {
|
|
struct effect *eff = calloc(1, sizeof(struct effect));
|
|
if (!eff)
|
|
NOMEMERR();
|
|
eff_init(eff, rect, old, new, type);
|
|
eff->step = blind_step;
|
|
eff->finish = blind_free;
|
|
return eff;
|
|
}
|
|
|
|
static void blind_vstep(float progress, SDL_Rect *rect, EffectTexture *tx_new, int offset_y, bool flip) {
|
|
const int N = 16;
|
|
|
|
int nr_bands = rect->h / N + N - 1;
|
|
int step = nr_bands * progress;
|
|
|
|
int top = (step - (N - 1)) * N;
|
|
if (top > 0) {
|
|
if (top > rect->w)
|
|
top = rect->w;
|
|
SDL_Rect sr = {0, 0, rect->w, top};
|
|
if (flip)
|
|
flip_rect_v(&sr, rect->h);
|
|
SDL_Rect dr = sr;
|
|
move_rect(&dr, rect->x, rect->y);
|
|
sr.y += offset_y;
|
|
render_effect_texture(tx_new, &sr, &dr);
|
|
}
|
|
for (int i = 1; i < N; i++) {
|
|
int y = (step - i) * N;
|
|
if (y < 0 || y >= rect->h)
|
|
continue;
|
|
SDL_Rect sr = {0, y, rect->w, i};
|
|
if (flip)
|
|
flip_rect_v(&sr, rect->h);
|
|
SDL_Rect dr = sr;
|
|
move_rect(&dr, rect->x, rect->y);
|
|
sr.y += offset_y;
|
|
render_effect_texture(tx_new, &sr, &dr);
|
|
}
|
|
}
|
|
|
|
static void blind_step(struct effect *eff, float progress) {
|
|
const int N = 16;
|
|
|
|
render_effect_texture(eff->tx_old, NULL, &eff->dst_rect);
|
|
|
|
if (eff->type == EFFECT_BLIND_DOWN || eff->type == EFFECT_BLIND_UP || eff->type == EFFECT_BLIND_DOWN_LR) {
|
|
blind_vstep(progress, &eff->dst_rect, eff->tx_new, 0, eff->type == EFFECT_BLIND_UP);
|
|
}
|
|
if (eff->type == EFFECT_BLIND_UP_DOWN) {
|
|
SDL_Rect top_rect = eff->dst_rect;
|
|
top_rect.h = top_rect.h / (N * 2) * N;
|
|
blind_vstep(progress, &top_rect, eff->tx_new, 0, false);
|
|
|
|
SDL_Rect bottom_rect = eff->dst_rect;
|
|
bottom_rect.y += top_rect.h;
|
|
bottom_rect.h -= top_rect.h;
|
|
blind_vstep(progress, &bottom_rect, eff->tx_new, top_rect.h, true);
|
|
}
|
|
|
|
if (eff->type == EFFECT_BLIND_LR || eff->type == EFFECT_BLIND_RL || eff->type == EFFECT_BLIND_DOWN_LR) {
|
|
int nr_bands = eff->dst_rect.w / N + N - 1;
|
|
int step = nr_bands * progress;
|
|
|
|
int lhs = (step - (N - 1)) * N;
|
|
if (lhs > 0) {
|
|
if (lhs > eff->dst_rect.w)
|
|
lhs = eff->dst_rect.w;
|
|
SDL_Rect sr = {0, 0, lhs, eff->dst_rect.h};
|
|
if (eff->type == EFFECT_BLIND_RL)
|
|
flip_rect_h(&sr, eff->dst_rect.w);
|
|
SDL_Rect dr = sr;
|
|
move_rect(&dr, eff->dst_rect.x, eff->dst_rect.y);
|
|
render_effect_texture(eff->tx_new, &sr, &dr);
|
|
}
|
|
for (int i = 1; i < N; i++) {
|
|
int x = (step - i) * N;
|
|
if (x < 0 || x >= eff->dst_rect.w)
|
|
continue;
|
|
SDL_Rect sr = {x, 0, i, eff->dst_rect.h};
|
|
if (eff->type == EFFECT_BLIND_RL)
|
|
flip_rect_h(&sr, eff->dst_rect.w);
|
|
SDL_Rect dr = sr;
|
|
move_rect(&dr, eff->dst_rect.x, eff->dst_rect.y);
|
|
render_effect_texture(eff->tx_new, &sr, &dr);
|
|
}
|
|
}
|
|
|
|
SDL_RenderPresent(gfx_renderer);
|
|
}
|
|
|
|
static void blind_free(struct effect *eff) {
|
|
eff_finish(eff, false);
|
|
free(eff);
|
|
}
|
|
|
|
// EFFECT_BLEND_*
|
|
|
|
static void blend_animation_step(struct effect *eff, float progress);
|
|
static void blend_animation_free(struct effect *eff);
|
|
|
|
static struct effect *blend_animation_new(SDL_Rect *rect, EffectTexture *old, EffectTexture *new, enum effect_type type) {
|
|
struct effect *eff = calloc(1, sizeof(struct effect));
|
|
if (!eff)
|
|
NOMEMERR();
|
|
eff_init(eff, rect, old, new, type);
|
|
eff->step = blend_animation_step;
|
|
eff->finish = blend_animation_free;
|
|
return eff;
|
|
}
|
|
|
|
static void blend_animation_step(struct effect *eff, float progress) {
|
|
const bool lr = eff->type == EFFECT_BLEND_LR_RL;
|
|
|
|
EffectTexture *bg, *fg;
|
|
if (progress < 0.5f) {
|
|
bg = eff->tx_old;
|
|
fg = eff->tx_new;
|
|
} else {
|
|
bg = eff->tx_new;
|
|
fg = eff->tx_old;
|
|
progress = 1.f - progress;
|
|
}
|
|
|
|
render_effect_texture(bg, NULL, &eff->dst_rect);
|
|
int k = (lr ? eff->dst_rect.w : eff->dst_rect.h) * progress;
|
|
SDL_SetTextureBlendMode(fg->tx, SDL_BLENDMODE_BLEND);
|
|
SDL_SetTextureAlphaMod(fg->tx, 127);
|
|
|
|
SDL_Rect sr = {0, 0, eff->dst_rect.w, eff->dst_rect.h};
|
|
SDL_Rect dr = eff->dst_rect;
|
|
if (lr)
|
|
sr.w = dr.w = k;
|
|
else
|
|
sr.h = dr.h = k;
|
|
render_effect_texture(fg, &sr, &dr);
|
|
|
|
if (lr) {
|
|
sr.x = eff->dst_rect.w - k;
|
|
dr.x += sr.x;
|
|
} else {
|
|
sr.y = eff->dst_rect.h - k;
|
|
dr.y += sr.y;
|
|
}
|
|
render_effect_texture(fg, &sr, &dr);
|
|
|
|
SDL_SetTextureAlphaMod(fg->tx, 255);
|
|
SDL_SetTextureBlendMode(fg->tx, SDL_BLENDMODE_NONE);
|
|
SDL_RenderPresent(gfx_renderer);
|
|
}
|
|
|
|
static void blend_animation_free(struct effect *eff) {
|
|
eff_finish(eff, false);
|
|
free(eff);
|
|
}
|
|
|
|
// EFFECT_ZOOM_BLEND_BLUR
|
|
|
|
#define ZOOM_BLEND_BLUR_STEPS 6
|
|
|
|
struct zoom_blend_blur_effect {
|
|
struct effect eff;
|
|
SDL_Texture *tx[ZOOM_BLEND_BLUR_STEPS];
|
|
int index;
|
|
};
|
|
|
|
static void zoom_blend_blur_step(struct effect *eff, float progress);
|
|
static void zoom_blend_blur_free(struct effect *eff);
|
|
|
|
static struct effect *zoom_blend_blur_new(SDL_Rect *rect, EffectTexture *old, EffectTexture *new) {
|
|
if (!SDL_RenderTargetSupported(gfx_renderer))
|
|
return fallback_effect_new(rect, old, new, EFFECT_ZOOM_BLEND_BLUR);
|
|
|
|
struct zoom_blend_blur_effect *eff = calloc(1, sizeof(struct zoom_blend_blur_effect));
|
|
if (!eff)
|
|
NOMEMERR();
|
|
eff_init(&eff->eff, rect, old, new, EFFECT_ZOOM_BLEND_BLUR);
|
|
eff->eff.step = zoom_blend_blur_step;
|
|
eff->eff.finish = zoom_blend_blur_free;
|
|
return &eff->eff;
|
|
}
|
|
|
|
static void zoom_blend_blur_step(struct effect *eff, float progress) {
|
|
struct zoom_blend_blur_effect *this = (struct zoom_blend_blur_effect *)eff;
|
|
|
|
if (!this->tx[this->index]) {
|
|
this->tx[this->index] = SDL_CreateTexture(gfx_renderer, SDL_PIXELFORMAT_RGB888, SDL_TEXTUREACCESS_TARGET, eff->dst_rect.w, eff->dst_rect.h);
|
|
}
|
|
|
|
float scale = (progress < 0.5f ? 1 - progress * 2 : (progress - 0.5f) * 2) * 0.9f + 0.1f; // 0.1 - 1.0
|
|
int w = eff->dst_rect.w * scale;
|
|
int h = eff->dst_rect.h * scale;
|
|
SDL_Rect r = {
|
|
.x = (eff->dst_rect.w - w) / 2,
|
|
.y = (eff->dst_rect.h - h) / 2,
|
|
.w = w,
|
|
.h = h
|
|
};
|
|
SDL_SetRenderTarget(gfx_renderer, this->tx[this->index]);
|
|
render_effect_texture(eff->tx_new, &r, NULL);
|
|
SDL_SetRenderTarget(gfx_renderer, NULL);
|
|
|
|
this->index = (this->index + 1) % ZOOM_BLEND_BLUR_STEPS;
|
|
|
|
SDL_RenderFillRect(gfx_renderer, &eff->dst_rect);
|
|
EffectTexture texture_rect = { NULL, { 0, 0, eff->dst_rect.w, eff->dst_rect.h } };
|
|
for (int i = 0; i < ZOOM_BLEND_BLUR_STEPS; i++) {
|
|
texture_rect.tx = this->tx[i];
|
|
EffectTexture *t = this->tx[i] ? &texture_rect : eff->tx_new;
|
|
SDL_SetTextureBlendMode(t->tx, SDL_BLENDMODE_ADD);
|
|
SDL_SetTextureAlphaMod(t->tx, 255 / ZOOM_BLEND_BLUR_STEPS);
|
|
render_effect_texture(t, NULL, &eff->dst_rect);
|
|
SDL_SetTextureBlendMode(t->tx, SDL_BLENDMODE_NONE);
|
|
SDL_SetTextureAlphaMod(t->tx, 255);
|
|
}
|
|
SDL_RenderPresent(gfx_renderer);
|
|
}
|
|
|
|
static void zoom_blend_blur_free(struct effect *eff) {
|
|
struct zoom_blend_blur_effect *this = (struct zoom_blend_blur_effect *)eff;
|
|
for (int i = 0; i < ZOOM_BLEND_BLUR_STEPS; i++) {
|
|
if (this->tx[i])
|
|
SDL_DestroyTexture(this->tx[i]);
|
|
}
|
|
eff_finish(&this->eff, true);
|
|
free(this);
|
|
}
|
|
|
|
// EFFECT_LINEAR_BLUR, EFFECT_LINEAR_BLUR_VERT
|
|
|
|
#define LINEAR_BLUR_STEPS 10
|
|
|
|
struct linear_blur_effect {
|
|
struct effect eff;
|
|
EffectTexture *blurred_old[LINEAR_BLUR_STEPS];
|
|
EffectTexture *blurred_new[LINEAR_BLUR_STEPS];
|
|
};
|
|
|
|
static void linear_blur_step(struct effect *eff, float progress);
|
|
static void linear_blur_free(struct effect *eff);
|
|
|
|
static struct effect *linear_blur_new(SDL_Rect *rect, EffectTexture *old, EffectTexture *new, enum effect_type type) {
|
|
if (!SDL_RenderTargetSupported(gfx_renderer))
|
|
return fallback_effect_new(rect, old, new, type);
|
|
|
|
struct linear_blur_effect *lbe = calloc(1, sizeof(struct linear_blur_effect));
|
|
if (!lbe)
|
|
NOMEMERR();
|
|
eff_init(&lbe->eff, rect, old, new, type);
|
|
lbe->eff.step = linear_blur_step;
|
|
lbe->eff.finish = linear_blur_free;
|
|
return &lbe->eff;
|
|
}
|
|
|
|
static EffectTexture *blur(struct linear_blur_effect *this, EffectTexture *src, int stride) {
|
|
bool vertical = this->eff.type == EFFECT_LINEAR_BLUR_VERT;
|
|
int w = this->eff.dst_rect.w;
|
|
int h = this->eff.dst_rect.h;
|
|
EffectTexture *dst = calloc(1, sizeof(EffectTexture));
|
|
dst->tx = SDL_CreateTexture(gfx_renderer, SDL_PIXELFORMAT_RGB888, SDL_TEXTUREACCESS_TARGET, w, h);
|
|
dst->rect = (SDL_Rect){0, 0, w, h};
|
|
|
|
SDL_SetRenderTarget(gfx_renderer, dst->tx);
|
|
SDL_RenderClear(gfx_renderer);
|
|
SDL_SetTextureBlendMode(src->tx, SDL_BLENDMODE_ADD);
|
|
SDL_SetTextureAlphaMod(src->tx, 127);
|
|
|
|
SDL_Rect dr = {0, 0, w, h};
|
|
*(vertical ? &dr. y : &dr.x) = -stride / 2;
|
|
render_effect_texture(src, NULL, &dr);
|
|
*(vertical ? &dr. y : &dr.x) = max(1, stride / 2);
|
|
render_effect_texture(src, NULL, &dr);
|
|
|
|
// Copy the edge pixels to emulate texture clamping.
|
|
SDL_Rect sr = {0, 0, vertical ? w : 1, vertical ? 1 : h};
|
|
*(vertical ? &dr.h : &dr.w) = max(1, stride / 2);
|
|
*(vertical ? &dr.y : &dr.x) = 0;
|
|
render_effect_texture(src, &sr, &dr);
|
|
if (stride > 1) {
|
|
if (vertical) {
|
|
sr.y = h - 1;
|
|
dr.y = h - stride / 2;
|
|
} else {
|
|
sr.x = w - 1;
|
|
dr.x = w - stride / 2;
|
|
}
|
|
render_effect_texture(src, &sr, &dr);
|
|
}
|
|
|
|
SDL_SetTextureAlphaMod(src->tx, 255);
|
|
SDL_SetTextureBlendMode(src->tx, SDL_BLENDMODE_NONE);
|
|
SDL_SetRenderTarget(gfx_renderer, NULL);
|
|
|
|
return dst;
|
|
}
|
|
|
|
static void linear_blur_step(struct effect *eff, float progress) {
|
|
struct linear_blur_effect *this = (struct linear_blur_effect *)eff;
|
|
|
|
int step = progress * LINEAR_BLUR_STEPS * 2;
|
|
if (step == LINEAR_BLUR_STEPS * 2) {
|
|
render_effect_texture(eff->tx_new, NULL, NULL);
|
|
SDL_RenderPresent(gfx_renderer);
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i <= step && i < LINEAR_BLUR_STEPS; i++) {
|
|
if (this->blurred_old[i])
|
|
continue;
|
|
this->blurred_old[i] = blur(this, i ? this->blurred_old[i - 1] : eff->tx_old, 1 << i);
|
|
this->blurred_new[i] = blur(this, i ? this->blurred_new[i - 1] : eff->tx_new, 1 << i);
|
|
}
|
|
|
|
int i = step < LINEAR_BLUR_STEPS ? step : LINEAR_BLUR_STEPS * 2 - 1 - step;
|
|
EffectTexture *old = this->blurred_old[i];
|
|
EffectTexture *new = this->blurred_new[i];
|
|
render_effect_texture(old, NULL, &eff->dst_rect);
|
|
SDL_SetTextureBlendMode(new->tx, SDL_BLENDMODE_BLEND);
|
|
SDL_SetTextureAlphaMod(new->tx, 255 * progress);
|
|
render_effect_texture(new, NULL, &eff->dst_rect);
|
|
SDL_RenderPresent(gfx_renderer);
|
|
}
|
|
|
|
static void linear_blur_free(struct effect *eff) {
|
|
struct linear_blur_effect *this = (struct linear_blur_effect *)eff;
|
|
for (int i = 0; i < LINEAR_BLUR_STEPS; i++) {
|
|
if (this->blurred_old[i])
|
|
destroy_effect_texture(this->blurred_old[i]);
|
|
if (this->blurred_new[i])
|
|
destroy_effect_texture(this->blurred_new[i]);
|
|
}
|
|
eff_finish(&this->eff, true);
|
|
free(this);
|
|
}
|
|
|
|
// EFFECT_PENTAGRAM_*, EFFECT_HEXAGRAM_*, EFFECT_WINDMILL*
|
|
|
|
struct polygon_mask_effect {
|
|
struct effect f;
|
|
SDL_Texture *tx_tmp;
|
|
};
|
|
|
|
static void polygon_mask_step(struct effect *eff, float progress);
|
|
static void polygon_mask_free(struct effect *eff);
|
|
|
|
static struct effect *polygon_mask_new(SDL_Rect *rect, EffectTexture *old, EffectTexture *new, enum effect_type type) {
|
|
#if HAS_SDL_RenderGeometry
|
|
if (SDL_RenderTargetSupported(gfx_renderer)) {
|
|
struct polygon_mask_effect *pmf = calloc(1, sizeof(struct polygon_mask_effect));
|
|
if (!pmf)
|
|
NOMEMERR();
|
|
eff_init(&pmf->f, rect, old, new, type);
|
|
pmf->tx_tmp = SDL_CreateTexture(gfx_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, rect->w, rect->h);
|
|
pmf->f.step = polygon_mask_step;
|
|
pmf->f.finish = polygon_mask_free;
|
|
return &pmf->f;
|
|
}
|
|
#endif // HAS_SDL_RenderGeometry
|
|
|
|
return fallback_effect_new(rect, old, new, type);
|
|
}
|
|
|
|
#if HAS_SDL_RenderGeometry
|
|
|
|
static void draw_pentagram(int center_x, int center_y, float radius, float rotate) {
|
|
const SDL_FPoint p[10] = {
|
|
{ 1.5, 0 },
|
|
{ 0.4635, 0.3368},
|
|
{ 0.4635, 1.4266},
|
|
{-0.1771, 0.5449},
|
|
{-1.2135, 0.8817},
|
|
{-0.5729, 0 },
|
|
{-1.2135, -0.8817},
|
|
{-0.1771, -0.5449},
|
|
{ 0.4635, -1.4266},
|
|
{ 0.4635, -0.3368},
|
|
};
|
|
SDL_Vertex v[10];
|
|
float sin_r = sinf(rotate);
|
|
float cos_r = cosf(rotate);
|
|
for (int i = 0; i < 10; i++) {
|
|
v[i].position.x = radius * (p[i].x * cos_r - p[i].y * sin_r) + center_x;
|
|
v[i].position.y = radius * (p[i].x * sin_r + p[i].y * cos_r) + center_y;
|
|
v[i].color = (SDL_Color){0, 0, 0, 0};
|
|
}
|
|
const int indices[15] = {
|
|
0, 3, 7,
|
|
2, 5, 9,
|
|
4, 7, 1,
|
|
6, 9, 3,
|
|
8, 1, 5
|
|
};
|
|
SDL_RenderGeometry(gfx_renderer, NULL, v, 10, indices, 15);
|
|
}
|
|
|
|
static void draw_hexagram(int center_x, int center_y, float radius, float rotate) {
|
|
const SDL_FPoint p[6] = {
|
|
{ 0, -1.0},
|
|
{-0.866, 0.5},
|
|
{ 0.866, 0.5},
|
|
|
|
{-0.866, -0.5},
|
|
{ 0, 1.0},
|
|
{ 0.866, -0.5}
|
|
};
|
|
SDL_Vertex v[6];
|
|
float sin_r = sinf(rotate);
|
|
float cos_r = cosf(rotate);
|
|
for (int i = 0; i < 6; i++) {
|
|
v[i].position.x = radius * (p[i].x * cos_r - p[i].y * sin_r) + center_x;
|
|
v[i].position.y = radius * (p[i].x * sin_r + p[i].y * cos_r) + center_y;
|
|
v[i].color = (SDL_Color){0, 0, 0, 0};
|
|
}
|
|
SDL_RenderGeometry(gfx_renderer, NULL, v, 6, NULL, 0);
|
|
}
|
|
|
|
static void draw_windmill_90(int w, int h, float theta) {
|
|
float cx = w / 2.f;
|
|
float cy = h / 2.f;
|
|
float r = max(cx, cy) * sqrtf(2);
|
|
float r_sin = r * sinf(theta);
|
|
float r_cos = r * cosf(theta);
|
|
|
|
SDL_Vertex v[13] = {
|
|
// center
|
|
{.position = {cx, cy }},
|
|
// top-right
|
|
{.position = {cx, cy - r }},
|
|
{.position = {cx + r, cy - r }},
|
|
{.position = {cx + r_sin, cy - r_cos}},
|
|
// bottom-right
|
|
{.position = {cx + r, cy }},
|
|
{.position = {cx + r, cy + r }},
|
|
{.position = {cx + r_cos, cy + r_sin}},
|
|
// bottlm-left
|
|
{.position = {cx, cy + r }},
|
|
{.position = {cx - r, cy + r }},
|
|
{.position = {cx - r_sin, cy + r_cos}},
|
|
// top-left
|
|
{.position = {cx - r, cy }},
|
|
{.position = {cx - r, cy - r }},
|
|
{.position = {cx - r_cos, cy - r_sin}},
|
|
};
|
|
|
|
if (theta <= M_PIf / 4) {
|
|
const int indices[12] = {
|
|
0, 1, 3,
|
|
0, 4, 6,
|
|
0, 7, 9,
|
|
0, 10, 12
|
|
};
|
|
SDL_RenderGeometry(gfx_renderer, NULL, v, 13, indices, 12);
|
|
} else {
|
|
const int indices[24] = {
|
|
0, 1, 2,
|
|
0, 2, 3,
|
|
0, 4, 5,
|
|
0, 5, 6,
|
|
0, 7, 8,
|
|
0, 8, 9,
|
|
0, 10, 11,
|
|
0, 11, 12
|
|
};
|
|
SDL_RenderGeometry(gfx_renderer, NULL, v, 13, indices, 24);
|
|
}
|
|
}
|
|
|
|
static void draw_windmill_180(int w, int h, float theta) {
|
|
float cx = w / 2.f;
|
|
float cy = h / 2.f;
|
|
float r = max(cx, cy) * sqrtf(2);
|
|
float r_sin = r * sinf(theta);
|
|
float r_cos = r * cosf(theta);
|
|
|
|
SDL_Vertex v[11] = {
|
|
{.position = {cx, cy }}, // center
|
|
|
|
{.position = {cx - r, cy - r }}, // left-top
|
|
{.position = {cx, cy - r }}, // top
|
|
{.position = {cx + r, cy - r }}, // top-right
|
|
{.position = {cx + r, cy }}, // right
|
|
{.position = {cx - r_cos, cy - r_sin}}, // theta
|
|
|
|
{.position = {cx + r, cy + r }}, // right-bottom
|
|
{.position = {cx, cy + r }}, // bottom
|
|
{.position = {cx - r, cy + r }}, // left-bottom
|
|
{.position = {cx - r, cy }}, // left
|
|
{.position = {cx + r_cos, cy + r_sin}}, // theta+M_PIf
|
|
};
|
|
|
|
int indices[24];
|
|
int i = 0;
|
|
|
|
indices[i++] = 0; // center
|
|
indices[i++] = 9; // left
|
|
for (int j = 1; theta > M_PIf / 4 * j; j++) {
|
|
indices[i++] = j;
|
|
indices[i++] = 0; // center
|
|
indices[i++] = j;
|
|
}
|
|
indices[i++] = 5; // theta
|
|
|
|
indices[i++] = 0; // center
|
|
indices[i++] = 4; // right
|
|
for (int j = 1; theta > M_PIf / 4 * j; j++) {
|
|
indices[i++] = j + 5;
|
|
indices[i++] = 0; // center
|
|
indices[i++] = j + 5;
|
|
}
|
|
indices[i++] = 10; // theta+M_PIf
|
|
|
|
SDL_RenderGeometry(gfx_renderer, NULL, v, 11, indices, i);
|
|
}
|
|
|
|
static void draw_windmill_360(int w, int h, float theta) {
|
|
float cx = w / 2.f;
|
|
float cy = h / 2.f;
|
|
float r = max(cx, cy) * sqrtf(2);
|
|
float r_sin = r * sinf(theta);
|
|
float r_cos = r * cosf(theta);
|
|
|
|
SDL_Vertex v[10] = {
|
|
{.position = {cx, cy }}, // center
|
|
{.position = {cx - r, cy - r }}, // left-top
|
|
{.position = {cx, cy - r }}, // top
|
|
{.position = {cx + r, cy - r }}, // top-right
|
|
{.position = {cx + r, cy }}, // right
|
|
{.position = {cx + r, cy + r }}, // right-bottom
|
|
{.position = {cx, cy + r }}, // bottom
|
|
{.position = {cx - r, cy + r }}, // left-bottom
|
|
{.position = {cx - r, cy }}, // left
|
|
{.position = {cx - r_cos, cy - r_sin}}, // theta
|
|
};
|
|
|
|
int indices[24];
|
|
int i = 0;
|
|
|
|
indices[i++] = 0; // center
|
|
indices[i++] = 8; // left
|
|
for (int j = 1; theta > M_PIf / 4 * j; j++) {
|
|
indices[i++] = j;
|
|
indices[i++] = 0; // center
|
|
indices[i++] = j;
|
|
}
|
|
indices[i++] = 9; // theta
|
|
SDL_RenderGeometry(gfx_renderer, NULL, v, 10, indices, i);
|
|
}
|
|
|
|
static void polygon_mask_step(struct effect *eff, float progress) {
|
|
struct polygon_mask_effect *this = (struct polygon_mask_effect *)eff;
|
|
int w = eff->dst_rect.w;
|
|
int h = eff->dst_rect.h;
|
|
SDL_SetRenderTarget(gfx_renderer, this->tx_tmp);
|
|
render_effect_texture(eff->tx_old, NULL, NULL);
|
|
switch (eff->type) {
|
|
case EFFECT_PENTAGRAM_IN_OUT:
|
|
draw_pentagram(w / 2, h / 2, max(w, h) * progress, M_PIf * progress);
|
|
break;
|
|
case EFFECT_PENTAGRAM_OUT_IN:
|
|
draw_pentagram(w / 2, h / 2, max(w, h) * (1 - progress), M_PIf * progress);
|
|
break;
|
|
case EFFECT_HEXAGRAM_IN_OUT:
|
|
draw_hexagram(w / 2, h / 2, max(w, h) * progress, M_PIf * progress);
|
|
break;
|
|
case EFFECT_HEXAGRAM_OUT_IN:
|
|
draw_hexagram(w / 2, h / 2, max(w, h) * (1 - progress), M_PIf * progress);
|
|
break;
|
|
case EFFECT_WINDMILL:
|
|
draw_windmill_90(w, h, M_PIf / 2 * progress);
|
|
break;
|
|
case EFFECT_WINDMILL_180:
|
|
draw_windmill_180(w, h, M_PIf * progress);
|
|
break;
|
|
case EFFECT_WINDMILL_360:
|
|
draw_windmill_360(w, h, M_PIf * 2 * progress);
|
|
break;
|
|
default:
|
|
assert(!"Cannot happen");
|
|
}
|
|
SDL_SetRenderTarget(gfx_renderer, NULL);
|
|
render_effect_texture(eff->tx_new, NULL, &eff->dst_rect);
|
|
SDL_SetTextureBlendMode(this->tx_tmp, SDL_BLENDMODE_BLEND);
|
|
SDL_RenderCopy(gfx_renderer, this->tx_tmp, NULL, &eff->dst_rect);
|
|
SDL_RenderPresent(gfx_renderer);
|
|
}
|
|
|
|
static void polygon_mask_free(struct effect *eff) {
|
|
struct polygon_mask_effect *this = (struct polygon_mask_effect *)eff;
|
|
SDL_DestroyTexture(this->tx_tmp);
|
|
eff_finish(&this->f, true);
|
|
free(this);
|
|
}
|
|
|
|
#endif // HAS_SDL_RenderGeometry
|
|
|
|
// EFFECT_ROTATE_*
|
|
|
|
static void rotate_step(struct effect *eff, float progress);
|
|
static void rotate_free(struct effect *eff);
|
|
|
|
static struct effect *rotate_new(SDL_Rect *rect, EffectTexture *old, EffectTexture *new, enum effect_type type) {
|
|
struct effect *eff = calloc(1, sizeof(struct effect));
|
|
if (!eff)
|
|
NOMEMERR();
|
|
eff_init(eff, rect, old, new, type);
|
|
eff->step = rotate_step;
|
|
eff->finish = rotate_free;
|
|
return eff;
|
|
}
|
|
|
|
static void rotate_step(struct effect *eff, float progress) {
|
|
EffectTexture *bg_texture, *fg_texture;
|
|
float angle = progress * 360;
|
|
float scale = progress;
|
|
switch (eff->type) {
|
|
case EFFECT_ROTATE_OUT:
|
|
scale = 1.f - scale;
|
|
bg_texture = eff->tx_new;
|
|
fg_texture = eff->tx_old;
|
|
angle *= -1.f;
|
|
break;
|
|
case EFFECT_ROTATE_IN:
|
|
bg_texture = eff->tx_old;
|
|
fg_texture = eff->tx_new;
|
|
angle *= -1.f;
|
|
break;
|
|
case EFFECT_ROTATE_OUT_CW:
|
|
scale = 1.f - scale;
|
|
bg_texture = eff->tx_new;
|
|
fg_texture = eff->tx_old;
|
|
break;
|
|
case EFFECT_ROTATE_IN_CW:
|
|
bg_texture = eff->tx_old;
|
|
fg_texture = eff->tx_new;
|
|
break;
|
|
case EFFECT_ZOOM_IN:
|
|
bg_texture = eff->tx_old;
|
|
fg_texture = eff->tx_new;
|
|
angle = 0;
|
|
break;
|
|
default:
|
|
assert(!"Cannot happen");
|
|
}
|
|
SDL_Rect r = eff->dst_rect;
|
|
r.x += (1.f - scale) * r.w / 2;
|
|
r.y += (1.f - scale) * r.h / 2;
|
|
r.w *= scale;
|
|
r.h *= scale;
|
|
render_effect_texture(bg_texture, NULL, &eff->dst_rect);
|
|
SDL_RenderCopyEx(gfx_renderer, fg_texture->tx, &fg_texture->rect, &r, angle, NULL, SDL_FLIP_NONE);
|
|
SDL_RenderPresent(gfx_renderer);
|
|
}
|
|
|
|
static void rotate_free(struct effect *eff) {
|
|
eff_finish(eff, true);
|
|
free(eff);
|
|
}
|
|
|
|
// EFFECT_POLYGON_ROTATE_*
|
|
|
|
static void polygon_rotate_step(struct effect *eff, float progress);
|
|
static void polygon_rotate_free(struct effect *eff);
|
|
|
|
static struct effect *polygon_rotate_new(SDL_Rect *rect, EffectTexture *old, EffectTexture *new, enum effect_type type) {
|
|
struct effect *eff = calloc(1, sizeof(struct effect));
|
|
if (!eff)
|
|
NOMEMERR();
|
|
eff_init(eff, rect, old, new, type);
|
|
eff->step = polygon_rotate_step;
|
|
eff->finish = polygon_rotate_free;
|
|
return eff;
|
|
}
|
|
|
|
static void polygon_rotate_step(struct effect *eff, float progress) {
|
|
bool vertical = eff->type == EFFECT_POLYGON_ROTATE_Y || eff->type == EFFECT_POLYGON_ROTATE_Y_CW;
|
|
EffectTexture *texture = progress < 0.5f ? eff->tx_old : eff->tx_new;
|
|
float angle = progress < 0.5f ? progress * M_PIf : (progress - 1.f) * M_PIf;
|
|
if (eff->type == EFFECT_POLYGON_ROTATE_Y_CW || eff->type == EFFECT_POLYGON_ROTATE_X_CW)
|
|
angle *= -1;
|
|
|
|
const float distance = 2.f;
|
|
float cos_a = cosf(angle);
|
|
float sin_a = sinf(angle);
|
|
float lz = -0.5f * sin_a + distance;
|
|
float rz = 0.5f * sin_a + distance;
|
|
float lx = -0.5f * cos_a / lz;
|
|
float rx = 0.5f * cos_a / rz;
|
|
|
|
int w = vertical ? eff->dst_rect.w : eff->dst_rect.h;
|
|
int h = vertical ? eff->dst_rect.h : eff->dst_rect.w;
|
|
float dlx = w * (lx * distance + 0.5f);
|
|
float drx = w * (rx * distance + 0.5f);
|
|
|
|
// Perspective-correct interpolation.
|
|
for (int x = dlx; x <= drx; x++) {
|
|
if (x < 0 || x >= w)
|
|
continue;
|
|
float t = (x - dlx) / (drx - dlx);
|
|
float recip_z = (1 - t) * (1 / lz) + t * (1 / rz);
|
|
float tx = t * (w / rz) / recip_z;
|
|
SDL_Rect sr = {tx, 0, 1, h};
|
|
float dh = h * recip_z * distance;
|
|
SDL_Rect dr = {x, (h - dh) / 2, 1, dh};
|
|
if (!vertical) {
|
|
transpose_rect(&sr);
|
|
transpose_rect(&dr);
|
|
}
|
|
render_effect_texture(texture, &sr, &dr);
|
|
}
|
|
|
|
SDL_RenderPresent(gfx_renderer);
|
|
}
|
|
|
|
static void polygon_rotate_free(struct effect *eff) {
|
|
eff_finish(eff, true);
|
|
free(eff);
|
|
}
|
|
|
|
// EFFECT_ZIGZAG_CROSSFADE
|
|
|
|
struct zigzag_crossfade_effect {
|
|
struct effect eff;
|
|
SDL_Texture *tmp1, *tmp2;
|
|
};
|
|
|
|
static void zigzag_crossfade_step(struct effect *eff, float progress);
|
|
static void zigzag_crossfade_free(struct effect *eff);
|
|
|
|
static struct effect *zigzag_crossfade_new(SDL_Rect *rect, EffectTexture *old, EffectTexture *new) {
|
|
if (!SDL_RenderTargetSupported(gfx_renderer))
|
|
return fallback_effect_new(rect, old, new, EFFECT_ZIGZAG_CROSSFADE);
|
|
|
|
struct zigzag_crossfade_effect *eff = calloc(1, sizeof(struct zigzag_crossfade_effect));
|
|
if (!eff)
|
|
NOMEMERR();
|
|
eff_init(&eff->eff, rect, old, new, EFFECT_ZIGZAG_CROSSFADE);
|
|
eff->tmp1 = SDL_CreateTexture(gfx_renderer, SDL_PIXELFORMAT_RGB888, SDL_TEXTUREACCESS_TARGET, rect->w, rect->h);
|
|
eff->tmp2 = SDL_CreateTexture(gfx_renderer, SDL_PIXELFORMAT_RGB888, SDL_TEXTUREACCESS_TARGET, rect->w, rect->h);
|
|
eff->eff.step = zigzag_crossfade_step;
|
|
eff->eff.finish = zigzag_crossfade_free;
|
|
return &eff->eff;
|
|
}
|
|
|
|
static void wave_warp_h(EffectTexture *src, SDL_Texture *dst, int w, int h, float amplitude, float length, float phase) {
|
|
SDL_SetRenderTarget(gfx_renderer, dst);
|
|
SDL_RenderClear(gfx_renderer);
|
|
SDL_Rect sr = {0, 0, w, 1};
|
|
SDL_Rect dr = {0, 0, w, 1};
|
|
for (int y = 0; y < h; y++) {
|
|
sr.y = dr.y = y;
|
|
dr.x = sinf(phase + y * (2 * M_PIf / 360 * length)) * amplitude;
|
|
render_effect_texture(src, &sr, &dr);
|
|
}
|
|
SDL_SetRenderTarget(gfx_renderer, NULL);
|
|
}
|
|
|
|
static void wave_warp_v(SDL_Texture *src, SDL_Texture *dst, int w, int h, float amplitude, float length, float phase) {
|
|
SDL_SetRenderTarget(gfx_renderer, dst);
|
|
SDL_RenderClear(gfx_renderer);
|
|
SDL_Rect sr = {0, 0, 1, h};
|
|
SDL_Rect dr = {0, 0, 1, h};
|
|
for (int x = 0; x < w; x++) {
|
|
sr.x = dr.x = x;
|
|
dr.y = sinf(phase + x * (2 * M_PIf / 360 * length)) * amplitude;
|
|
SDL_RenderCopy(gfx_renderer, src, &sr, &dr);
|
|
}
|
|
SDL_SetRenderTarget(gfx_renderer, NULL);
|
|
}
|
|
|
|
static void zigzag_crossfade_step(struct effect *eff, float progress) {
|
|
struct zigzag_crossfade_effect *this = (struct zigzag_crossfade_effect *)eff;
|
|
|
|
float amp = (progress < 0.5f ? progress : 1.f - progress) * 80;
|
|
float len = (progress < 0.5f ? progress : 1.f - progress) * 20;
|
|
float phase = progress * 10;
|
|
|
|
wave_warp_h(eff->tx_old, this->tmp1, eff->dst_rect.w, eff->dst_rect.h, amp, len, phase);
|
|
wave_warp_v(this->tmp1, this->tmp2, eff->dst_rect.w, eff->dst_rect.h, amp, len, phase);
|
|
SDL_RenderCopy(gfx_renderer, this->tmp2, NULL, &eff->dst_rect);
|
|
|
|
wave_warp_h(eff->tx_new, this->tmp1, eff->dst_rect.w, eff->dst_rect.h, amp, len, phase);
|
|
wave_warp_v(this->tmp1, this->tmp2, eff->dst_rect.w, eff->dst_rect.h, amp, len, phase);
|
|
SDL_SetTextureBlendMode(this->tmp2, SDL_BLENDMODE_BLEND);
|
|
SDL_SetTextureAlphaMod(this->tmp2, progress * 255);
|
|
SDL_RenderCopy(gfx_renderer, this->tmp2, NULL, &eff->dst_rect);
|
|
SDL_SetTextureBlendMode(this->tmp2, SDL_BLENDMODE_NONE);
|
|
|
|
SDL_RenderPresent(gfx_renderer);
|
|
}
|
|
|
|
static void zigzag_crossfade_free(struct effect *eff) {
|
|
struct zigzag_crossfade_effect *this = (struct zigzag_crossfade_effect *)eff;
|
|
SDL_DestroyTexture(this->tmp1);
|
|
SDL_DestroyTexture(this->tmp2);
|
|
eff_finish(&this->eff, true);
|
|
free(this);
|
|
}
|
|
|
|
// EFFECT_MAGNIFY
|
|
|
|
struct magnify_effect {
|
|
struct effect eff;
|
|
SDL_Rect new_rect;
|
|
};
|
|
|
|
static void magnify_step(struct effect *eff, float progress);
|
|
static void magnify_free(struct effect *eff);
|
|
|
|
static struct effect *magnify_new(EffectTexture *tx, SDL_Rect *old_rect, SDL_Rect *new_rect) {
|
|
struct magnify_effect *eff = calloc(1, sizeof(struct magnify_effect));
|
|
if (!eff)
|
|
NOMEMERR();
|
|
eff_init(&eff->eff, old_rect, tx, NULL, EFFECT_MAGNIFY);
|
|
eff->new_rect = *new_rect;
|
|
eff->eff.step = magnify_step;
|
|
eff->eff.finish = magnify_free;
|
|
return &eff->eff;
|
|
}
|
|
|
|
static void magnify_step(struct effect *eff, float progress) {
|
|
struct magnify_effect *this = (struct magnify_effect *)eff;
|
|
SDL_Rect *or = &eff->dst_rect;
|
|
SDL_Rect *nr = &this->new_rect;
|
|
SDL_Rect r = {
|
|
.x = or->x + (nr->x - or->x) * progress,
|
|
.y = or->y + (nr->y - or->y) * progress,
|
|
.w = or->w + (nr->w - or->w) * progress,
|
|
.h = or->h + (nr->h - or->h) * progress,
|
|
};
|
|
render_effect_texture(eff->tx_old, &r, NULL);
|
|
SDL_RenderPresent(gfx_renderer);
|
|
}
|
|
|
|
static void magnify_free(struct effect *eff) {
|
|
eff_finish(eff, false);
|
|
free(eff);
|
|
}
|
|
|
|
// EFFECT_RASTER_BLEND
|
|
|
|
static void raster_blend_step(struct effect *eff, float progress);
|
|
static void raster_blend_free(struct effect *eff);
|
|
|
|
static struct effect *raster_blend_new(SDL_Rect *rect, int sx, int sy) {
|
|
SDL_Surface *sprite = gfx_dib_to_surface_with_alpha(sx, sy, rect->w, rect->h);
|
|
EffectTexture *texture = create_effect_texture_from_surface(sprite);
|
|
SDL_FreeSurface(sprite);
|
|
struct effect *eff = calloc(1, sizeof(struct effect));
|
|
if (!eff)
|
|
NOMEMERR();
|
|
eff_init(eff, rect, NULL, texture, EFFECT_RASTER_BLEND);
|
|
eff->step = raster_blend_step;
|
|
eff->finish = raster_blend_free;
|
|
return eff;
|
|
}
|
|
|
|
static void raster_blend_step(struct effect *eff, float progress) {
|
|
SDL_Rect s_rect = { 0, 0, eff->dst_rect.w, 1 };
|
|
SDL_Rect d_rect = eff->dst_rect;
|
|
d_rect.h = 1;
|
|
for (int y = 0; y < eff->dst_rect.h; y++) {
|
|
float t = 1.f - 4.f * progress + 3.f * y / eff->dst_rect.h;
|
|
d_rect.x = eff->dst_rect.x + (t < 0 ? 0 : sinf(2 * M_PIf * t) * 50);
|
|
render_effect_texture(eff->tx_new, &s_rect, &d_rect);
|
|
s_rect.y++;
|
|
d_rect.y++;
|
|
}
|
|
SDL_RenderPresent(gfx_renderer);
|
|
}
|
|
|
|
static void raster_blend_free(struct effect *eff) {
|
|
eff_finish(eff, false);
|
|
free(eff);
|
|
}
|
|
|
|
// SACTAMASK
|
|
|
|
struct sactamask_effect {
|
|
struct effect eff;
|
|
SDL_Surface *sf_new;
|
|
SDL_Surface *mask;
|
|
};
|
|
|
|
static void sactamask_step(struct effect *eff, float progress);
|
|
static void sactamask_free(struct effect *eff);
|
|
|
|
static struct effect *sactamask_new(EffectTexture *tx_old, SDL_Surface *sf_new, SDL_Surface *mask) {
|
|
struct sactamask_effect *eff = calloc(1, sizeof(struct sactamask_effect));
|
|
if (!eff)
|
|
NOMEMERR();
|
|
|
|
EffectTexture *tx_new = calloc(1, sizeof(EffectTexture));
|
|
tx_new->tx = SDL_CreateTexture(gfx_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, sf_new->w, sf_new->h);
|
|
tx_new->rect = (SDL_Rect){ 0, 0, sf_new->w, sf_new->h };
|
|
SDL_SetTextureBlendMode(tx_new->tx, SDL_BLENDMODE_BLEND);
|
|
|
|
SDL_Rect rect = {0, 0, mask->w, mask->h};
|
|
eff_init(&eff->eff, &rect, tx_old, tx_new, EFFECT_SACTAMASK);
|
|
eff->sf_new = sf_new;
|
|
eff->mask = mask;
|
|
eff->eff.step = sactamask_step;
|
|
eff->eff.finish = sactamask_free;
|
|
return &eff->eff;
|
|
}
|
|
|
|
static void sactamask_step(struct effect *eff, float progress) {
|
|
struct sactamask_effect *this = (struct sactamask_effect *)eff;
|
|
|
|
SDL_Surface *sf;
|
|
SDL_LockTextureToSurface(eff->tx_new->tx, NULL, &sf);
|
|
SDL_BlitSurface(this->sf_new, NULL, sf, NULL);
|
|
|
|
// Scale the alpha value in mask and write it to the alpha channel of sf.
|
|
int val = 255 * progress;
|
|
for (int y = 0; y < this->mask->h; y++) {
|
|
uint8_t *src = PIXEL_AT(this->mask, 0, y);
|
|
uint8_t *dst = ALPHA_AT(sf, 0, y);
|
|
for (int x = 0; x < this->mask->w; x++) {
|
|
int i = 255 - (*src - val) * 16;
|
|
*dst = min(255, max(0, i));
|
|
src++; dst += 4;
|
|
}
|
|
}
|
|
|
|
SDL_UnlockTexture(eff->tx_new->tx);
|
|
render_effect_texture(eff->tx_old, NULL, &eff->dst_rect);
|
|
render_effect_texture(eff->tx_new, NULL, &eff->dst_rect);
|
|
SDL_RenderPresent(gfx_renderer);
|
|
}
|
|
|
|
static void sactamask_free(struct effect *eff) {
|
|
eff_finish(eff, true);
|
|
free(eff);
|
|
}
|
|
|
|
// -----------------
|
|
|
|
struct effect *effect_init(SDL_Rect *rect, surface_t *old, int ox, int oy, surface_t *new, int nx, int ny, enum effect_type type) {
|
|
EffectTexture *tx_old = create_effect_texture(old, ox, oy, rect->w, rect->h);
|
|
EffectTexture *tx_new = create_effect_texture(new, nx, ny, rect->w, rect->h);
|
|
|
|
switch (type) {
|
|
case EFFECT_CROSSFADE:
|
|
return crossfade_new(rect, tx_old, tx_new);
|
|
case EFFECT_CROSSFADE_DOWN:
|
|
case EFFECT_CROSSFADE_UP:
|
|
case EFFECT_CROSSFADE_LR:
|
|
case EFFECT_CROSSFADE_RL:
|
|
case EFFECT_CROSSFADE_LR_RL:
|
|
case EFFECT_CROSSFADE_UP_DOWN:
|
|
return crossfade_animation_new(rect, tx_old, tx_new, type);
|
|
case EFFECT_MOSAIC:
|
|
case EFFECT_CROSSFADE_MOSAIC:
|
|
return mosaic_new(rect, tx_old, tx_new, type);
|
|
case EFFECT_FADEOUT:
|
|
case EFFECT_FADEOUT_FROM_NEW:
|
|
case EFFECT_FADEIN:
|
|
case EFFECT_WHITEOUT:
|
|
case EFFECT_WHITEOUT_FROM_NEW:
|
|
case EFFECT_WHITEIN:
|
|
return brightness_new(rect, tx_old, tx_new, type);
|
|
case EFFECT_DITHERING_FADEOUT:
|
|
case EFFECT_DITHERING_FADEIN:
|
|
case EFFECT_DITHERING_WHITEOUT:
|
|
case EFFECT_DITHERING_WHITEIN:
|
|
return dithering_fade_new(rect, tx_old, tx_new, type);
|
|
case EFFECT_PAN_IN_DOWN:
|
|
case EFFECT_PAN_IN_UP:
|
|
return pan_in_new(rect, tx_old, tx_new, type);
|
|
case EFFECT_SKIP_LINE_UP_DOWN:
|
|
case EFFECT_SKIP_LINE_LR_RL:
|
|
return skip_line_new(rect, tx_old, tx_new, type);
|
|
case EFFECT_WIPE_IN:
|
|
case EFFECT_WIPE_OUT:
|
|
case EFFECT_WIPE_LR:
|
|
case EFFECT_WIPE_RL:
|
|
case EFFECT_WIPE_DOWN:
|
|
case EFFECT_WIPE_UP:
|
|
case EFFECT_WIPE_OUT_V:
|
|
case EFFECT_WIPE_IN_V:
|
|
case EFFECT_WIPE_OUT_H:
|
|
case EFFECT_WIPE_IN_H:
|
|
return wipe_new(rect, tx_old, tx_new, type);
|
|
case EFFECT_CIRCLE_WIPE_OUT:
|
|
case EFFECT_CIRCLE_WIPE_IN:
|
|
return circle_wipe_new(rect, tx_old, tx_new, type);
|
|
case EFFECT_BLIND_DOWN:
|
|
case EFFECT_BLIND_UP:
|
|
case EFFECT_BLIND_LR:
|
|
case EFFECT_BLIND_RL:
|
|
case EFFECT_BLIND_UP_DOWN:
|
|
case EFFECT_BLIND_DOWN_LR:
|
|
return blind_new(rect, tx_old, tx_new, type);
|
|
case EFFECT_BLEND_UP_DOWN:
|
|
case EFFECT_BLEND_LR_RL:
|
|
return blend_animation_new(rect, tx_old, tx_new, type);
|
|
case EFFECT_ZOOM_BLEND_BLUR:
|
|
return zoom_blend_blur_new(rect, tx_old, tx_new);
|
|
case EFFECT_LINEAR_BLUR:
|
|
case EFFECT_LINEAR_BLUR_VERT:
|
|
return linear_blur_new(rect, tx_old, tx_new, type);
|
|
case EFFECT_PENTAGRAM_IN_OUT:
|
|
case EFFECT_PENTAGRAM_OUT_IN:
|
|
case EFFECT_HEXAGRAM_IN_OUT:
|
|
case EFFECT_HEXAGRAM_OUT_IN:
|
|
case EFFECT_WINDMILL:
|
|
case EFFECT_WINDMILL_180:
|
|
case EFFECT_WINDMILL_360:
|
|
return polygon_mask_new(rect, tx_old, tx_new, type);
|
|
case EFFECT_ZOOM_IN:
|
|
case EFFECT_ROTATE_OUT:
|
|
case EFFECT_ROTATE_IN:
|
|
case EFFECT_ROTATE_OUT_CW:
|
|
case EFFECT_ROTATE_IN_CW:
|
|
return rotate_new(rect, tx_old, tx_new, type);
|
|
case EFFECT_POLYGON_ROTATE_Y:
|
|
case EFFECT_POLYGON_ROTATE_Y_CW:
|
|
case EFFECT_POLYGON_ROTATE_X:
|
|
case EFFECT_POLYGON_ROTATE_X_CW:
|
|
return polygon_rotate_new(rect, tx_old, tx_new, type);
|
|
case EFFECT_ZIGZAG_CROSSFADE:
|
|
return zigzag_crossfade_new(rect, tx_old, tx_new);
|
|
default:
|
|
WARNING("Unknown effect %d", type);
|
|
return crossfade_new(rect, tx_old, tx_new);
|
|
}
|
|
}
|
|
|
|
struct effect *sprite_effect_init(SDL_Rect *rect, int dx, int dy, int sx, int sy, int col, enum effect_type type) {
|
|
EffectTexture *tx_old = create_effect_texture(gfx_dibinfo, dx, dy, rect->w, rect->h);
|
|
SDL_Surface *sprite = gfx_dib_to_surface_colorkey(sx, sy, rect->w, rect->h, col);
|
|
EffectTexture *tx_new = create_effect_texture_from_surface(sprite);
|
|
SDL_FreeSurface(sprite);
|
|
|
|
switch (type) {
|
|
case EFFECT_PAN_IN_DOWN:
|
|
case EFFECT_PAN_IN_UP:
|
|
return pan_in_new(rect, tx_old, tx_new, type);
|
|
case EFFECT_SKIP_LINE_UP_DOWN:
|
|
case EFFECT_SKIP_LINE_LR_RL:
|
|
return skip_line_new(rect, tx_old, tx_new, type);
|
|
case EFFECT_RASTER_BLEND:
|
|
return raster_blend_new(rect, sx, sy);
|
|
default:
|
|
SYSERROR("Unknown sprite effect %d", type);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
struct effect *effect_magnify_init(surface_t *surface, SDL_Rect *view_rect, SDL_Rect *target_rect) {
|
|
EffectTexture *tx = create_effect_texture(surface, view_rect->x, view_rect->y, view_rect->w, view_rect->h);
|
|
return magnify_new(tx, view_rect, target_rect);
|
|
}
|
|
|
|
struct effect *effect_sactamask_init(SDL_Surface *mask) {
|
|
EffectTexture *tx_old = create_effect_texture(NULL, 0, 0, main_surface->w, main_surface->h);
|
|
return sactamask_new(tx_old, main_surface, mask);
|
|
}
|
|
|
|
void effect_step(struct effect *eff, float progress) {
|
|
SDL_RenderClear(gfx_renderer);
|
|
if (!eff->is_fullscreen)
|
|
SDL_RenderCopy(gfx_renderer, gfx_texture, NULL, NULL);
|
|
eff->step(eff, progress);
|
|
}
|
|
|
|
void effect_finish(struct effect *eff) {
|
|
eff->finish(eff);
|
|
}
|