From b547b59bab6f8666ede091cd3befdc477edb7579 Mon Sep 17 00:00:00 2001 From: Nathaniel Walizer Date: Sun, 26 Jan 2025 11:46:54 -0800 Subject: [PATCH] Add CRT overlay effect --- Makefile | 2 +- src/input.h | 1 + src/menu.c | 3 ++ src/nese.c | 7 +++-- src/sdl_effect.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ src/sdl_effect.h | 17 ++++++++++++ src/sdl_render.c | 18 ++++++++++++ src/state.c | 6 +++- src/state.h | 1 + 9 files changed, 122 insertions(+), 4 deletions(-) create mode 100644 src/sdl_effect.c create mode 100644 src/sdl_effect.h diff --git a/Makefile b/Makefile index ed0108b..42bb8d0 100644 --- a/Makefile +++ b/Makefile @@ -54,7 +54,7 @@ SRC_SRCS_1 += overlay.c menu.c SRC_SRCS_1 += state.c ini.c SRC_SRCS_1 += compat.c SRC_SRCS_1 += sdl_render.c sdl_input.c sdl_audio.c sdl_timer.c -SRC_SRCS_1 += sdl_overlay.c +SRC_SRCS_1 += sdl_overlay.c sdl_effect.c PLAT_SRCS_1 = filemap.c diff --git a/src/input.h b/src/input.h index eeb8ad4..bd74471 100644 --- a/src/input.h +++ b/src/input.h @@ -19,6 +19,7 @@ typedef enum { input_Result_View, input_Result_Refresh, input_Result_Scale, + input_Result_Effect, } nes_Input_Result; #define nes_controller_num_buttons (8U) diff --git a/src/menu.c b/src/menu.c index d528d32..664a8d3 100644 --- a/src/menu.c +++ b/src/menu.c @@ -281,6 +281,8 @@ int run_game_menu(menu_state* state, nese_Components* comp, #endif (nese->flags & (1 << State_Bit_Integer_Scale)) ? "Force Aspect" : "Integer Scaling", + (nese->flags & (1 << State_Bit_CRT_Effect)) ? + "No Effect" : "CRT Effect", "Exit", }; static const int choices[] = { @@ -293,6 +295,7 @@ int run_game_menu(menu_state* state, nese_Components* comp, input_Result_View, #endif input_Result_Scale, + input_Result_Effect, input_Result_Quit, }; const file_list options = { diff --git a/src/nese.c b/src/nese.c index 0198418..2b2133d 100644 --- a/src/nese.c +++ b/src/nese.c @@ -179,14 +179,17 @@ static int do_game_menu(menu_state* menu, status = run_game_menu(menu, comp, state); if ( input_Result_View == status || - input_Result_Scale == status ) { + input_Result_Scale == status || + input_Result_Effect == status ) { if (input_Result_View == status) { #ifndef STANDALONE state->flags ^= (1 << State_Bit_Fullscreen); #endif - } else { + } else if (input_Result_Scale == status) { state->flags ^= (1 << State_Bit_Integer_Scale); + } else { + state->flags ^= (1 << State_Bit_CRT_Effect); } nes_render_set_flags(comp->rend, state->flags); continue; diff --git a/src/sdl_effect.c b/src/sdl_effect.c new file mode 100644 index 0000000..1aa1719 --- /dev/null +++ b/src/sdl_effect.c @@ -0,0 +1,71 @@ +#include "sdl_effect.h" + +/* +static const uint8_t the_effect[3][3][4] = { + {{14, 0, 216, 204}, {16, 0, 0, 217}, {21, 225, 1, 0},}, + {{81, 0, 1, 1}, {83, 1, 0, 3}, {72, 2, 0, 0},}, + {{ 5, 0, 252, 245}, { 6, 0, 0, 243}, {10, 236, 1, 0},}, +}; + +static const int effect_w = 3; +static const int effect_h = 3; +*/ + +static const uint8_t the_effect[2][2][4] = { + {{ 25, 0, 0, 0}, { 1, 0, 0, 0},}, + {{144, 0, 0, 0}, {133, 0, 0, 0},}, +}; + +static const int effect_w = 2; +static const int effect_h = 2; + + +sdl_effect* effect_init(SDL_Renderer* renderer, + int view_w, int view_h) { + const int tex_w = view_w * 2; + const int tex_h = view_h * 2; + + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); + + SDL_Texture* effect = SDL_CreateTexture( + renderer, SDL_PIXELFORMAT_BGRA8888, + SDL_TEXTUREACCESS_STREAMING, tex_w, tex_h + ); + + void* pixels = NULL; + int pitch = 0; + SDL_LockTexture(effect, NULL, &pixels, &pitch); + + uint8_t* dst_line = (uint8_t*)pixels; + for (int y = 0; y < tex_h; ++y) { + uint8_t* dst = dst_line; + for (int x = 0; x < tex_w; ++x) { + dst[0] = the_effect[y % effect_h][x % effect_w][0]; + dst[1] = the_effect[y % effect_h][x % effect_w][1]; + dst[2] = the_effect[y % effect_h][x % effect_w][2]; + dst[3] = the_effect[y % effect_h][x % effect_w][3]; + dst += 4; + } + dst_line += pitch; + } + + SDL_UnlockTexture(effect); + + SDL_SetTextureBlendMode(effect, SDL_BLENDMODE_BLEND); + + return (sdl_effect*)effect; +} + +void effect_done(sdl_effect* effect) { + if (effect) { + SDL_DestroyTexture((SDL_Texture*)effect); + } +} + +void effect_apply(sdl_effect* effect, SDL_Renderer* renderer, + const SDL_Rect* rect) { + if (effect) { + SDL_RenderCopy(renderer, (SDL_Texture*)effect, + NULL, rect); + } +} diff --git a/src/sdl_effect.h b/src/sdl_effect.h new file mode 100644 index 0000000..86784a1 --- /dev/null +++ b/src/sdl_effect.h @@ -0,0 +1,17 @@ +#ifndef NESE_SDL_EFFECT_H_ +#define NESE_SDL_EFFECT_H_ + +#include + + +typedef struct sdl_effect sdl_effect; + + +sdl_effect* effect_init(SDL_Renderer*, int, int); + +void effect_done(sdl_effect*); + +void effect_apply(sdl_effect*, SDL_Renderer*, const SDL_Rect*); + + +#endif // NESE_SDL_EFFECT_H_ diff --git a/src/sdl_render.c b/src/sdl_render.c index 9eff97a..7b719aa 100644 --- a/src/sdl_render.c +++ b/src/sdl_render.c @@ -7,6 +7,7 @@ #include "mapper.h" #include "menu.h" #include "sdl_overlay.h" +#include "sdl_effect.h" static SDL_Color nes_palette[64] = { @@ -47,6 +48,8 @@ typedef struct { SDL_Texture* texture; SDL_Rect view; + + struct sdl_effect* effect; } sdl_render_data; static sdl_render_data the_render_data = {0}; @@ -221,6 +224,11 @@ static int sdl_render_init(nes_Renderer* rend) { } if (0 == status) { + data->effect = effect_init( + data->renderer, + nes_ppu_render_w, nes_ppu_render_h + ); + SDL_SetPaletteColors(data->background->format->palette, nes_palette, 0U, 64U); SDL_SetColorKey(data->background, SDL_TRUE, 0xFFU); @@ -235,6 +243,7 @@ static int sdl_render_init(nes_Renderer* rend) { static void sdl_render_done(nes_Renderer* rend) { sdl_render_data* data = (sdl_render_data*)rend->data; + if (data->effect) effect_done(data->effect); overlay_done(&rend->overlay); sdl_overlay_font_done(&data->font); SDL_DestroyTexture(data->texture); @@ -629,6 +638,11 @@ static int sdl_render(nes_Renderer* rend, nes_ppu* ppu) { SDL_LockTextureToSurface(data->texture, NULL, &data->target); + if (rend->flags & (1 << State_Bit_CRT_Effect)) { + effect_apply(data->effect, data->renderer, + &data->view); + } + sdl_overlay_frame( &rend->overlay, &data->font, data->renderer, data->view.x, data->view.y, @@ -662,6 +676,10 @@ static void sdl_redraw_frame(nes_Renderer* rend, int dim) { NULL, &data->view); SDL_LockTextureToSurface(data->texture, NULL, &data->target); + if (rend->flags & (1 << State_Bit_CRT_Effect)) { + effect_apply(data->effect, data->renderer, &data->view); + } + if (dim) { SDL_SetTextureBlendMode(data->texture, SDL_BLENDMODE_NONE); diff --git a/src/state.c b/src/state.c index 33d3f82..59b2835 100644 --- a/src/state.c +++ b/src/state.c @@ -43,7 +43,7 @@ static const ini_datum prefs_schema = { .type = ini_section, .name = ".flags", .offset = offsetof(nese_State, flags), - .count = 2, + .count = 3, .data = (ini_datum[]){ { .type = ini_flag, @@ -53,6 +53,10 @@ static const ini_datum prefs_schema = { .type = ini_flag, .name = "integer_scale", .shift = State_Bit_Integer_Scale, + }, { + .type = ini_flag, + .name = "crt_effect", + .shift = State_Bit_CRT_Effect, }, } }, diff --git a/src/state.h b/src/state.h index 7b8c91f..c751bec 100644 --- a/src/state.h +++ b/src/state.h @@ -35,6 +35,7 @@ void cart_info_done(cart_info*); typedef enum { State_Bit_Fullscreen = 0, State_Bit_Integer_Scale = 1, + State_Bit_CRT_Effect = 2, } nese_State_Flag_Bits; typedef struct {