From 0aa515355afc7c52cc81cef0a44a10313d522396 Mon Sep 17 00:00:00 2001 From: "Christian F. Coors" Date: Sat, 9 Dec 2017 17:08:38 +0100 Subject: [PATCH] Add cursor scaling --- src/openrct2-ui/CursorRepository.cpp | 130 ++++++++++++++++++++------- src/openrct2-ui/CursorRepository.h | 46 ++++++++-- src/openrct2-ui/UiContext.cpp | 5 ++ src/openrct2-ui/windows/Options.cpp | 2 + src/openrct2/Context.cpp | 5 ++ src/openrct2/Context.h | 1 + src/openrct2/interface/Cursors.h | 6 +- src/openrct2/interface/console.c | 1 + src/openrct2/ui/DummyUiContext.cpp | 1 + src/openrct2/ui/UiContext.h | 1 + 10 files changed, 161 insertions(+), 37 deletions(-) diff --git a/src/openrct2-ui/CursorRepository.cpp b/src/openrct2-ui/CursorRepository.cpp index 1b798012bf..d6d18c3d76 100644 --- a/src/openrct2-ui/CursorRepository.cpp +++ b/src/openrct2-ui/CursorRepository.cpp @@ -14,41 +14,27 @@ *****************************************************************************/ #pragma endregion +#include + #include -#include +#include #include #include + #include "CursorRepository.h" using namespace OpenRCT2::Ui; CursorRepository::~CursorRepository() { - for (size_t i = 0; i < CURSOR_COUNT; i++) - { - SDL_FreeCursor(_loadedCursors[i]); - _loadedCursors[i] = nullptr; - } + _scaledCursors.clear(); _currentCursor = CURSOR_UNDEFINED; + _currentCursorScale = 1; } void CursorRepository::LoadCursors() { - // Using system cursors - _loadedCursors[CURSOR_ARROW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); - _loadedCursors[CURSOR_HAND_POINT] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND); - - // Using custom cursors - for (size_t i = 0; i < CURSOR_COUNT; i++) - { - const CursorData * cursorData = GetCursorData((CURSOR_ID)i); - if (cursorData != nullptr) - { - _loadedCursors[i] = Create(cursorData); - } - } - - _currentCursor = CURSOR_UNDEFINED; + SetCursorScale(static_cast(round(gConfigGeneral.window_scale))); SetCurrentCursor(CURSOR_ARROW); } @@ -61,20 +47,104 @@ void CursorRepository::SetCurrentCursor(CURSOR_ID cursorId) { if (_currentCursor != cursorId) { - SDL_Cursor * cursor = _loadedCursors[cursorId]; + SDL_Cursor * cursor = _scaledCursors.at(_currentCursorScale).getScaledCursor(cursorId); SDL_SetCursor(cursor); _currentCursor = cursorId; } } -SDL_Cursor * CursorRepository::Create(const CursorData * cursorInfo) +static bool getBit(const uint8 * data, size_t x, size_t y, size_t width) { - SDL_Cursor * cursor = SDL_CreateCursor( - cursorInfo->Data, - cursorInfo->Mask, - CURSOR_WIDTH, - CURSOR_HEIGHT, - cursorInfo->HotSpot.X, - cursorInfo->HotSpot.Y); + size_t position = y * width + x; + return (data[position / 8] & (1 << (7 - (x % 8)))) != 0; +} + +static void setBit(uint8 * data, size_t x, size_t y, size_t width) +{ + size_t position = y * width + x; + data[position / 8] |= (1 << (7 - (position % 8))); +} + +static void drawRect(uint8 * data, size_t x, size_t y, size_t width, size_t scale) +{ + for (size_t outY = (y * scale); outY < ((1 + y) * scale); outY++) + { + for (size_t outX = (x * scale); outX < ((1 + x) * scale); outX++) + { + setBit(data, outX, outY, width * scale); + } + } +} + +static uint8 * scaleDataArray(const uint8 data[], size_t width, size_t height, size_t scale) +{ + size_t length = width * height; + auto * ret = static_cast(calloc(sizeof(uint8), length * scale * scale)); + + for (size_t y = 0; y < height * 8; y++) + { + for (size_t x = 0; x < width; x++) + { + bool value = getBit(data, x, y, width); + if (!value) continue; + + drawRect(ret, x, y, width, scale); + } + } + + return ret; +} + +SDL_Cursor * CursorRepository::Create(const CursorData * cursorInfo, uint8 scale) +{ + SDL_Cursor * cursor; + + auto integer_scale = (int) round(scale); + + auto data = scaleDataArray(cursorInfo->Data, CURSOR_BIT_WIDTH, CURSOR_HEIGHT, static_cast(integer_scale)); + auto mask = scaleDataArray(cursorInfo->Mask, CURSOR_BIT_WIDTH, CURSOR_HEIGHT, static_cast(integer_scale)); + + cursor = SDL_CreateCursor( + data, + mask, + BASE_CURSOR_WIDTH * integer_scale, + BASE_CURSOR_HEIGHT * integer_scale, + cursorInfo->HotSpot.X * integer_scale, + cursorInfo->HotSpot.Y * integer_scale); + + free(data); + free(mask); + + return cursor; } + +void CursorRepository::SetCursorScale(uint8 cursorScale) +{ + if (cursorScale > 0.0) + { + _currentCursorScale = cursorScale; + GenerateScaledCursorSetHolder(_currentCursorScale); + } +} + +void CursorRepository::GenerateScaledCursorSetHolder(uint8 scale) +{ + if (_scaledCursors.find(scale) == _scaledCursors.end()) + { + std::function cursorGenerator = [this, scale](CURSOR_ID cursorId) + { + switch (cursorId) + { + // We can't scale the system cursors, but they should be appropriately scaled anyway + case CURSOR_ARROW: + return SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); + case CURSOR_HAND_POINT: + return SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND); + default: + return this->Create(GetCursorData(cursorId), scale); + } + }; + _scaledCursors.emplace(scale, cursorGenerator); + } +} diff --git a/src/openrct2-ui/CursorRepository.h b/src/openrct2-ui/CursorRepository.h index dba0140c18..0d0ff64f1b 100644 --- a/src/openrct2-ui/CursorRepository.h +++ b/src/openrct2-ui/CursorRepository.h @@ -14,6 +14,11 @@ *****************************************************************************/ #pragma endregion +#include +#include + +#include + #include struct SDL_Cursor; @@ -25,20 +30,51 @@ namespace OpenRCT2 class CursorRepository { private: - constexpr static sint32 CURSOR_WIDTH = 32; - constexpr static sint32 CURSOR_HEIGHT = 32; + class CursorSetHolder + { + private: + SDL_Cursor * _cursors[CURSOR_COUNT] = {nullptr}; + public: + CursorSetHolder(const std::function & getCursor) + { + for (size_t i = 0; i < CURSOR_COUNT; i++) + { + _cursors[i] = getCursor(static_cast(i)); + } + } - SDL_Cursor * _loadedCursors[CURSOR_COUNT] = { nullptr }; - CURSOR_ID _currentCursor = CURSOR_UNDEFINED; + ~CursorSetHolder() + { + for (size_t i = 0; i < CURSOR_COUNT; i++) + { + SDL_FreeCursor(_cursors[i]); + } + } + + SDL_Cursor * getScaledCursor(CURSOR_ID cursorId) + { + return _cursors[cursorId]; + } + }; + + constexpr static sint32 BASE_CURSOR_WIDTH = 32; + constexpr static sint32 BASE_CURSOR_HEIGHT = 32; + + CURSOR_ID _currentCursor = CURSOR_UNDEFINED; + uint8 _currentCursorScale = 1; + + std::map _scaledCursors; public: ~CursorRepository(); void LoadCursors(); CURSOR_ID GetCurrentCursor(); void SetCurrentCursor(CURSOR_ID cursorId); + void SetCursorScale(uint8 cursorScale); private: - SDL_Cursor * Create(const CursorData * cursorInfo); + SDL_Cursor * Create(const CursorData * cursorInfo, uint8 scale); + void GenerateScaledCursorSetHolder(uint8 scale); static const CursorData * GetCursorData(CURSOR_ID cursorId); }; } diff --git a/src/openrct2-ui/UiContext.cpp b/src/openrct2-ui/UiContext.cpp index c48aa29b05..a400b867d9 100644 --- a/src/openrct2-ui/UiContext.cpp +++ b/src/openrct2-ui/UiContext.cpp @@ -212,6 +212,11 @@ public: _cursorRepository.SetCurrentCursor(cursor); } + void SetCursorScale(uint8 scale) override + { + _cursorRepository.SetCursorScale(scale); + } + void SetCursorVisible(bool value) override { SDL_ShowCursor(value ? SDL_ENABLE : SDL_DISABLE); diff --git a/src/openrct2-ui/windows/Options.cpp b/src/openrct2-ui/windows/Options.cpp index d4a66eda38..5bdbdfb177 100644 --- a/src/openrct2-ui/windows/Options.cpp +++ b/src/openrct2-ui/windows/Options.cpp @@ -970,6 +970,7 @@ static void window_options_mousedown(rct_window *w, rct_widgetindex widgetIndex, config_save_default(); gfx_invalidate_screen(); context_trigger_resize(); + context_update_cursor_scale(); break; case WIDX_SCALE_DOWN: gConfigGeneral.window_scale -= 0.25f; @@ -977,6 +978,7 @@ static void window_options_mousedown(rct_window *w, rct_widgetindex widgetIndex, config_save_default(); gfx_invalidate_screen(); context_trigger_resize(); + context_update_cursor_scale(); break; case WIDX_SCALE_QUALITY_DROPDOWN: gDropdownItemsFormat[0] = STR_DROPDOWN_MENU_LABEL; diff --git a/src/openrct2/Context.cpp b/src/openrct2/Context.cpp index 13317bdea0..1b740de3d5 100644 --- a/src/openrct2/Context.cpp +++ b/src/openrct2/Context.cpp @@ -907,6 +907,11 @@ extern "C" GetContext()->GetUiContext()->SetCursor((CURSOR_ID)cursor); } + void context_update_cursor_scale() + { + GetContext()->GetUiContext()->SetCursorScale(static_cast(round(gConfigGeneral.window_scale))); + } + void context_hide_cursor() { GetContext()->GetUiContext()->SetCursorVisible(false); diff --git a/src/openrct2/Context.h b/src/openrct2/Context.h index 83961191e3..899de44c3a 100644 --- a/src/openrct2/Context.h +++ b/src/openrct2/Context.h @@ -195,6 +195,7 @@ extern "C" #endif void context_init(); void context_setcurrentcursor(sint32 cursor); + void context_update_cursor_scale(); void context_hide_cursor(); void context_show_cursor(); void context_get_cursor_position(sint32 * x, sint32 * y); diff --git a/src/openrct2/interface/Cursors.h b/src/openrct2/interface/Cursors.h index e70da4adb3..c3bd7b7dc8 100644 --- a/src/openrct2/interface/Cursors.h +++ b/src/openrct2/interface/Cursors.h @@ -55,6 +55,8 @@ enum CURSOR_ID namespace OpenRCT2 { namespace Ui { + constexpr size_t CURSOR_BIT_WIDTH = 32; + constexpr size_t CURSOR_HEIGHT = 4; struct CursorData { struct HotSpot @@ -62,8 +64,8 @@ namespace OpenRCT2 { namespace Ui sint16 X; sint16 Y; } HotSpot; - uint8 Data[32 * 4]; - uint8 Mask[32 * 4]; + uint8 Data[CURSOR_BIT_WIDTH * CURSOR_HEIGHT]; + uint8 Mask[CURSOR_BIT_WIDTH * CURSOR_HEIGHT]; }; } } diff --git a/src/openrct2/interface/console.c b/src/openrct2/interface/console.c index f4a3e5b21f..28d4a19976 100644 --- a/src/openrct2/interface/console.c +++ b/src/openrct2/interface/console.c @@ -1038,6 +1038,7 @@ static sint32 cc_set(const utf8 **argv, sint32 argc) config_save_default(); gfx_invalidate_screen(); context_trigger_resize(); + context_update_cursor_scale(); console_execute_silent("get window_scale"); } else if (strcmp(argv[0], "window_limit") == 0 && invalidArguments(&invalidArgs, int_valid[0])) { diff --git a/src/openrct2/ui/DummyUiContext.cpp b/src/openrct2/ui/DummyUiContext.cpp index 53ded40df2..7bab40d58b 100644 --- a/src/openrct2/ui/DummyUiContext.cpp +++ b/src/openrct2/ui/DummyUiContext.cpp @@ -54,6 +54,7 @@ namespace OpenRCT2 { namespace Ui const CursorState * GetCursorState() override { return nullptr; } CURSOR_ID GetCursor() override { return CURSOR_ARROW; } void SetCursor(CURSOR_ID cursor) override { } + void SetCursorScale(uint8 scale) override { } void SetCursorVisible(bool value) override { } void GetCursorPosition(sint32 * x, sint32 * y) override { } void SetCursorPosition(sint32 x, sint32 y) override { } diff --git a/src/openrct2/ui/UiContext.h b/src/openrct2/ui/UiContext.h index 6169a66812..f982c758b0 100644 --- a/src/openrct2/ui/UiContext.h +++ b/src/openrct2/ui/UiContext.h @@ -117,6 +117,7 @@ namespace OpenRCT2 virtual const CursorState * GetCursorState() abstract; virtual CURSOR_ID GetCursor() abstract; virtual void SetCursor(CURSOR_ID cursor) abstract; + virtual void SetCursorScale(uint8 scale) abstract; virtual void SetCursorVisible(bool value) abstract; virtual void GetCursorPosition(sint32 * x, sint32 * y) abstract; virtual void SetCursorPosition(sint32 x, sint32 y) abstract;