- restaurades les paletes amb la ordenacio original

- afegida opció de reordenar les paletes automaticament per luminositat o paregut a la paleta d'spectrum
This commit is contained in:
2026-04-02 07:59:30 +02:00
parent 3bd13b72cd
commit 015a9cc4e1
32 changed files with 595 additions and 354 deletions

View File

@@ -80,7 +80,9 @@ categories:
- keyword: PALETTE - keyword: PALETTE
handler: cmd_palette handler: cmd_palette
description: "Color palette (F5/F6)" description: "Color palette (F5/F6)"
usage: "PALETTE [NEXT|PREV|<name>]" usage: "PALETTE [NEXT|PREV|SORT [ORIGINAL|LUMINANCE|SPECTRUM]|DEFAULT|<name>]"
completions:
PALETTE SORT: [ORIGINAL, LUMINANCE, SPECTRUM]
dynamic_completions: true dynamic_completions: true
- name: AUDIO - name: AUDIO

View File

@@ -116,6 +116,7 @@ ui:
supersampling_enabled: "SUPERMOSTREIG ACTIVAT" supersampling_enabled: "SUPERMOSTREIG ACTIVAT"
supersampling_disabled: "SUPERMOSTREIG DESACTIVAT" supersampling_disabled: "SUPERMOSTREIG DESACTIVAT"
palette: "PALETA" palette: "PALETA"
palette_sort: "ORDENACIÓ PALETA"
integer_scale_enabled: "ESCALAT SENCER ACTIVAT" integer_scale_enabled: "ESCALAT SENCER ACTIVAT"
integer_scale_disabled: "ESCALAT SENCER DESACTIVAT" integer_scale_disabled: "ESCALAT SENCER DESACTIVAT"
vsync_enabled: "V-SYNC ACTIVAT" vsync_enabled: "V-SYNC ACTIVAT"

View File

@@ -116,6 +116,7 @@ ui:
supersampling_enabled: "SUPERSAMPLING ON" supersampling_enabled: "SUPERSAMPLING ON"
supersampling_disabled: "SUPERSAMPLING OFF" supersampling_disabled: "SUPERSAMPLING OFF"
palette: "PALETTE" palette: "PALETTE"
palette_sort: "PALETTE SORT"
integer_scale_enabled: "INTEGER SCALE ENABLED" integer_scale_enabled: "INTEGER SCALE ENABLED"
integer_scale_disabled: "INTEGER SCALE DISABLED" integer_scale_disabled: "INTEGER SCALE DISABLED"
vsync_enabled: "V-SYNC ENABLED" vsync_enabled: "V-SYNC ENABLED"

View File

@@ -1,19 +1,19 @@
JASC-PAL JASC-PAL
0100 0100
16 16
52 49 40 52 49 40
80 73 57 80 73 57
92 104 82 92 104 82
108 116 76 108 116 76
125 130 73 125 130 73
163 158 85 163 158 85
202 181 103 202 181 103
119 63 53 119 63 53
132 86 64 132 86 64
160 119 84 160 119 84
188 153 120 188 153 120
214 193 157 214 193 157
234 220 193 234 220 193
247 240 221 247 240 221
255 251 237 255 251 237
255 255 255 255 255 255

View File

@@ -1,19 +1,19 @@
JASC-PAL JASC-PAL
0100 0100
16 16
32 32 32 32 32 32
45 33 30 45 33 30
69 41 35 69 41 35
109 61 41 109 61 41
177 107 74 177 107 74
232 159 110 232 159 110
232 190 130 232 190 130
93 117 87 93 117 87
142 146 87 142 146 87
112 123 136 112 123 136
138 167 172 138 167 172
229 93 77 229 93 77
241 134 108 241 134 108
210 103 48 210 103 48
222 154 40 222 154 40
232 216 165 232 216 165

View File

@@ -1,19 +1,19 @@
JASC-PAL JASC-PAL
0100 0100
16 16
67 0 103 22 23 26
22 23 26 127 6 34
127 6 34 214 36 17
0 40 89 255 132 38
148 33 106 255 209 0
35 73 117 250 253 255
214 36 17 255 128 164
255 38 116 255 38 116
0 120 153 148 33 106
255 132 38 67 0 103
255 128 164 35 73 117
104 174 212 104 174 212
16 210 117 191 255 60
255 209 0 16 210 117
191 255 60 0 120 153
250 253 255 0 40 89

View File

@@ -1,19 +1,19 @@
JASC-PAL JASC-PAL
0100 0100
16 16
0 0 0 0 0 0
0 20 24 0 20 24
0 32 36 0 32 36
0 44 56 0 44 56
20 52 68 20 52 68
68 52 68 68 52 68
88 60 72 88 60 72
108 76 68 108 76 68
128 96 88 128 96 88
108 112 108 108 112 108
136 128 120 136 128 120
164 148 132 164 148 132
196 172 156 196 172 156
216 176 168 216 176 168
236 212 208 236 212 208
252 252 252 252 252 252

View File

@@ -1,19 +1,19 @@
JASC-PAL JASC-PAL
0100 0100
16 16
31 32 37 226 217 228
46 51 77 108 154 154
76 82 116 82 103 93
124 143 178 55 64 59
128 77 83 243 200 147
191 125 133 229 152 125
114 51 76 203 94 92
192 165 169 114 51 76
82 103 93 192 165 169
108 154 154 191 125 133
108 154 154 128 77 83
226 217 228 64 48 56
243 200 147 124 143 178
229 152 125 76 82 116
192 165 169 46 51 77
226 217 228 31 32 37

View File

