1 /*****************************************************************************
2 * Copyright (c) 2014-2020 OpenRCT2 developers
3 *
4 * For a complete list of all authors, please refer to contributors.md
5 * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
6 *
7 * OpenRCT2 is licensed under the GNU General Public License version 3.
8 *****************************************************************************/
9
10 #include "CursorRepository.h"
11
12 #include <cmath>
13 #include <openrct2/common.h>
14 #include <openrct2/config/Config.h>
15 #include <openrct2/core/Guard.hpp>
16 #include <openrct2/interface/Cursors.h>
17 #include <vector>
18
19 using namespace OpenRCT2::Ui;
20
~CursorRepository()21 CursorRepository::~CursorRepository()
22 {
23 _scaledCursors.clear();
24 _currentCursor = CursorID::Undefined;
25 _currentCursorScale = 1;
26 }
27
LoadCursors()28 void CursorRepository::LoadCursors()
29 {
30 SetCursorScale(static_cast<uint8_t>(round(gConfigGeneral.window_scale)));
31 SetCurrentCursor(CursorID::Arrow);
32 }
33
GetCurrentCursor()34 CursorID CursorRepository::GetCurrentCursor()
35 {
36 return _currentCursor;
37 }
38
SetCurrentCursor(CursorID cursorId)39 void CursorRepository::SetCurrentCursor(CursorID cursorId)
40 {
41 if (_currentCursor != cursorId)
42 {
43 SDL_Cursor* cursor = _scaledCursors.at(_currentCursorScale).getScaledCursor(cursorId);
44 SDL_SetCursor(cursor);
45 _currentCursor = cursorId;
46 }
47 }
48
getBit(const uint8_t data[],size_t x,size_t y,size_t width)49 static bool getBit(const uint8_t data[], size_t x, size_t y, size_t width) noexcept
50 {
51 const size_t position = y * width + x;
52 return (data[position / 8] & (1 << (7 - (x % 8)))) != 0;
53 }
54
setBit(uint8_t data[],size_t x,size_t y,size_t width)55 static void setBit(uint8_t data[], size_t x, size_t y, size_t width) noexcept
56 {
57 size_t position = y * width + x;
58 data[position / 8] |= (1 << (7 - (position % 8)));
59 }
60
drawRect(uint8_t data[],size_t x,size_t y,size_t width,size_t scale)61 static void drawRect(uint8_t data[], size_t x, size_t y, size_t width, size_t scale) noexcept
62 {
63 for (size_t outY = (y * scale); outY < ((1 + y) * scale); outY++)
64 {
65 for (size_t outX = (x * scale); outX < ((1 + x) * scale); outX++)
66 {
67 setBit(data, outX, outY, width * scale);
68 }
69 }
70 }
71
scaleDataArray(const uint8_t data[],size_t width,size_t height,size_t scale)72 static std::vector<uint8_t> scaleDataArray(const uint8_t data[], size_t width, size_t height, size_t scale)
73 {
74 const size_t length = width * height;
75
76 std::vector<uint8_t> res;
77 res.resize(length * scale * scale);
78
79 for (size_t y = 0; y < height * 8; y++)
80 {
81 for (size_t x = 0; x < width; x++)
82 {
83 const bool value = getBit(data, x, y, width);
84 if (!value)
85 continue;
86
87 drawRect(res.data(), x, y, width, scale);
88 }
89 }
90
91 return res;
92 }
93
Create(const CursorData * cursorInfo,uint8_t scale)94 SDL_Cursor* CursorRepository::Create(const CursorData* cursorInfo, uint8_t scale)
95 {
96 const auto integer_scale = static_cast<int>(round(scale));
97
98 auto data = scaleDataArray(cursorInfo->Data, CURSOR_BIT_WIDTH, CURSOR_HEIGHT, static_cast<size_t>(integer_scale));
99 auto mask = scaleDataArray(cursorInfo->Mask, CURSOR_BIT_WIDTH, CURSOR_HEIGHT, static_cast<size_t>(integer_scale));
100
101 auto* cursor = SDL_CreateCursor(
102 data.data(), mask.data(), BASE_CURSOR_WIDTH * integer_scale, BASE_CURSOR_HEIGHT * integer_scale,
103 cursorInfo->HotSpot.X * integer_scale, cursorInfo->HotSpot.Y * integer_scale);
104
105 return cursor;
106 }
107
SetCursorScale(uint8_t cursorScale)108 void CursorRepository::SetCursorScale(uint8_t cursorScale)
109 {
110 if (cursorScale > 0.0)
111 {
112 _currentCursorScale = cursorScale;
113 GenerateScaledCursorSetHolder(_currentCursorScale);
114 }
115 }
116
GenerateScaledCursorSetHolder(uint8_t scale)117 void CursorRepository::GenerateScaledCursorSetHolder(uint8_t scale)
118 {
119 if (_scaledCursors.find(scale) == _scaledCursors.end())
120 {
121 std::function<SDL_Cursor*(CursorID)> cursorGenerator = [this, scale](CursorID cursorId) {
122 switch (cursorId)
123 {
124 // We can't scale the system cursors, but they should be appropriately scaled anyway
125 case CursorID::Arrow:
126 return SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
127 case CursorID::HandPoint:
128 return SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
129 default:
130 return this->Create(GetCursorData(cursorId), scale);
131 }
132 };
133 _scaledCursors.emplace(scale, cursorGenerator);
134 }
135 }
136