diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ef63cbd..5efe064e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ option(JAPANESE "Enable the Japanese-language build" OFF) option(FIX_BUGS "Fix certain bugs (see src/Bug Fixes.txt)" OFF) option(NONPORTABLE "Enable bits of code that aren't portable, but are what the original game used" OFF) option(FORCE_LOCAL_LIBS "Compile the built-in versions of SDL2, FreeType, and FLTK instead of using the system-provided ones" OFF) +set(RENDERER "Texture" CACHE STRING "Which renderer the game should use: 'Texture' for SDL2's hardware-accelerated Texture API, and 'Software' for a handwritten software renderer") project(CSE2 LANGUAGES C CXX) @@ -155,7 +156,6 @@ add_executable(CSE2 src/ValueView.cpp src/ValueView.h src/WindowsWrapper.h - src/Backends/SDLSoftware.cpp src/Backends/Rendering.h ) @@ -246,6 +246,12 @@ if(NONPORTABLE) target_compile_definitions(CSE2 PRIVATE NONPORTABLE) endif() +if(RENDERER MATCHES "Texture") + target_sources(CSE2 PRIVATE "src/Backends/SDLTexture.cpp") +elseif(RENDERER MATCHES "Software") + target_sources(CSE2 PRIVATE "src/Backends/SDLSoftware.cpp") +endif() + # Make some tweaks if we're targetting Windows if(WIN32) target_sources(CSE2 PRIVATE "${ASSETS_DIRECTORY}/resources/ICON/ICON.rc") diff --git a/src/Backends/Rendering.h b/src/Backends/Rendering.h index 60ff001f..9416c958 100644 --- a/src/Backends/Rendering.h +++ b/src/Backends/Rendering.h @@ -14,8 +14,8 @@ void Backend_DrawScreen(void); Backend_Surface* Backend_CreateSurface(unsigned int width, unsigned int height); void Backend_FreeSurface(Backend_Surface *surface); void Backend_LoadPixels(Backend_Surface *surface, const unsigned char *pixels, unsigned int width, unsigned int height, unsigned int pitch); -void Backend_Blit(const Backend_Surface *source_surface, const RECT *rect, Backend_Surface *destination_surface, long x, long y, BOOL colour_key); -void Backend_BlitToScreen(const Backend_Surface *source_surface, const RECT *rect, long x, long y, BOOL colour_key); +void Backend_Blit(Backend_Surface *source_surface, const RECT *rect, Backend_Surface *destination_surface, long x, long y, BOOL colour_key); +void Backend_BlitToScreen(Backend_Surface *source_surface, const RECT *rect, long x, long y, BOOL colour_key); void Backend_ColourFill(Backend_Surface *surface, const RECT *rect, unsigned char red, unsigned char green, unsigned char blue); void Backend_ColourFillToScreen(const RECT *rect, unsigned char red, unsigned char green, unsigned char blue); void Backend_ScreenToSurface(Backend_Surface *surface, const RECT *rect); diff --git a/src/Backends/SDLSoftware.cpp b/src/Backends/SDLSoftware.cpp index 1764334d..b9ed8b33 100644 --- a/src/Backends/SDLSoftware.cpp +++ b/src/Backends/SDLSoftware.cpp @@ -78,6 +78,7 @@ Backend_Surface* Backend_CreateSurface(unsigned int width, unsigned int height) void Backend_FreeSurface(Backend_Surface *surface) { free(surface->pixels); + free(surface); } void Backend_LoadPixels(Backend_Surface *surface, const unsigned char *pixels, unsigned int width, unsigned int height, unsigned int pitch) @@ -91,7 +92,7 @@ void Backend_LoadPixels(Backend_Surface *surface, const unsigned char *pixels, u } } -void Backend_Blit(const Backend_Surface *source_surface, const RECT *rect, Backend_Surface *destination_surface, long x, long y, BOOL colour_key) +void Backend_Blit(Backend_Surface *source_surface, const RECT *rect, Backend_Surface *destination_surface, long x, long y, BOOL colour_key) { RECT rect_clamped; @@ -163,7 +164,7 @@ void Backend_Blit(const Backend_Surface *source_surface, const RECT *rect, Backe } } -void Backend_BlitToScreen(const Backend_Surface *source_surface, const RECT *rect, long x, long y, BOOL colour_key) +void Backend_BlitToScreen(Backend_Surface *source_surface, const RECT *rect, long x, long y, BOOL colour_key) { Backend_Blit(source_surface, rect, &framebuffer, x, y, colour_key); } diff --git a/src/Backends/SDLTexture.cpp b/src/Backends/SDLTexture.cpp new file mode 100644 index 00000000..f4475397 --- /dev/null +++ b/src/Backends/SDLTexture.cpp @@ -0,0 +1,276 @@ +#include "Rendering.h" + +#include +#include + +#include "SDL.h" + +#include "../WindowsWrapper.h" + +#include "../Font.h" + +struct Backend_Surface +{ + BOOL needs_syncing; + SDL_Surface *sdl_surface; + SDL_Texture *texture; +}; + +static SDL_Renderer *renderer; +static SDL_Texture *screen_texture; + +static void FlushSurface(Backend_Surface *surface) +{ + unsigned char *buffer = (unsigned char*)malloc(surface->sdl_surface->w * surface->sdl_surface->h * 4); + unsigned char *buffer_pointer = buffer; + + // Convert the SDL_Surface's colour-keyed pixels to RGBA32 + for (int h = 0; h < surface->sdl_surface->h; ++h) + { + unsigned char *src_pixel = (unsigned char*)surface->sdl_surface->pixels + (h * surface->sdl_surface->pitch); + + for (int w = 0; w < surface->sdl_surface->w; ++w) + { + *buffer_pointer++ = src_pixel[0]; + *buffer_pointer++ = src_pixel[1]; + *buffer_pointer++ = src_pixel[2]; + + if (src_pixel[0] != 0 || src_pixel[1] != 0 || src_pixel[2] != 0) // Assumes the colour key will always be #00000000 (black) + *buffer_pointer++ = 0xFF; + else + *buffer_pointer++ = 0; + + src_pixel += 3; + } + } + + SDL_UpdateTexture(surface->texture, NULL, buffer, surface->sdl_surface->w * 4); + + free(buffer); +} + +static void RectToSDLRect(const RECT *rect, SDL_Rect *sdl_rect) +{ + sdl_rect->x = (int)rect->left; + sdl_rect->y = (int)rect->top; + sdl_rect->w = (int)(rect->right - rect->left); + sdl_rect->h = (int)(rect->bottom - rect->top); + + if (sdl_rect->w < 0) + sdl_rect->w = 0; + + if (sdl_rect->h < 0) + sdl_rect->h = 0; +} + +BOOL Backend_Init(SDL_Window *window) +{ + renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE); + + if (renderer == NULL) + return FALSE; + + int width, height; + SDL_GetRendererOutputSize(renderer, &width, &height); + screen_texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET, width, height); + + SDL_SetRenderTarget(renderer, screen_texture); + + return TRUE; +} + +void Backend_Deinit(void) +{ + SDL_DestroyRenderer(renderer); +} + +void Backend_DrawScreen(void) +{ + SDL_SetRenderTarget(renderer, NULL); + SDL_RenderCopy(renderer, screen_texture, NULL, NULL); + SDL_SetRenderTarget(renderer, screen_texture); + SDL_RenderPresent(renderer); +} + +Backend_Surface* Backend_CreateSurface(unsigned int width, unsigned int height) +{ + Backend_Surface *surface = (Backend_Surface*)malloc(sizeof(Backend_Surface)); + + if (surface == NULL) + return NULL; + + surface->sdl_surface = SDL_CreateRGBSurfaceWithFormat(0, width, height, 0, SDL_PIXELFORMAT_RGB24); + + if (surface->sdl_surface == NULL) + { + free(surface); + return NULL; + } + + surface->texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET, width, height); + + if (surface->texture == NULL) + { + SDL_FreeSurface(surface->sdl_surface); + free(surface); + return NULL; + } + + SDL_SetColorKey(surface->sdl_surface, SDL_TRUE, SDL_MapRGB(surface->sdl_surface->format, 0, 0, 0)); + + surface->needs_syncing = FALSE; + + return surface; +} + +void Backend_FreeSurface(Backend_Surface *surface) +{ + SDL_FreeSurface(surface->sdl_surface); + free(surface); +} + +void Backend_LoadPixels(Backend_Surface *surface, const unsigned char *pixels, unsigned int width, unsigned int height, unsigned int pitch) +{ + for (unsigned int h = 0; h < height; ++h) + { + const unsigned char *src_row = &pixels[h * pitch]; + unsigned char *dst_row = (unsigned char*)surface->sdl_surface->pixels + h * surface->sdl_surface->pitch; + + memcpy(dst_row, src_row, width * 3); + } + + surface->needs_syncing = TRUE; +} + +void Backend_Blit(Backend_Surface *source_surface, const RECT *rect, Backend_Surface *destination_surface, long x, long y, BOOL colour_key) +{ + if (source_surface->needs_syncing) + { + FlushSurface(source_surface); + source_surface->needs_syncing = FALSE; + } + + SDL_Rect source_rect; + RectToSDLRect(rect, &source_rect); + + SDL_Rect destination_rect = {x, y, source_rect.w, source_rect.h}; + + // Blit the surface + SDL_SetSurfaceBlendMode(source_surface->sdl_surface, colour_key ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE); + SDL_BlitSurface(source_surface->sdl_surface, &source_rect, destination_surface->sdl_surface, &destination_rect); + + // Now blit the texture + SDL_SetTextureBlendMode(source_surface->texture, colour_key ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE); + SDL_SetRenderTarget(renderer, destination_surface->texture); + SDL_RenderCopy(renderer, source_surface->texture, &source_rect, &destination_rect); + SDL_SetRenderTarget(renderer, screen_texture); +} + +void Backend_BlitToScreen(Backend_Surface *source_surface, const RECT *rect, long x, long y, BOOL colour_key) +{ + if (source_surface->needs_syncing) + { + FlushSurface(source_surface); + source_surface->needs_syncing = FALSE; + } + + SDL_Rect source_rect; + RectToSDLRect(rect, &source_rect); + + SDL_Rect destination_rect = {x, y, source_rect.w, source_rect.h}; + + // Blit the texture + SDL_SetTextureBlendMode(source_surface->texture, colour_key ? SDL_BLENDMODE_BLEND : SDL_BLENDMODE_NONE); + SDL_RenderCopy(renderer, source_surface->texture, &source_rect, &destination_rect); +} + +void Backend_ColourFill(Backend_Surface *surface, const RECT *rect, unsigned char red, unsigned char green, unsigned char blue) +{ + SDL_Rect sdl_rect; + RectToSDLRect(rect, &sdl_rect); + + // Blit the surface + SDL_FillRect(surface->sdl_surface, &sdl_rect, SDL_MapRGB(surface->sdl_surface->format, red, green, blue)); + + // Check colour-key + if (red == 0 && green == 0 && blue == 0) + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0); + else + SDL_SetRenderDrawColor(renderer, red, green, blue, 0xFF); + + // Draw colour + SDL_SetRenderTarget(renderer, surface->texture); + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE); + SDL_RenderFillRect(renderer, &sdl_rect); + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); + SDL_SetRenderTarget(renderer, screen_texture); +} + +void Backend_ColourFillToScreen(const RECT *rect, unsigned char red, unsigned char green, unsigned char blue) +{ + SDL_Rect sdl_rect; + RectToSDLRect(rect, &sdl_rect); + + // Draw colour + SDL_SetRenderDrawColor(renderer, red, green, blue, 0xFF); + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE); + SDL_RenderFillRect(renderer, &sdl_rect); + SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND); +} + +void Backend_ScreenToSurface(Backend_Surface *surface, const RECT *rect) +{ + SDL_Rect sdl_rect; + RectToSDLRect(rect, &sdl_rect); + + + // + // Copy screen to surface + // + + // Get renderer size + int w, h; + SDL_GetRendererOutputSize(renderer, &w, &h); + + // Get surface of what's currently rendered on screen + SDL_Surface *screen_surface = SDL_CreateRGBSurfaceWithFormat(0, w, h, 0, SDL_PIXELFORMAT_RGB24); + SDL_RenderReadPixels(renderer, NULL, SDL_PIXELFORMAT_RGB24, screen_surface->pixels, screen_surface->pitch); + + // Copy to specified surface + SDL_BlitSurface(screen_surface, &sdl_rect, surface->sdl_surface, &sdl_rect); + + // Cleanup + SDL_FreeSurface(screen_surface); + + + // + // Copy screen to texture + // + + SDL_SetRenderTarget(renderer, surface->texture); + SDL_RenderCopy(renderer, screen_texture, &sdl_rect, &sdl_rect); + SDL_SetRenderTarget(renderer, screen_texture); +} + +void Backend_DrawText(Backend_Surface *surface, FontObject *font, int x, int y, const char *text, unsigned long colour) +{ + DrawText(font, (unsigned char*)surface->sdl_surface->pixels, surface->sdl_surface->pitch, surface->sdl_surface->w, surface->sdl_surface->h, x, y, colour, text, strlen(text)); + surface->needs_syncing = TRUE; +} + +void Backend_DrawTextToScreen(FontObject *font, int x, int y, const char *text, unsigned long colour) +{ + // Painfully slow. Really need to add hardware-accelerated font rendering. + int surface_width, surface_height; + SDL_GetRendererOutputSize(renderer, &surface_width, &surface_height); + + SDL_Surface *screen_surface = SDL_CreateRGBSurfaceWithFormat(0, surface_width, surface_height, 0, SDL_PIXELFORMAT_RGB24); + SDL_RenderReadPixels(renderer, NULL, SDL_PIXELFORMAT_RGB24, screen_surface->pixels, screen_surface->pitch); + + DrawText(font, (unsigned char*)screen_surface->pixels, screen_surface->pitch, screen_surface->w, screen_surface->h, x, y, colour, text, strlen(text)); + + SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, screen_surface); + SDL_FreeSurface(screen_surface); + SDL_RenderCopy(renderer, texture, NULL, NULL); + SDL_DestroyTexture(texture); +} diff --git a/src/Draw.cpp b/src/Draw.cpp index 0b12fb80..8d81b01e 100644 --- a/src/Draw.cpp +++ b/src/Draw.cpp @@ -343,27 +343,6 @@ BOOL ReloadBitmap_Resource(const char *res, Surface_Ids surf_no) { return LoadBitmap_Resource(res, surf_no, FALSE); } -/* -static SDL_Rect RectToSDLRect(RECT *rect) -{ - SDL_Rect SDLRect = {(int)rect->left, (int)rect->top, (int)(rect->right - rect->left), (int)(rect->bottom - rect->top)}; - if (SDLRect.w < 0) - SDLRect.w = 0; - if (SDLRect.h < 0) - SDLRect.h = 0; - return SDLRect; -} - -static SDL_Rect RectToSDLRectScaled(RECT *rect) -{ - SDL_Rect SDLRect = RectToSDLRect(rect); - SDLRect.x *= magnification; - SDLRect.y *= magnification; - SDLRect.w *= magnification; - SDLRect.h *= magnification; - return SDLRect; -} -*/ static void ScaleRect(const RECT *source_rect, RECT *destination_rect) { @@ -379,17 +358,10 @@ void BackupSurface(Surface_Ids surf_no, const RECT *rect) ScaleRect(rect, &frameRect); Backend_ScreenToSurface(surf[surf_no].backend, &frameRect); - //surf[surf_no].needs_updating = TRUE; } static void DrawBitmap(const RECT *rcView, int x, int y, const RECT *rect, Surface_Ids surf_no, BOOL transparent) { -/* if (surf[surf_no].needs_updating) - { - FlushSurface(surf_no); - surf[surf_no].needs_updating = FALSE; - } -*/ RECT frameRect; frameRect.left = rect->left; @@ -445,7 +417,6 @@ void Surface2Surface(int x, int y, const RECT *rect, int to, int from) ScaleRect(rect, &frameRect); Backend_Blit(surf[from].backend, &frameRect, surf[to].backend, x * magnification, y * magnification, TRUE); - //surf[to].needs_updating = TRUE; } unsigned long GetCortBoxColor(unsigned long col) @@ -481,7 +452,6 @@ void CortBox2(const RECT *rect, unsigned long col, Surface_Ids surf_no) const unsigned char col_blue = (unsigned char)((col >> 16) & 0xFF); Backend_ColourFill(surf[surf_no].backend, &destRect, col_red, col_green, col_blue); - //surf[surf_no].needs_updating = TRUE; } #ifdef WINDOWS @@ -591,7 +561,6 @@ void PutText(int x, int y, const char *text, unsigned long color) void PutText2(int x, int y, const char *text, unsigned long color, Surface_Ids surf_no) { Backend_DrawText(surf[surf_no].backend, gFont, x * magnification, y * magnification, text, color); - //surf[surf_no].needs_updating = TRUE; } void EndTextObject()