@@ -1,19 +1,19 @@
JASC-PAL JASC-PAL
0100 0100
16 16
57 52 87 255 255 255
106 55 113 109 247 193
201 36 100 17 173 193
203 77 104 96 108 129
96 108 129 57 52 87
30 136 117 30 136 117
17 173 193 91 179 97
155 156 130 161 229 90
91 179 97 247 228 118
249 146 82 249 146 82
244 140 182 203 77 104
247 182 158 106 55 113
161 229 90 201 36 100
109 247 193 244 140 182
247 228 118 247 182 158
255 255 255 155 156 130

View File

@@ -1,19 +1,19 @@
JASC-PAL JASC-PAL
0100 0100
16 16
75 61 68 209 177 135
77 69 57 199 123 88
87 72 82 174 93 64
121 68 74 121 68 74
75 114 110 75 61 68
174 93 64 186 145 88
119 116 59 146 116 65
146 116 65 77 69 57
132 120 117 119 116 59
199 123 88 179 165 85
186 145 88 210 201 165
171 155 142 140 171 161
179 165 85 75 114 110
140 171 161 87 72 82
209 177 135 132 120 117
210 201 165 171 155 142

View File

@@ -1,19 +1,19 @@
JASC-PAL JASC-PAL
0100 0100
16 16
31 14 28 140 143 174
62 33 55 88 69 99
23 67 75 62 33 55
157 48 59 154 99 72
112 55 127 215 155 125
88 69 99 245 237 186
154 99 72 192 199 65
100 125 52 100 125 52
52 133 157 228 148 58
210 100 113 157 48 59
140 143 174 210 100 113
228 148 58 112 55 127
215 155 125 126 196 193
126 196 193 52 133 157
192 199 65 23 67 75
245 237 186 31 14 28

View File

@@ -1,19 +1,19 @@
JASC-PAL JASC-PAL
0100 0100
16 16
0 0 0 0 0 0
29 43 83 29 43 83
41 173 255 126 37 83
131 118 156 0 135 81
255 0 77 171 82 54
171 82 54 95 87 79
255 119 168 194 195 199
194 195 199 255 241 232
0 228 54 255 0 77
0 135 81 255 163 0
95 87 79 255 236 39
255 241 232 0 228 54
255 236 39 41 173 255
255 163 0 131 118 156
255 204 170 255 119 168
126 37 83 255 204 170

View File

@@ -1,19 +1,19 @@
JASC-PAL JASC-PAL
0100 0100
16 16
10 8 25 10 8 25
49 50 67 49 50 67
69 59 70 69 59 70
87 84 117 87 84 117
130 105 128 130 105 128
164 111 114 164 111 114
185 115 113 185 115 113
205 95 105 205 95 105
229 76 81 229 76 81
201 55 73 201 55 73
144 161 168 144 161 168
140 147 137 140 147 137
195 150 145 195 150 145
236 151 134 236 151 134
235 171 145 235 171 145
219 182 167 219 182 167

View File

@@ -1,19 +1,19 @@
JASC-PAL JASC-PAL
0100 0100
16 16
17 17 37 17 17 37
36 34 114 82 75 109
52 112 190 176 201 196
150 58 191 255 252 241
255 94 57 36 34 114
159 32 98 52 112 190
255 105 246 159 32 98
176 201 196 255 94 57
44 126 75 150 58 191
160 195 95 255 105 246
67 152 196 44 126 75
147 255 229 160 195 95
210 133 55 67 152 196
254 245 107 147 255 229
255 252 241 210 133 55
82 75 109 254 245 107

View File

@@ -1,19 +1,19 @@
JASC-PAL JASC-PAL
0100 0100
16 16
15 11 56 15 11 56
122 87 22 97 106 130
40 19 160 173 180 183
74 107 255 249 255 236
160 35 17 40 19 160
237 23 95 74 107 255
238 20 181 160 35 17
115 16 147 237 23 95
39 139 97 115 16 147
157 255 38 238 20 181
71 233 223 39 139 97
27 105 167 157 255 38
247 229 77 27 105 167
173 180 183 71 233 223
249 255 236 122 87 22
97 106 130 247 229 77

View File

@@ -1,19 +1,19 @@
JASC-PAL JASC-PAL
0100 0100
16 16
0 3 60 0 3 60
0 82 96 0 82 96
0 56 132 0 157 74
42 46 121 10 255 82
255 0 78 0 56 132
177 5 133 0 138 197
172 41 206 0 247 255
255 92 255 255 92 255
10 255 82 172 41 206
0 157 74 96 0 136
0 247 255 177 5 133
0 138 197 255 0 78
78 110 168 42 46 121
255 255 255 78 110 168
173 212 250 173 212 250
96 0 136 255 255 255

View File

@@ -1,19 +1,19 @@
JASC-PAL JASC-PAL
0100 0100
16 16
23 14 25 33 59 37
47 33 59 58 96 74
59 33 55 79 119 84
33 59 37 161 159 124
67 58 96 119 116 79
96 59 58 119 92 79
79 82 119 96 59 58
58 96 74 59 33 55
119 92 79 23 14 25
79 119 84 47 33 59
101 115 140 67 58 96
119 116 79 79 82 119
124 148 161 101 115 140
161 159 124 124 148 161
160 185 186 160 185 186
192 209 204 192 209 204

View File

@@ -1,19 +1,19 @@
JASC-PAL JASC-PAL
0100 0100
16 16
26 28 44 26 28 44
51 60 87 93 39 93
59 93 201 177 62 83
41 54 111 239 125 87
177 62 83 255 205 117
239 125 87 167 240 112
93 39 93 56 183 100
148 176 194 37 113 121
56 183 100 41 54 111
37 113 121 59 93 201
65 166 246 65 166 246
115 239 247 115 239 247
255 205 117 244 244 244
167 240 112 148 176 194
244 244 244 86 108 134
86 108 134 51 60 87

