From 114a166a011a13051e42577fb6fa902b46df4ed9 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sun, 9 Feb 2020 14:00:52 +0000 Subject: [PATCH 1/4] Blit: Fix scaled output handling Fixes #594 --- SourceS/sdl2_to_1_2_backports.h | 18 ++++++++- SourceS/sdl_compat.h | 9 +++++ SourceX/dx.cpp | 71 +++++++++++++++++++++------------ 3 files changed, 71 insertions(+), 27 deletions(-) diff --git a/SourceS/sdl2_to_1_2_backports.h b/SourceS/sdl2_to_1_2_backports.h index 97bb566e7..c35e13e8d 100644 --- a/SourceS/sdl2_to_1_2_backports.h +++ b/SourceS/sdl2_to_1_2_backports.h @@ -215,6 +215,21 @@ SDL_FreePalette(SDL_Palette *palette) SDL_free(palette); } +inline bool SDL_HasColorKey(SDL_Surface *surface) +{ + return (surface->flags & (SDL_SRCCOLORKEY | SDL_RLEACCELOK)) != 0; +} + +inline int SDL_GetColorKey(SDL_Surface *surface, Uint32 *colorkey) +{ + if (!SDL_HasColorKey(surface)) { + SDL_SetError("Surface doesn't have a colorkey"); + return -1; + } + *colorkey = surface->format->colorkey; + return 0; +} + //= Pixel formats #define SDL_PIXELFORMAT_INDEX8 1 @@ -259,7 +274,8 @@ inline void SDLBackport_PixelformatToMask(int pixelformat, Uint32 *flags, Uint32 */ inline bool SDLBackport_PixelFormatFormatEq(const SDL_PixelFormat *a, const SDL_PixelFormat *b) { - return a->BitsPerPixel == b->BitsPerPixel && (a->palette != nullptr) == (b->palette != nullptr); + return a->BitsPerPixel == b->BitsPerPixel && (a->palette != nullptr) == (b->palette != nullptr) + && a->Rmask == b->Rmask && a->Gmask == b->Gmask && a->Bmask == b->Bmask; } /** diff --git a/SourceS/sdl_compat.h b/SourceS/sdl_compat.h index 9c14ad16c..aac01ef42 100644 --- a/SourceS/sdl_compat.h +++ b/SourceS/sdl_compat.h @@ -14,6 +14,7 @@ #define SDLC_KEYSTATE_LEFT SDL_SCANCODE_LEFT #define SDLC_KEYSTATE_RIGHT SDL_SCANCODE_RIGHT #else +#include "sdl2_to_1_2_backports.h" #define SDLC_KEYSTATE_LEFTCTRL SDLK_LCTRL #define SDLC_KEYSTATE_RIGHTCTRL SDLK_RCTRL #define SDLC_KEYSTATE_LEFTSHIFT SDLK_LSHIFT @@ -26,6 +27,14 @@ #define SDLC_KEYSTATE_RIGHT SDLK_RIGHT #endif +inline bool SDLC_PixelFormatEq(const SDL_PixelFormat *a, const SDL_PixelFormat *b) { +#ifndef USE_SDL1 + return a->format == b->format; +#else + return SDLBackport_PixelFormatFormatEq(a, b); +#endif +} + inline const Uint8 *SDLC_GetKeyState() { #ifndef USE_SDL1 diff --git a/SourceX/dx.cpp b/SourceX/dx.cpp index 3fc9b044c..06da4adc3 100644 --- a/SourceX/dx.cpp +++ b/SourceX/dx.cpp @@ -173,40 +173,59 @@ void CreatePalette() void BltFast(SDL_Rect *src_rect, SDL_Rect *dst_rect) { - if (OutputRequiresScaling()) { - ScaleOutputRect(dst_rect); - // Convert from 8-bit to 32-bit - SDL_Surface *tmp = SDL_ConvertSurface(pal_surface, GetOutputSurface()->format, 0); - if (SDL_BlitScaled(tmp, src_rect, GetOutputSurface(), dst_rect) <= -1) { - SDL_FreeSurface(tmp); - ErrSdl(); - } - SDL_FreeSurface(tmp); - } else { - // Convert from 8-bit to 32-bit - if (SDL_BlitSurface(pal_surface, src_rect, GetOutputSurface(), dst_rect) <= -1) { - ErrSdl(); - } - } + Blit(pal_surface, src_rect, dst_rect); } void Blit(SDL_Surface *src, SDL_Rect *src_rect, SDL_Rect *dst_rect) { - if (OutputRequiresScaling()) { - ScaleOutputRect(dst_rect); - // Convert from 8-bit to 32-bit - SDL_Surface *tmp = SDL_ConvertSurface(src, GetOutputSurface()->format, 0); - if (SDL_BlitScaled(tmp, src_rect, GetOutputSurface(), dst_rect) <= -1) { - SDL_FreeSurface(tmp); + SDL_Surface *dst = GetOutputSurface(); + if (!OutputRequiresScaling()) { + if (SDL_BlitSurface(src, src_rect, dst, dst_rect) < 0) ErrSdl(); - } - SDL_FreeSurface(tmp); - } else { - // Convert from 8-bit to 32-bit - if (SDL_BlitSurface(src, src_rect, GetOutputSurface(), dst_rect) <= -1) { + return; + } + + ScaleOutputRect(dst_rect); + + // Same pixel format: We can call BlitScaled directly. + if (SDLC_PixelFormatEq(src->format, dst->format)) { + if (SDL_BlitScaled(src, src_rect, dst, dst_rect) < 0) + ErrSdl(); + return; + } + + // If the surface has a color key, we must stretch first and can then call BlitSurface. + if (SDL_HasColorKey(src)) { + SDL_Surface *stretched = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_rect->w, dst_rect->h, src->format->BitsPerPixel, + src->format->Rmask, src->format->Gmask, src->format->BitsPerPixel, src->format->Amask); + Uint32 colorkey; + SDL_GetColorKey(src, &colorkey); + SDLC_SetColorKey(stretched, colorkey); +#ifndef USE_SDL1 + if (SDL_ISPIXELFORMAT_INDEXED(src->format->format)) + SDL_SetSurfacePalette(stretched, src->format->palette); +#else + if (src->format->palette != nullptr) + SDL_SetPalette(stretched, SDL_LOGPAL, src->format->palette->colors, 0, src->format->palette->ncolors); +#endif + SDL_Rect stretched_rect = { 0, 0, dst_rect->w, dst_rect->h }; + if (SDL_SoftStretch(src, src_rect, stretched, &stretched_rect) < 0 + || SDL_BlitSurface(stretched, &stretched_rect, dst, dst_rect) < 0) { + SDL_FreeSurface(stretched); ErrSdl(); } + SDL_FreeSurface(stretched); + return; + } + + // A surface with a non-output pixel format but without a color key needs scaling. + // We can convert the format and then call BlitScaled. + SDL_Surface *converted = SDL_ConvertSurface(src, dst->format, 0); + if (SDL_BlitScaled(converted, src_rect, dst, dst_rect) < 0) { + SDL_FreeSurface(converted); + ErrSdl(); } + SDL_FreeSurface(converted); } /** From 58fde28a2a45bff8a070ee0cb31a8c0732e9da43 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sun, 9 Feb 2020 15:08:28 +0000 Subject: [PATCH 2/4] Blit: ifdef all SDL1 stretching code OutputRequiresScaling is always false on SDL2 so we don't need SDL2 compatibility there --- SourceS/sdl2_to_1_2_backports.h | 12 +----------- SourceS/sdl_compat.h | 9 --------- SourceX/dx.cpp | 20 +++++++++----------- 3 files changed, 10 insertions(+), 31 deletions(-) diff --git a/SourceS/sdl2_to_1_2_backports.h b/SourceS/sdl2_to_1_2_backports.h index c35e13e8d..fe9ed24d9 100644 --- a/SourceS/sdl2_to_1_2_backports.h +++ b/SourceS/sdl2_to_1_2_backports.h @@ -217,17 +217,7 @@ SDL_FreePalette(SDL_Palette *palette) inline bool SDL_HasColorKey(SDL_Surface *surface) { - return (surface->flags & (SDL_SRCCOLORKEY | SDL_RLEACCELOK)) != 0; -} - -inline int SDL_GetColorKey(SDL_Surface *surface, Uint32 *colorkey) -{ - if (!SDL_HasColorKey(surface)) { - SDL_SetError("Surface doesn't have a colorkey"); - return -1; - } - *colorkey = surface->format->colorkey; - return 0; + return (surface->flags & SDL_SRCCOLORKEY) != 0; } //= Pixel formats diff --git a/SourceS/sdl_compat.h b/SourceS/sdl_compat.h index aac01ef42..9c14ad16c 100644 --- a/SourceS/sdl_compat.h +++ b/SourceS/sdl_compat.h @@ -14,7 +14,6 @@ #define SDLC_KEYSTATE_LEFT SDL_SCANCODE_LEFT #define SDLC_KEYSTATE_RIGHT SDL_SCANCODE_RIGHT #else -#include "sdl2_to_1_2_backports.h" #define SDLC_KEYSTATE_LEFTCTRL SDLK_LCTRL #define SDLC_KEYSTATE_RIGHTCTRL SDLK_RCTRL #define SDLC_KEYSTATE_LEFTSHIFT SDLK_LSHIFT @@ -27,14 +26,6 @@ #define SDLC_KEYSTATE_RIGHT SDLK_RIGHT #endif -inline bool SDLC_PixelFormatEq(const SDL_PixelFormat *a, const SDL_PixelFormat *b) { -#ifndef USE_SDL1 - return a->format == b->format; -#else - return SDLBackport_PixelFormatFormatEq(a, b); -#endif -} - inline const Uint8 *SDLC_GetKeyState() { #ifndef USE_SDL1 diff --git a/SourceX/dx.cpp b/SourceX/dx.cpp index 06da4adc3..29d996009 100644 --- a/SourceX/dx.cpp +++ b/SourceX/dx.cpp @@ -179,16 +179,20 @@ void BltFast(SDL_Rect *src_rect, SDL_Rect *dst_rect) void Blit(SDL_Surface *src, SDL_Rect *src_rect, SDL_Rect *dst_rect) { SDL_Surface *dst = GetOutputSurface(); +#ifndef USE_SDL1 + if (SDL_BlitSurface(src, src_rect, dst, dst_rect) < 0) + ErrSdl(); + return; +#else if (!OutputRequiresScaling()) { if (SDL_BlitSurface(src, src_rect, dst, dst_rect) < 0) ErrSdl(); return; } - - ScaleOutputRect(dst_rect); + if (dst_rect != nullptr) ScaleOutputRect(dst_rect); // Same pixel format: We can call BlitScaled directly. - if (SDLC_PixelFormatEq(src->format, dst->format)) { + if (SDLBackport_PixelFormatFormatEq(src->format, dst->format)) { if (SDL_BlitScaled(src, src_rect, dst, dst_rect) < 0) ErrSdl(); return; @@ -198,16 +202,9 @@ void Blit(SDL_Surface *src, SDL_Rect *src_rect, SDL_Rect *dst_rect) if (SDL_HasColorKey(src)) { SDL_Surface *stretched = SDL_CreateRGBSurface(SDL_SWSURFACE, dst_rect->w, dst_rect->h, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask, src->format->BitsPerPixel, src->format->Amask); - Uint32 colorkey; - SDL_GetColorKey(src, &colorkey); - SDLC_SetColorKey(stretched, colorkey); -#ifndef USE_SDL1 - if (SDL_ISPIXELFORMAT_INDEXED(src->format->format)) - SDL_SetSurfacePalette(stretched, src->format->palette); -#else + SDL_SetColorKey(stretched, SDL_SRCCOLORKEY, src->format->colorkey); if (src->format->palette != nullptr) SDL_SetPalette(stretched, SDL_LOGPAL, src->format->palette->colors, 0, src->format->palette->ncolors); -#endif SDL_Rect stretched_rect = { 0, 0, dst_rect->w, dst_rect->h }; if (SDL_SoftStretch(src, src_rect, stretched, &stretched_rect) < 0 || SDL_BlitSurface(stretched, &stretched_rect, dst, dst_rect) < 0) { @@ -226,6 +223,7 @@ void Blit(SDL_Surface *src, SDL_Rect *src_rect, SDL_Rect *dst_rect) ErrSdl(); } SDL_FreeSurface(converted); +#endif } /** From bbe22b3be7c653b3ce1185844eb7d1a8cce0039b Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sun, 9 Feb 2020 16:34:02 +0000 Subject: [PATCH 3/4] Fix credits when using software scaling --- SourceX/DiabloUI/credits.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/SourceX/DiabloUI/credits.cpp b/SourceX/DiabloUI/credits.cpp index d405d6443..4b1a56fec 100644 --- a/SourceX/DiabloUI/credits.cpp +++ b/SourceX/DiabloUI/credits.cpp @@ -224,7 +224,11 @@ void CreditsRenderer::Render() while (lines_.back().index + 1 != lines_end) lines_.push_back(PrepareLine(lines_.back().index + 1)); - SDL_SetClipRect(GetOutputSurface(), &VIEWPORT); + SDL_Rect viewport = VIEWPORT; + ScaleOutputRect(&viewport); + SDL_SetClipRect(GetOutputSurface(), &viewport); + + // We use unscaled coordinates for calculation throughout. decltype(SDL_Rect().y) dest_y = VIEWPORT.y - (offset_y - lines_begin * LINE_H); for (std::size_t i = 0; i < lines_.size(); ++i, dest_y += LINE_H) { auto &line = lines_[i]; From 1f129de0c34679224e93a9784ef0b5c9ce483180 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sun, 9 Feb 2020 16:29:58 +0000 Subject: [PATCH 4/4] DiabloUI: Scale surfaces at load/creation time --- SourceX/DiabloUI/art.cpp | 4 ++++ SourceX/DiabloUI/art_draw.cpp | 7 ++++++- SourceX/DiabloUI/credits.cpp | 12 ++++++++++-- SourceX/display.cpp | 36 +++++++++++++++++++++++++++++++++++ SourceX/display.h | 3 +++ 5 files changed, 59 insertions(+), 3 deletions(-) diff --git a/SourceX/DiabloUI/art.cpp b/SourceX/DiabloUI/art.cpp index 3d9087f18..4e517f767 100644 --- a/SourceX/DiabloUI/art.cpp +++ b/SourceX/DiabloUI/art.cpp @@ -1,4 +1,5 @@ #include "DiabloUI/art.h" +#include "display.h" namespace dvl { @@ -40,6 +41,8 @@ void LoadArt(const char *pszFile, Art *art, int frames, SDL_Color *pPalette) art->surface = art_surface; art->frame_height = height / frames; + + ScaleSurfaceToOutput(&art->surface); } void LoadMaskedArt(const char *pszFile, Art *art, int frames, int mask) @@ -55,6 +58,7 @@ void LoadArt(Art *art, const BYTE *artData, int w, int h, int frames) art->surface = SDL_CreateRGBSurfaceWithFormatFrom( const_cast(artData), w, h, 8, w, SDL_PIXELFORMAT_INDEX8); art->frame_height = h / frames; + ScaleSurfaceToOutput(&art->surface); } } // namespace dvl diff --git a/SourceX/DiabloUI/art_draw.cpp b/SourceX/DiabloUI/art_draw.cpp index 8cf426ad0..52a116caa 100644 --- a/SourceX/DiabloUI/art_draw.cpp +++ b/SourceX/DiabloUI/art_draw.cpp @@ -1,4 +1,5 @@ #include "DiabloUI/art_draw.h" +#include "display.h" namespace dvl { @@ -18,11 +19,14 @@ void DrawArt(int screenX, int screenY, Art *art, int nFrame, static_cast(art->w()), static_cast(art->h()) }; + ScaleOutputRect(&src_rect); + if (srcW && srcW < src_rect.w) src_rect.w = srcW; if (srcH && srcH < src_rect.h) src_rect.h = srcH; SDL_Rect dst_rect = { screenX, screenY, src_rect.w, src_rect.h }; + ScaleOutputRect(&dst_rect); if (art->surface->format->BitsPerPixel == 8 && art->palette_version != pal_surface_palette_version) { if (SDLC_SetSurfaceColors(art->surface, pal_surface->format->palette) <= -1) @@ -30,7 +34,8 @@ void DrawArt(int screenX, int screenY, Art *art, int nFrame, art->palette_version = pal_surface_palette_version; } - Blit(art->surface, &src_rect, &dst_rect); + if (SDL_BlitSurface(art->surface, &src_rect, GetOutputSurface(), &dst_rect) < 0) + ErrSdl(); } void DrawAnimatedArt(Art *art, int screenX, int screenY) { diff --git a/SourceX/DiabloUI/credits.cpp b/SourceX/DiabloUI/credits.cpp index 4b1a56fec..71edf0575 100644 --- a/SourceX/DiabloUI/credits.cpp +++ b/SourceX/DiabloUI/credits.cpp @@ -95,6 +95,10 @@ CachedLine PrepareLine(std::size_t index) if (SDL_BlitSurface(text.get(), nullptr, surface.get(), nullptr) <= -1) ErrSdl(); + + SDL_Surface *surface_ptr = surface.release(); + ScaleSurfaceToOutput(&surface_ptr); + surface.reset(surface_ptr); } return CachedLine(index, std::move(surface)); @@ -243,8 +247,12 @@ void CreditsRenderer::Render() if (CREDITS_LINES[line.index][0] == '\t') dest_x += 40; - SDL_Rect dest_rect = { dest_x, dest_y, line.surface.get()->w, line.surface.get()->h }; - Blit(line.surface.get(), nullptr, &dest_rect); + SDL_Rect dst_rect = { dest_x, dest_y, 0, 0 }; + ScaleOutputRect(&dst_rect); + dst_rect.w = line.surface.get()->w; + dst_rect.h = line.surface.get()->h; + if (SDL_BlitSurface(line.surface.get(), nullptr, GetOutputSurface(), &dst_rect) < 0) + ErrSdl(); } SDL_SetClipRect(GetOutputSurface(), nullptr); } diff --git a/SourceX/display.cpp b/SourceX/display.cpp index 812e584cc..9490edfc8 100644 --- a/SourceX/display.cpp +++ b/SourceX/display.cpp @@ -171,4 +171,40 @@ void ScaleOutputRect(SDL_Rect *rect) rect->h = rect->h * surface->h / SCREEN_HEIGHT; } +#ifdef USE_SDL1 +namespace { + +SDL_Surface *CreateScaledSurface(SDL_Surface *src) +{ + SDL_Rect stretched_rect = { 0, 0, static_cast(src->w), static_cast(src->h) }; + ScaleOutputRect(&stretched_rect); + SDL_Surface *stretched = SDL_CreateRGBSurface( + SDL_SWSURFACE, stretched_rect.w, stretched_rect.h, src->format->BitsPerPixel, + src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask); + if (SDL_HasColorKey(src)) { + SDL_SetColorKey(stretched, SDL_SRCCOLORKEY, src->format->colorkey); + if (src->format->palette != nullptr) + SDL_SetPalette(stretched, SDL_LOGPAL, src->format->palette->colors, 0, src->format->palette->ncolors); + } + if (SDL_SoftStretch((src), nullptr, stretched, &stretched_rect) < 0) { + SDL_FreeSurface(stretched); + ErrSdl(); + } + return stretched; +} + +} // namespace +#endif // USE_SDL1 + +void ScaleSurfaceToOutput(SDL_Surface **surface) +{ +#ifdef USE_SDL1 + if (!OutputRequiresScaling()) + return; + SDL_Surface *stretched = CreateScaledSurface(*surface); + SDL_FreeSurface((*surface)); + *surface = stretched; +#endif +} + } // namespace dvl diff --git a/SourceX/display.h b/SourceX/display.h index 3873cf8f2..e1a2eac53 100644 --- a/SourceX/display.h +++ b/SourceX/display.h @@ -28,6 +28,9 @@ bool OutputRequiresScaling(); // Scales rect if necessary. void ScaleOutputRect(SDL_Rect *rect); +// If the output requires software scaling, replaces the given surface with a scaled one. +void ScaleSurfaceToOutput(SDL_Surface **surface); + // Convert from output coordinates to logical (resolution-independent) coordinates. template < typename T,