View File

@@ -1,19 +1,19 @@
JASC-PAL JASC-PAL
0100 0100
16 16
40 40 46 40 40 46
108 86 113 108 86 113
217 200 191 217 200 191
249 130 132 249 130 132
176 169 228 176 169 228
172 204 228 172 204 228
179 227 218 179 227 218
254 170 228 254 170 228
135 168 137 135 168 137
176 235 147 176 235 147
233 245 157 233 245 157
255 230 198 255 230 198
222 163 139 222 163 139
255 195 132 255 195 132
255 247 160 255 247 160
255 247 228 255 247 228

View File

@@ -1,19 +1,19 @@
JASC-PAL JASC-PAL
0100 0100
16 16
0 0 0 0 0 0
50 1 50 50 1 50
50 50 171 84 1 103
35 103 239 118 50 118
254 1 69 50 50 171
119 70 2 35 103 239
118 50 118 152 186 220
239 152 152 253 253 253
1 137 84 1 186 152
1 186 152 1 137 84
152 186 220 254 239 69
253 253 253 119 70 2
254 239 69 186 118 84
186 118 84 254 1 69
254 205 205 239 152 152
84 1 103 254 205 205

View File

@@ -127,6 +127,11 @@ namespace GlobalInputs {
Notifier::get()->show({Locale::get()->get("ui.palette") + " " + toUpper(Screen::get()->getPalettePrettyName())}); // NOLINT(readability-static-accessed-through-instance) Notifier::get()->show({Locale::get()->get("ui.palette") + " " + toUpper(Screen::get()->getPalettePrettyName())}); // NOLINT(readability-static-accessed-through-instance)
} }
void handleNextPaletteSortMode() {
Screen::get()->nextPaletteSortMode();
Notifier::get()->show({Locale::get()->get("ui.palette_sort") + " " + toUpper(Screen::get()->getPaletteSortModeName())}); // NOLINT(readability-static-accessed-through-instance)
}
void handleToggleIntegerScale() { void handleToggleIntegerScale() {
Screen::get()->toggleIntegerScale(); Screen::get()->toggleIntegerScale();
Screen::get()->setVideoMode(Options::video.fullscreen); Screen::get()->setVideoMode(Options::video.fullscreen);
@@ -172,10 +177,13 @@ namespace GlobalInputs {
} }
} }
if (Input::get()->checkAction(InputAction::NEXT_PALETTE, Input::DO_NOT_ALLOW_REPEAT)) { if (Input::get()->checkAction(InputAction::NEXT_PALETTE, Input::DO_NOT_ALLOW_REPEAT)) {
return InputAction::NEXT_PALETTE; if ((SDL_GetModState() & SDL_KMOD_CTRL) != 0U) {
return InputAction::PREVIOUS_PALETTE; // Ctrl+F5
}
return InputAction::NEXT_PALETTE; // F5
} }
if (Input::get()->checkAction(InputAction::PREVIOUS_PALETTE, Input::DO_NOT_ALLOW_REPEAT)) { if (Input::get()->checkAction(InputAction::NEXT_PALETTE_SORT, Input::DO_NOT_ALLOW_REPEAT)) {
return InputAction::PREVIOUS_PALETTE; return InputAction::NEXT_PALETTE_SORT; // F6
} }
if (Input::get()->checkAction(InputAction::TOGGLE_INTEGER_SCALE, Input::DO_NOT_ALLOW_REPEAT)) { if (Input::get()->checkAction(InputAction::TOGGLE_INTEGER_SCALE, Input::DO_NOT_ALLOW_REPEAT)) {
return InputAction::TOGGLE_INTEGER_SCALE; return InputAction::TOGGLE_INTEGER_SCALE;
@@ -273,6 +281,10 @@ namespace GlobalInputs {
handlePreviousPalette(); handlePreviousPalette();
break; break;
case InputAction::NEXT_PALETTE_SORT:
handleNextPaletteSortMode();
break;
case InputAction::TOGGLE_INTEGER_SCALE: case InputAction::TOGGLE_INTEGER_SCALE:
handleToggleIntegerScale(); handleToggleIntegerScale();
break; break;

View File

@@ -45,7 +45,7 @@ Input::Input(std::string game_controller_db_path)
{Action::TOGGLE_FULLSCREEN, KeyState{.scancode = SDL_SCANCODE_F3}}, {Action::TOGGLE_FULLSCREEN, KeyState{.scancode = SDL_SCANCODE_F3}},
{Action::TOGGLE_SHADER, KeyState{.scancode = SDL_SCANCODE_F4}}, {Action::TOGGLE_SHADER, KeyState{.scancode = SDL_SCANCODE_F4}},
{Action::NEXT_PALETTE, KeyState{.scancode = SDL_SCANCODE_F5}}, {Action::NEXT_PALETTE, KeyState{.scancode = SDL_SCANCODE_F5}},
{Action::PREVIOUS_PALETTE, KeyState{.scancode = SDL_SCANCODE_F6}}, {Action::NEXT_PALETTE_SORT, KeyState{.scancode = SDL_SCANCODE_F6}},
{Action::TOGGLE_INTEGER_SCALE, KeyState{.scancode = SDL_SCANCODE_F7}}, {Action::TOGGLE_INTEGER_SCALE, KeyState{.scancode = SDL_SCANCODE_F7}},
{Action::TOGGLE_IN_GAME_MUSIC, KeyState{.scancode = SDL_SCANCODE_F8}}, {Action::TOGGLE_IN_GAME_MUSIC, KeyState{.scancode = SDL_SCANCODE_F8}},
{Action::TOGGLE_BORDER, KeyState{.scancode = SDL_SCANCODE_F9}}, {Action::TOGGLE_BORDER, KeyState{.scancode = SDL_SCANCODE_F9}},

View File

@@ -20,6 +20,7 @@ const std::unordered_map<InputAction, std::string> ACTION_TO_STRING = {
{InputAction::TOGGLE_IN_GAME_MUSIC, "TOGGLE_MUSIC"}, {InputAction::TOGGLE_IN_GAME_MUSIC, "TOGGLE_MUSIC"},
{InputAction::NEXT_PALETTE, "NEXT_PALETTE"}, {InputAction::NEXT_PALETTE, "NEXT_PALETTE"},
{InputAction::PREVIOUS_PALETTE, "PREVIOUS_PALETTE"}, {InputAction::PREVIOUS_PALETTE, "PREVIOUS_PALETTE"},
{InputAction::NEXT_PALETTE_SORT, "NEXT_PALETTE_SORT"},
{InputAction::TOGGLE_SHADER, "TOGGLE_POSTFX"}, {InputAction::TOGGLE_SHADER, "TOGGLE_POSTFX"},
{InputAction::NEXT_SHADER_PRESET, "NEXT_POSTFX_PRESET"}, {InputAction::NEXT_SHADER_PRESET, "NEXT_POSTFX_PRESET"},
{InputAction::TOGGLE_INFO, "TOGGLE_DEBUG"}, {InputAction::TOGGLE_INFO, "TOGGLE_DEBUG"},
@@ -42,6 +43,7 @@ const std::unordered_map<std::string, InputAction> STRING_TO_ACTION = {
{"TOGGLE_MUSIC", InputAction::TOGGLE_IN_GAME_MUSIC}, {"TOGGLE_MUSIC", InputAction::TOGGLE_IN_GAME_MUSIC},
{"NEXT_PALETTE", InputAction::NEXT_PALETTE}, {"NEXT_PALETTE", InputAction::NEXT_PALETTE},
{"PREVIOUS_PALETTE", InputAction::PREVIOUS_PALETTE}, {"PREVIOUS_PALETTE", InputAction::PREVIOUS_PALETTE},
{"NEXT_PALETTE_SORT", InputAction::NEXT_PALETTE_SORT},
{"TOGGLE_POSTFX", InputAction::TOGGLE_SHADER}, {"TOGGLE_POSTFX", InputAction::TOGGLE_SHADER},
{"NEXT_POSTFX_PRESET", InputAction::NEXT_SHADER_PRESET}, {"NEXT_POSTFX_PRESET", InputAction::NEXT_SHADER_PRESET},
{"TOGGLE_DEBUG", InputAction::TOGGLE_INFO}, {"TOGGLE_DEBUG", InputAction::TOGGLE_INFO},

View File

@@ -31,6 +31,7 @@ enum class InputAction : int { // Acciones de entrada posibles en el juego
TOGGLE_IN_GAME_MUSIC, TOGGLE_IN_GAME_MUSIC,
NEXT_PALETTE, NEXT_PALETTE,
PREVIOUS_PALETTE, PREVIOUS_PALETTE,
NEXT_PALETTE_SORT,
TOGGLE_INFO, TOGGLE_INFO,
TOGGLE_CONSOLE, TOGGLE_CONSOLE,

View File

@@ -2,7 +2,9 @@
#include <algorithm> #include <algorithm>
#include <cctype> #include <cctype>
#include <cmath>
#include <string> #include <string>
#include <vector>
#include "core/rendering/surface.hpp" #include "core/rendering/surface.hpp"
#include "core/resources/resource_cache.hpp" #include "core/resources/resource_cache.hpp"
@@ -10,13 +12,136 @@
#include "game/options.hpp" #include "game/options.hpp"
#include "utils/utils.hpp" #include "utils/utils.hpp"
// ── Conversión string ↔ PaletteSortMode ──────────────────────────────────────
auto sortModeFromString(const std::string& str) -> PaletteSortMode {
const std::string lower = toLower(str);
if (lower == "luminance") { return PaletteSortMode::LUMINANCE; }
if (lower == "spectrum") { return PaletteSortMode::SPECTRUM; }
return PaletteSortMode::ORIGINAL;
}
auto sortModeToString(PaletteSortMode mode) -> std::string {
switch (mode) {
case PaletteSortMode::LUMINANCE:
return "luminance";
case PaletteSortMode::SPECTRUM:
return "spectrum";
default:
return "original";
}
}
// ── Paleta de referencia ZX Spectrum (16 colores ARGB) ───────────────────────
namespace {
// Helpers para extraer componentes RGB de un color ARGB (0xAARRGGBB)
constexpr auto redOf(Uint32 c) -> int { return static_cast<int>((c >> 16) & 0xFF); }
constexpr auto greenOf(Uint32 c) -> int { return static_cast<int>((c >> 8) & 0xFF); }
constexpr auto blueOf(Uint32 c) -> int { return static_cast<int>(c & 0xFF); }
constexpr auto makeARGB(int r, int g, int b) -> Uint32 {
return (0xFFU << 24) | (static_cast<Uint32>(r) << 16) | (static_cast<Uint32>(g) << 8) | static_cast<Uint32>(b);
}
// Paleta ZX Spectrum de referencia (misma que en tools/sort_palette/sort_palette.py)
constexpr std::array<Uint32, 16> SPECTRUM_REFERENCE = {
makeARGB(0, 0, 0),
makeARGB(0, 0, 0),
makeARGB(0, 0, 216),
makeARGB(0, 0, 255),
makeARGB(216, 0, 0),
makeARGB(255, 0, 0),
makeARGB(216, 0, 216),
makeARGB(255, 0, 255),
makeARGB(0, 216, 0),
makeARGB(0, 255, 0),
makeARGB(0, 216, 216),
makeARGB(0, 255, 255),
makeARGB(216, 216, 0),
makeARGB(255, 255, 0),
makeARGB(216, 216, 216),
makeARGB(255, 255, 255),
};
// Luminancia percibida (ITU-R BT.709)
auto luminance(Uint32 color) -> double {
return 0.2126 * redOf(color) + 0.7152 * greenOf(color) + 0.0722 * blueOf(color);
}
// Distancia euclídea al cuadrado en espacio RGB (no necesita sqrt para comparar)
auto rgbDistanceSq(Uint32 a, Uint32 b) -> int {
const int dr = redOf(a) - redOf(b);
const int dg = greenOf(a) - greenOf(b);
const int db = blueOf(a) - blueOf(b);
return dr * dr + dg * dg + db * db;
}
// Cuenta los colores activos en la paleta (los que tienen alpha != 0)
auto countActiveColors(const Palette& palette) -> size_t {
size_t count = 0;
for (const auto& c : palette) {
if (c == 0) { break; }
++count;
}
return count;
}
// Ordenar por luminancia
auto sortByLuminance(const Palette& palette) -> Palette {
const size_t n = countActiveColors(palette);
std::vector<Uint32> colors(palette.begin(), palette.begin() + static_cast<ptrdiff_t>(n));
std::sort(colors.begin(), colors.end(), [](Uint32 a, Uint32 b) {
return luminance(a) < luminance(b);
});
Palette result{};
result.fill(0);
std::copy(colors.begin(), colors.end(), result.begin());
return result;
}
// Ordenar por similitud con la paleta ZX Spectrum (greedy matching)
auto sortBySpectrum(const Palette& palette) -> Palette {
const size_t n = countActiveColors(palette);
std::vector<Uint32> available(palette.begin(), palette.begin() + static_cast<ptrdiff_t>(n));
std::vector<Uint32> result;
result.reserve(n);
// Para cada color de referencia del Spectrum, buscar el más cercano disponible
const size_t refs = std::min(n, SPECTRUM_REFERENCE.size());
for (size_t i = 0; i < refs && !available.empty(); ++i) {
const Uint32 ref = SPECTRUM_REFERENCE[i];
auto best = std::min_element(available.begin(), available.end(), [ref](Uint32 a, Uint32 b) {
return rgbDistanceSq(a, ref) < rgbDistanceSq(b, ref);
});
result.push_back(*best);
available.erase(best);
}
// Si quedan colores sin asignar, añadirlos al final
for (const auto& c : available) {
result.push_back(c);
}
Palette out{};
out.fill(0);
std::copy(result.begin(), result.end(), out.begin());
return out;
}
} // namespace
// ── PaletteManager ───────────────────────────────────────────────────────────
PaletteManager::PaletteManager( PaletteManager::PaletteManager(
std::vector<std::string> raw_paths, std::vector<std::string> raw_paths,
const std::string& initial_name, const std::string& initial_name,
PaletteSortMode initial_sort_mode,
std::shared_ptr<Surface> game_surface, std::shared_ptr<Surface> game_surface,
std::shared_ptr<Surface> border_surface, std::shared_ptr<Surface> border_surface,
OnChangeCallback on_change) OnChangeCallback on_change)
: palettes_(std::move(raw_paths)), : palettes_(std::move(raw_paths)),
sort_mode_(initial_sort_mode),
game_surface_(std::move(game_surface)), game_surface_(std::move(game_surface)),
border_surface_(std::move(border_surface)), border_surface_(std::move(border_surface)),
on_change_(std::move(on_change)) { on_change_(std::move(on_change)) {
@@ -24,7 +149,7 @@ PaletteManager::PaletteManager(
// Leer y aplicar paleta inicial directamente desde el archivo // Leer y aplicar paleta inicial directamente desde el archivo
// (Resource::Cache aún no está disponible en este punto del ciclo de vida) // (Resource::Cache aún no está disponible en este punto del ciclo de vida)
const auto initial_palette = readPalFile(palettes_.at(current_)); const auto initial_palette = sortPalette(readPalFile(palettes_.at(current_)), sort_mode_);
game_surface_->setPalette(initial_palette); game_surface_->setPalette(initial_palette);
border_surface_->setPalette(initial_palette); border_surface_->setPalette(initial_palette);
@@ -83,9 +208,31 @@ auto PaletteManager::getPrettyName() const -> std::string {
return name; return name;
} }
void PaletteManager::nextSortMode() {
sort_mode_ = static_cast<PaletteSortMode>((static_cast<int>(sort_mode_) + 1) % static_cast<int>(PaletteSortMode::COUNT));
Options::video.palette_sort = sortModeToString(sort_mode_);
apply();
}
void PaletteManager::setSortMode(PaletteSortMode mode) {
sort_mode_ = mode;
Options::video.palette_sort = sortModeToString(sort_mode_);
apply();
}
auto PaletteManager::getSortMode() const -> PaletteSortMode {
return sort_mode_;
}
auto PaletteManager::getSortModeName() const -> std::string {
return sortModeToString(sort_mode_);
}
void PaletteManager::apply() { void PaletteManager::apply() {
game_surface_->loadPalette(Resource::Cache::get()->getPalette(palettes_.at(current_))); Palette raw = Resource::Cache::get()->getPalette(palettes_.at(current_));
border_surface_->loadPalette(Resource::Cache::get()->getPalette(palettes_.at(current_))); Palette sorted = sortPalette(raw, sort_mode_);
game_surface_->loadPalette(sorted);
border_surface_->loadPalette(sorted);
Options::video.palette = getCurrentName(); Options::video.palette = getCurrentName();
@@ -116,3 +263,14 @@ void PaletteManager::processPathList() {
palette = getFileName(palette); palette = getFileName(palette);
} }
} }
auto PaletteManager::sortPalette(const Palette& palette, PaletteSortMode mode) -> Palette {
switch (mode) {
case PaletteSortMode::LUMINANCE:
return sortByLuminance(palette);
case PaletteSortMode::SPECTRUM:
return sortBySpectrum(palette);
default:
return palette;
}
}

View File

@@ -1,12 +1,30 @@
#pragma once #pragma once
#include <SDL3/SDL.h>
#include <array>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
// Alias de paleta (igual que en surface.hpp; evita incluir todo el header)
using Palette = std::array<Uint32, 256>;
class Surface; class Surface;
// Modo de ordenación de paletas
enum class PaletteSortMode : int {
ORIGINAL = 0, // Paleta tal cual viene del fichero
LUMINANCE = 1, // Ordenada por luminancia percibida
SPECTRUM = 2, // Reordenada para imitar la paleta ZX Spectrum
COUNT = 3
};
// Conversión string ↔ PaletteSortMode
auto sortModeFromString(const std::string& str) -> PaletteSortMode;
auto sortModeToString(PaletteSortMode mode) -> std::string;
class PaletteManager { class PaletteManager {
public: public:
using OnChangeCallback = std::function<void()>; using OnChangeCallback = std::function<void()>;
@@ -14,6 +32,7 @@ class PaletteManager {
PaletteManager( PaletteManager(
std::vector<std::string> raw_paths, std::vector<std::string> raw_paths,
const std::string& initial_name, const std::string& initial_name,
PaletteSortMode initial_sort_mode,
std::shared_ptr<Surface> game_surface, std::shared_ptr<Surface> game_surface,
std::shared_ptr<Surface> border_surface, std::shared_ptr<Surface> border_surface,
OnChangeCallback on_change = nullptr); OnChangeCallback on_change = nullptr);
@@ -25,13 +44,20 @@ class PaletteManager {
[[nodiscard]] auto getCurrentName() const -> std::string; // Nombre de la paleta actual (minúsculas, sin .pal) [[nodiscard]] auto getCurrentName() const -> std::string; // Nombre de la paleta actual (minúsculas, sin .pal)
[[nodiscard]] auto getPrettyName() const -> std::string; // Nombre actual con guiones sustituidos por espacios [[nodiscard]] auto getPrettyName() const -> std::string; // Nombre actual con guiones sustituidos por espacios
void nextSortMode(); // Cicla al siguiente modo de ordenación
void setSortMode(PaletteSortMode mode); // Establece un modo de ordenación concreto
[[nodiscard]] auto getSortMode() const -> PaletteSortMode; // Devuelve el modo de ordenación actual
[[nodiscard]] auto getSortModeName() const -> std::string; // Nombre del modo actual ("ORIGINAL", etc.)
private: private:
void apply(); // Aplica la paleta actual a ambas surfaces void apply(); // Aplica la paleta actual a ambas surfaces
[[nodiscard]] auto findIndex(const std::string& name) const -> size_t; // Localiza paleta por nombre en el vector [[nodiscard]] auto findIndex(const std::string& name) const -> size_t; // Localiza paleta por nombre en el vector
void processPathList(); // Extrae nombres de archivo de las rutas completas void processPathList(); // Extrae nombres de archivo de las rutas completas
static auto sortPalette(const Palette& palette, PaletteSortMode mode) -> Palette; // Reordena una paleta según el modo
std::vector<std::string> palettes_; std::vector<std::string> palettes_;
size_t current_{0}; size_t current_{0};
PaletteSortMode sort_mode_{PaletteSortMode::ORIGINAL};
std::shared_ptr<Surface> game_surface_; std::shared_ptr<Surface> game_surface_;
std::shared_ptr<Surface> border_surface_; std::shared_ptr<Surface> border_surface_;
OnChangeCallback on_change_; OnChangeCallback on_change_;

View File

@@ -85,6 +85,7 @@ Screen::Screen() {
palette_manager_ = std::make_unique<PaletteManager>( palette_manager_ = std::make_unique<PaletteManager>(
Resource::List::get()->getListByType(Resource::List::Type::PALETTE), Resource::List::get()->getListByType(Resource::List::Type::PALETTE),
Options::video.palette, Options::video.palette,
sortModeFromString(Options::video.palette_sort),
game_surface_, game_surface_,
border_surface_, border_surface_,
[this]() { [this]() {
@@ -449,6 +450,9 @@ auto Screen::setPaletteByName(const std::string& name) -> bool { return palette_
// Devuelve los nombres de paletas disponibles (minúsculas, sin extensión .pal) // Devuelve los nombres de paletas disponibles (minúsculas, sin extensión .pal)
auto Screen::getPaletteNames() const -> std::vector<std::string> { return palette_manager_->getNames(); } auto Screen::getPaletteNames() const -> std::vector<std::string> { return palette_manager_->getNames(); }
auto Screen::getPalettePrettyName() const -> std::string { return palette_manager_->getPrettyName(); } auto Screen::getPalettePrettyName() const -> std::string { return palette_manager_->getPrettyName(); }
void Screen::nextPaletteSortMode() { palette_manager_->nextSortMode(); }
void Screen::setPaletteSortMode(PaletteSortMode mode) { palette_manager_->setSortMode(mode); }
auto Screen::getPaletteSortModeName() const -> std::string { return palette_manager_->getSortModeName(); }
// Limpia la game_surface_ // Limpia la game_surface_
void Screen::clearSurface(Uint8 index) { game_surface_->clear(index); } void Screen::clearSurface(Uint8 index) { game_surface_->clear(index); }

View File

@@ -59,6 +59,9 @@ class Screen {
auto setPaletteByName(const std::string& name) -> bool; // Cambia a paleta por nombre; false si no existe auto setPaletteByName(const std::string& name) -> bool; // Cambia a paleta por nombre; false si no existe
[[nodiscard]] auto getPaletteNames() const -> std::vector<std::string>; // Nombres disponibles (minúsculas, sin .pal) [[nodiscard]] auto getPaletteNames() const -> std::vector<std::string>; // Nombres disponibles (minúsculas, sin .pal)
[[nodiscard]] auto getPalettePrettyName() const -> std::string; // Nombre actual con guiones sustituidos por espacios [[nodiscard]] auto getPalettePrettyName() const -> std::string; // Nombre actual con guiones sustituidos por espacios
void nextPaletteSortMode(); // Cicla al siguiente modo de ordenación de paleta
void setPaletteSortMode(PaletteSortMode mode); // Establece modo de ordenación concreto
[[nodiscard]] auto getPaletteSortModeName() const -> std::string; // Nombre del modo de ordenación actual
void toggleShaders(); // Activa/desactiva todos los shaders respetando current_shader void toggleShaders(); // Activa/desactiva todos los shaders respetando current_shader
void toggleSupersampling(); // Activa/desactiva el supersampling global void toggleSupersampling(); // Activa/desactiva el supersampling global
void reloadPostFX(); // Recarga el shader del preset actual sin toggle void reloadPostFX(); // Recarga el shader del preset actual sin toggle

View File

@@ -30,6 +30,7 @@ namespace Defaults::Video {
constexpr bool INTEGER_SCALE = true; // Escalado entero activado por defecto constexpr bool INTEGER_SCALE = true; // Escalado entero activado por defecto
constexpr bool KEEP_ASPECT = true; // Mantener aspecto activado por defecto constexpr bool KEEP_ASPECT = true; // Mantener aspecto activado por defecto
constexpr const char* PALETTE_NAME = "zx-spectrum"; // Paleta por defecto constexpr const char* PALETTE_NAME = "zx-spectrum"; // Paleta por defecto
constexpr const char* PALETTE_SORT = "original"; // Modo de ordenación de paleta por defecto
constexpr bool LINEAR_UPSCALE = false; // Upscale NEAREST por defecto constexpr bool LINEAR_UPSCALE = false; // Upscale NEAREST por defecto
constexpr int DOWNSCALE_ALGO = 1; // Downscale Lanczos2 por defecto constexpr int DOWNSCALE_ALGO = 1; // Downscale Lanczos2 por defecto
constexpr bool GPU_ACCELERATION = true; // Aceleración GPU activada por defecto constexpr bool GPU_ACCELERATION = true; // Aceleración GPU activada por defecto

View File

@@ -288,12 +288,20 @@ namespace Options {
// Helper: carga el campo palette; PaletteManager hará el fallback si el nombre no existe // Helper: carga el campo palette; PaletteManager hará el fallback si el nombre no existe
void loadPaletteFromYaml(const fkyaml::node& vid) { void loadPaletteFromYaml(const fkyaml::node& vid) {
if (!vid.contains("palette")) { return; } if (vid.contains("palette")) {
try { try {
auto palette_str = vid["palette"].get_value<std::string>(); auto palette_str = vid["palette"].get_value<std::string>();
video.palette = toLower(palette_str); video.palette = toLower(palette_str);
} catch (...) { } catch (...) {
video.palette = Defaults::Video::PALETTE_NAME; video.palette = Defaults::Video::PALETTE_NAME;
}
}
if (vid.contains("palette_sort")) {
try {
video.palette_sort = toLower(vid["palette_sort"].get_value<std::string>());
} catch (...) {
video.palette_sort = Defaults::Video::PALETTE_SORT;
}
} }
} }
@@ -748,6 +756,7 @@ namespace Options {
file << " keep_aspect: " << (video.keep_aspect ? "true" : "false") << "\n"; file << " keep_aspect: " << (video.keep_aspect ? "true" : "false") << "\n";
file << " filter: " << filterToString(video.filter) << " # filter: nearest (pixel perfect) | linear (smooth)\n"; file << " filter: " << filterToString(video.filter) << " # filter: nearest (pixel perfect) | linear (smooth)\n";
file << " palette: " << video.palette << "\n"; file << " palette: " << video.palette << "\n";
file << " palette_sort: " << video.palette_sort << "\n";
file << " border:\n"; file << " border:\n";
file << " enabled: " << (video.border.enabled ? "true" : "false") << "\n"; file << " enabled: " << (video.border.enabled ? "true" : "false") << "\n";
file << " width: " << video.border.width << "\n"; file << " width: " << video.border.width << "\n";

View File

@@ -100,17 +100,18 @@ namespace Options {
// Estructura para las opciones de video // Estructura para las opciones de video
struct Video { struct Video {
bool fullscreen{Defaults::Video::FULLSCREEN}; // Contiene el valor del modo de pantalla completa bool fullscreen{Defaults::Video::FULLSCREEN}; // Contiene el valor del modo de pantalla completa
Screen::Filter filter{Defaults::Video::FILTER}; // Filtro usado para el escalado de la imagen Screen::Filter filter{Defaults::Video::FILTER}; // Filtro usado para el escalado de la imagen
bool vertical_sync{Defaults::Video::VERTICAL_SYNC}; // Indica si se quiere usar vsync o no bool vertical_sync{Defaults::Video::VERTICAL_SYNC}; // Indica si se quiere usar vsync o no
bool integer_scale{Defaults::Video::INTEGER_SCALE}; // Indica si el escalado de la imagen ha de ser entero en el modo a pantalla completa bool integer_scale{Defaults::Video::INTEGER_SCALE}; // Indica si el escalado de la imagen ha de ser entero en el modo a pantalla completa
bool keep_aspect{Defaults::Video::KEEP_ASPECT}; // Indica si se ha de mantener la relación de aspecto al poner el modo a pantalla completa bool keep_aspect{Defaults::Video::KEEP_ASPECT}; // Indica si se ha de mantener la relación de aspecto al poner el modo a pantalla completa
std::string palette{Defaults::Video::PALETTE_NAME}; // Paleta de colores a usar en el juego std::string palette{Defaults::Video::PALETTE_NAME}; // Paleta de colores a usar en el juego
std::string info; // Información sobre el modo de vídeo std::string palette_sort{Defaults::Video::PALETTE_SORT}; // Modo de ordenación de la paleta (original/luminance/spectrum)
Border border{}; // Borde de la pantalla std::string info; // Información sobre el modo de vídeo
GPU gpu{}; // Opciones de aceleración GPU Border border{}; // Borde de la pantalla
Supersampling supersampling{}; // Opciones de supersampling GPU gpu{}; // Opciones de aceleración GPU
ShaderConfig shader{}; // Opciones de shader post-procesado Supersampling supersampling{}; // Opciones de supersampling
ShaderConfig shader{}; // Opciones de shader post-procesado
}; };
// Estructura para las opciones de musica // Estructura para las opciones de musica

View File

@@ -333,12 +333,12 @@ static auto cmd_driver(const std::vector<std::string>& args) -> std::string {
return "Driver: " + driver_lower + " (restart)"; return "Driver: " + driver_lower + " (restart)";
} }
// PALETTE NEXT/PREV/<name> // PALETTE NEXT/PREV/SORT/DEFAULT/<name>
static auto cmd_palette(const std::vector<std::string>& args) -> std::string { static auto cmd_palette(const std::vector<std::string>& args) -> std::string {
const auto palName = []() -> std::string { const auto palName = []() -> std::string {
return Screen::get()->getPalettePrettyName(); return Screen::get()->getPalettePrettyName();
}; };
if (args.empty()) { return "usage: palette [next|prev|<name>]"; } if (args.empty()) { return "usage: palette [next|prev|sort [original|luminance|spectrum]|default|<name>]"; }
if (args[0] == "NEXT") { if (args[0] == "NEXT") {
Screen::get()->nextPalette(); Screen::get()->nextPalette();
return "Palette: " + palName(); return "Palette: " + palName();
@@ -347,6 +347,26 @@ static auto cmd_palette(const std::vector<std::string>& args) -> std::string {
Screen::get()->previousPalette(); Screen::get()->previousPalette();
return "Palette: " + palName(); return "Palette: " + palName();
} }
if (args[0] == "DEFAULT") {
Screen::get()->setPaletteByName(Defaults::Video::PALETTE_NAME);
return "Palette: " + palName();
}
if (args[0] == "SORT") {
if (args.size() == 1) {
Screen::get()->nextPaletteSortMode();
return "Palette sort: " + Screen::get()->getPaletteSortModeName();
}
if (args[1] == "ORIGINAL") {
Screen::get()->setPaletteSortMode(PaletteSortMode::ORIGINAL);
} else if (args[1] == "LUMINANCE") {
Screen::get()->setPaletteSortMode(PaletteSortMode::LUMINANCE);
} else if (args[1] == "SPECTRUM") {
Screen::get()->setPaletteSortMode(PaletteSortMode::SPECTRUM);
} else {
return "Unknown sort mode. Use: original, luminance, spectrum";
}
return "Palette sort: " + Screen::get()->getPaletteSortModeName();
}
if (!Screen::get()->setPaletteByName(args[0])) { if (!Screen::get()->setPaletteByName(args[0])) {
std::string arg_lower = args[0]; std::string arg_lower = args[0];
std::ranges::transform(arg_lower, arg_lower.begin(), ::tolower); std::ranges::transform(arg_lower, arg_lower.begin(), ::tolower);
@@ -815,7 +835,7 @@ void CommandRegistry::registerHandlers() {
// Proveedores de completions dinámicas // Proveedores de completions dinámicas
// PALETTE: NEXT, PREV + nombres de paletas disponibles (UPPERCASE) // PALETTE: NEXT, PREV + nombres de paletas disponibles (UPPERCASE)
dynamic_providers_["PALETTE"] = []() -> std::vector<std::string> { dynamic_providers_["PALETTE"] = []() -> std::vector<std::string> {
std::vector<std::string> result = {"NEXT", "PREV"}; std::vector<std::string> result = {"NEXT", "PREV", "SORT", "DEFAULT"};
if (Screen::get() != nullptr) { if (Screen::get() != nullptr) {
for (const auto& name : Screen::get()->getPaletteNames()) { for (const auto& name : Screen::get()->getPaletteNames()) {
result.push_back(toUpper(name)); result.push_back(toUpper(name));