tidy-fix automàtic (sense naming)

This commit is contained in:
2026-05-14 18:28:23 +02:00
parent 358e91ea30
commit b7a551c158
81 changed files with 1549 additions and 831 deletions
+114 -72
View File
@@ -1,5 +1,6 @@
#include "core/rendering/menu.hpp"
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <functional>
@@ -72,16 +73,20 @@ namespace Menu {
std::string subtitle; // opcional — si no buit, es dibuixa sota el títol
};
static bool isVisible(const Item& it) { return !it.visible || it.visible(); }
static auto isVisible(const Item& it) -> bool { return !it.visible || it.visible(); }
// Troba el pròxim ítem visible en direcció `dir` (±1) a partir de `from`.
// Si cap és visible retorna `from`.
static int nextVisibleCursor(const Page& p, int from, int dir) {
static auto nextVisibleCursor(const Page& p, int from, int dir) -> int {
const int n = static_cast<int>(p.items.size());
if (n <= 0) return from;
if (n <= 0) {
return from;
}
for (int i = 1; i <= n; ++i) {
int idx = ((from + dir * i) % n + n) % n;
if (isVisible(p.items[idx])) return idx;
if (isVisible(p.items[idx])) {
return idx;
}
}
return from;
}
@@ -97,7 +102,7 @@ namespace Menu {
// --- Transició entre pàgines ---
static constexpr float TRANSITION_SPEED = 5.5F; // ~180 ms
static Page transition_outgoing_{"", {}, 0};
static Page transition_outgoing_{.title = "", .items = {}, .cursor = 0};
static bool transition_active_{false};
static float transition_progress_{1.0F};
static int transition_dir_{+1}; // +1 endavant, -1 enrere
@@ -120,19 +125,19 @@ namespace Menu {
// --- Helpers ---
static std::string yesNo(bool b) { return b ? Locale::get("menu.values.yes") : Locale::get("menu.values.no"); }
static std::string onOff(bool b) { return b ? Locale::get("menu.values.on") : Locale::get("menu.values.off"); }
static auto yesNo(bool b) -> std::string { return b ? Locale::get("menu.values.yes") : Locale::get("menu.values.no"); }
static auto onOff(bool b) -> std::string { return b ? Locale::get("menu.values.on") : Locale::get("menu.values.off"); }
// --- Builders de pàgines ---
static Page buildVideo();
static Page buildAudio();
static Page buildControls();
static Page buildGame();
static Page buildSystem();
static auto buildVideo() -> Page;
static auto buildAudio() -> Page;
static auto buildControls() -> Page;
static auto buildGame() -> Page;
static auto buildSystem() -> Page;
static Page buildRoot() {
Page p{Locale::get("menu.titles.root"), {}, 0};
static auto buildRoot() -> Page {
Page p{.title = Locale::get("menu.titles.root"), .items = {}, .cursor = 0};
p.items.push_back({Locale::get("menu.items.video"), ItemKind::Submenu, nullptr, nullptr, [] { pushPage(buildVideo()); }, nullptr});
p.items.push_back({Locale::get("menu.items.audio"), ItemKind::Submenu, nullptr, nullptr, [] { pushPage(buildAudio()); }, nullptr});
p.items.push_back({Locale::get("menu.items.controls"), ItemKind::Submenu, nullptr, nullptr, [] { pushPage(buildControls()); }, nullptr});
@@ -141,8 +146,8 @@ namespace Menu {
return p;
}
static Page buildVideo() {
Page p{Locale::get("menu.titles.video"), {}, 0};
static auto buildVideo() -> Page {
Page p{.title = Locale::get("menu.titles.video"), .items = {}, .cursor = 0};
// Zoom i fullscreen: sense sentit a WASM (el navegador posseix el canvas)
#ifndef __EMSCRIPTEN__
@@ -150,8 +155,9 @@ namespace Menu {
char buf[16];
std::snprintf(buf, sizeof(buf), "%dX", Screen::get()->getZoom());
return std::string(buf); }, [](int dir) {
if (dir < 0) Screen::get()->decZoom();
else if (dir > 0) Screen::get()->incZoom(); }, nullptr, nullptr});
if (dir < 0) { Screen::get()->decZoom();
} else if (dir > 0) { Screen::get()->incZoom();
} }, nullptr, nullptr});
p.items.push_back({Locale::get("menu.items.screen"), ItemKind::Toggle, [] { return std::string(Screen::get()->isFullscreen() ? Locale::get("menu.values.fullscreen") : Locale::get("menu.values.windowed")); }, [](int) { Screen::get()->toggleFullscreen(); }, nullptr, nullptr, nullptr});
#endif
@@ -185,15 +191,18 @@ namespace Menu {
p.items.push_back({Locale::get("menu.items.shader"), ItemKind::Toggle, [] { return onOff(Options::video.shader_enabled); }, [](int) { Screen::get()->toggleShaders(); }, nullptr, nullptr, nullptr});
p.items.push_back({Locale::get("menu.items.shader_type"), ItemKind::Cycle, [] { return std::string(Screen::get()->getActiveShaderName()); }, [](int dir) {
if (dir < 0) Screen::get()->prevShaderType();
else Screen::get()->nextShaderType(); }, nullptr, nullptr, [] { return Options::video.shader_enabled; }});
if (dir < 0) { Screen::get()->prevShaderType();
} else { Screen::get()->nextShaderType();
} }, nullptr, nullptr, [] { return Options::video.shader_enabled; }});
p.items.push_back({Locale::get("menu.items.preset"), ItemKind::Cycle, [] { return std::string(Screen::get()->getCurrentPresetName()); }, [](int dir) {
if (dir < 0) Screen::get()->prevPreset();
else Screen::get()->nextPreset(); }, nullptr, nullptr, [] { return Options::video.shader_enabled; }});
if (dir < 0) { Screen::get()->prevPreset();
} else { Screen::get()->nextPreset();
} }, nullptr, nullptr, [] { return Options::video.shader_enabled; }});
p.items.push_back({Locale::get("menu.items.supersampling"), ItemKind::Toggle, [] { return onOff(Options::video.supersampling); }, [](int) { Screen::get()->toggleSupersampling(); }, nullptr, nullptr, [] {
if (!Options::video.shader_enabled) return false;
if (!Options::video.shader_enabled) { return false;
}
const char* name = Screen::get()->getActiveShaderName();
return name && std::string(name) == "POSTFX"; }});
#endif
@@ -213,10 +222,10 @@ namespace Menu {
}
// Converteix volum 0..1 a percentatge i ho formata com "50%"
static std::string volPct(float v) {
int pct = static_cast<int>(v * 100.0F + 0.5F);
if (pct < 0) pct = 0;
if (pct > 100) pct = 100;
static auto volPct(float v) -> std::string {
int pct = static_cast<int>((v * 100.0F) + 0.5F);
pct = std::max(pct, 0);
pct = std::min(pct, 100);
char buf[8];
std::snprintf(buf, sizeof(buf), "%d%%", pct);
return std::string(buf);
@@ -225,13 +234,13 @@ namespace Menu {
// Canvi +/- d'un volum en steps de 0.05 (5%) amb clamping
static void stepVolume(float& v, int dir) {
v += (dir >= 0 ? 0.05F : -0.05F);
if (v < 0.0F) v = 0.0F;
if (v > 1.0F) v = 1.0F;
v = std::max(v, 0.0F);
v = std::min(v, 1.0F);
Options::applyAudio();
}
static Page buildControls() {
Page p{Locale::get("menu.titles.controls"), {}, 0};
static auto buildControls() -> Page {
Page p{.title = Locale::get("menu.titles.controls"), .items = {}, .cursor = 0};
p.items.push_back({Locale::get("menu.items.move_up"), ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_game.up});
p.items.push_back({Locale::get("menu.items.move_down"), ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_game.down});
p.items.push_back({Locale::get("menu.items.move_left"), ItemKind::KeyBind, nullptr, nullptr, nullptr, &Options::keys_game.left});
@@ -240,8 +249,8 @@ namespace Menu {
return p;
}
static Page buildAudio() {
Page p{Locale::get("menu.titles.audio"), {}, 0};
static auto buildAudio() -> Page {
Page p{.title = Locale::get("menu.titles.audio"), .items = {}, .cursor = 0};
p.items.push_back({Locale::get("menu.items.master_enable"), ItemKind::Toggle, [] { return onOff(Options::audio.enabled); }, [](int) {
Options::audio.enabled = !Options::audio.enabled;
@@ -264,8 +273,8 @@ namespace Menu {
return p;
}
static Page buildGame() {
Page p{Locale::get("menu.titles.game"), {}, 0};
static auto buildGame() -> Page {
Page p{.title = Locale::get("menu.titles.game"), .items = {}, .cursor = 0};
p.items.push_back({Locale::get("menu.items.use_new_logo"), ItemKind::Toggle, [] { return yesNo(Options::game.use_new_logo); }, [](int) { Options::game.use_new_logo = !Options::game.use_new_logo; }, nullptr});
@@ -276,19 +285,23 @@ namespace Menu {
return p;
}
static Page buildSystem() {
Page p{Locale::get("menu.titles.system"), {}, 0};
static auto buildSystem() -> Page {
Page p{.title = Locale::get("menu.titles.system"), .items = {}, .cursor = 0};
p.subtitle = std::string("v") + Texts::VERSION + " (" + Version::GIT_HASH + ")";
p.items.push_back({Locale::get("menu.items.restart"), ItemKind::Action, nullptr, nullptr, [] {
if (Director::get()) Director::get()->requestRestart();
if (Director::get()) {
Director::get()->requestRestart();
}
},
nullptr,
nullptr});
#ifndef __EMSCRIPTEN__
p.items.push_back({Locale::get("menu.items.exit_game"), ItemKind::Action, nullptr, nullptr, [] {
if (Director::get()) Director::get()->requestQuit();
if (Director::get()) {
Director::get()->requestQuit();
}
},
nullptr,
nullptr});
@@ -307,10 +320,14 @@ namespace Menu {
const Uint8 sb = (src_argb >> 16) & 0xFF;
const Uint8 inv = 255 - sa;
for (int row = y; row < y + h; row++) {
if (row < 0 || row >= SCREEN_H) continue;
if (row < 0 || row >= SCREEN_H) {
continue;
}
for (int col = x; col < x + w; col++) {
if (col < 0 || col >= SCREEN_W) continue;
Uint32* p = &buf[col + row * SCREEN_W];
if (col < 0 || col >= SCREEN_W) {
continue;
}
Uint32* p = &buf[col + (row * SCREEN_W)];
Uint32 dst = *p;
Uint8 dr = dst & 0xFF;
Uint8 dg = (dst >> 8) & 0xFF;
@@ -318,17 +335,21 @@ namespace Menu {
Uint8 r = (sr * sa + dr * inv) / 255;
Uint8 g = (sg * sa + dg * inv) / 255;
Uint8 b = (sb * sa + db * inv) / 255;
*p = 0xFF000000u | (static_cast<Uint32>(b) << 16) | (static_cast<Uint32>(g) << 8) | r;
*p = 0xFF000000U | (static_cast<Uint32>(b) << 16) | (static_cast<Uint32>(g) << 8) | r;
}
}
}
static void fillRect(Uint32* buf, int x, int y, int w, int h, Uint32 color) {
for (int row = y; row < y + h; row++) {
if (row < 0 || row >= SCREEN_H) continue;
if (row < 0 || row >= SCREEN_H) {
continue;
}
for (int col = x; col < x + w; col++) {
if (col < 0 || col >= SCREEN_W) continue;
buf[col + row * SCREEN_W] = color;
if (col < 0 || col >= SCREEN_W) {
continue;
}
buf[col + (row * SCREEN_W)] = color;
}
}
}
@@ -343,12 +364,14 @@ namespace Menu {
// Mida final de la caixa segons el nombre d'items *visibles*.
// body = (N-1) * ITEM_SPACING + charH — així BOTTOM_PAD és el buit real
// sota el text del darrer ítem, no un buit extra per sobre d'un "slot" buit.
static int boxHeight(const Page& page) {
static auto boxHeight(const Page& page) -> int {
int n = 0;
for (const auto& it : page.items) {
if (isVisible(it)) ++n;
if (isVisible(it)) {
++n;
}
}
int body = (n == 0) ? 8 : (n - 1) * ITEM_SPACING + 8;
int body = (n == 0) ? 8 : ((n - 1) * ITEM_SPACING) + 8;
int header = HEADER_H + (page.subtitle.empty() ? 0 : SUBTITLE_H);
return header + body + BOTTOM_PAD;
}
@@ -403,7 +426,9 @@ namespace Menu {
// render() faça decréixer open_anim_ fins a 0. En aquell moment es neteja
// l'estat. Si es crida estant ja tancat o tancant-se, no-op.
void close() {
if (stack_.empty() || closing_) return;
if (stack_.empty() || closing_) {
return;
}
closing_ = true;
capturing_ = nullptr;
transition_active_ = false;
@@ -416,7 +441,9 @@ namespace Menu {
}
void captureKey(SDL_Scancode sc) {
if (!capturing_) return;
if (capturing_ == nullptr) {
return;
}
if (sc == SDL_SCANCODE_ESCAPE) {
// Cancel·la
capturing_ = nullptr;
@@ -427,15 +454,18 @@ namespace Menu {
}
void handleKey(SDL_Scancode sc) {
if (!isOpen()) return;
if (!isOpen()) {
return;
}
Page& page = stack_.back();
if (page.items.empty()) {
// Pàgina buida — només backspace surt
if (sc == SDL_SCANCODE_BACKSPACE) {
if (stack_.size() > 1)
if (stack_.size() > 1) {
popPage();
else
} else {
close();
}
}
return;
}
@@ -467,7 +497,9 @@ namespace Menu {
case SDL_SCANCODE_KP_ENTER:
if (page.items[page.cursor].kind == ItemKind::Submenu ||
page.items[page.cursor].kind == ItemKind::Action) {
if (page.items[page.cursor].enter) page.items[page.cursor].enter();
if (page.items[page.cursor].enter) {
page.items[page.cursor].enter();
}
} else if (page.items[page.cursor].kind == ItemKind::KeyBind) {
capturing_ = page.items[page.cursor].scancode;
} else if (page.items[page.cursor].change) {
@@ -475,10 +507,11 @@ namespace Menu {
}
break;
case SDL_SCANCODE_BACKSPACE:
if (stack_.size() > 1)
if (stack_.size() > 1) {
popPage();
else
} else {
close();
}
break;
default:
break;
@@ -500,7 +533,7 @@ namespace Menu {
static void renderPageContent(Uint32* pixel_data, const Page& page, int box_x, int box_y, int x_offset, int clip_x_min, int clip_x_max, int clip_y_min, int clip_y_max) {
// Títol
int title_w = font_->width(page.title);
int title_x = box_x + (BOX_W - title_w) / 2 + x_offset;
int title_x = box_x + ((BOX_W - title_w) / 2) + x_offset;
font_->drawClipped(pixel_data, title_x, box_y + TITLE_PAD_Y, page.title, TITLE_COLOR, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
// Línia sota el títol (també lliscada) — clippada manualment
@@ -519,26 +552,31 @@ namespace Menu {
int items_y = title_line_y + 4;
if (!page.subtitle.empty()) {
int sub_w = font_->width(page.subtitle.c_str());
int sub_x = box_x + (BOX_W - sub_w) / 2 + x_offset;
int sub_x = box_x + ((BOX_W - sub_w) / 2) + x_offset;
font_->drawClipped(pixel_data, sub_x, items_y, page.subtitle.c_str(), LABEL_COLOR, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
items_y += SUBTITLE_H;
}
// Compta visibles — si cap, dibuixa placeholder (caixa totalment col·lapsada però oberta)
int visible_count = 0;
for (const auto& it : page.items)
if (isVisible(it)) ++visible_count;
for (const auto& it : page.items) {
if (isVisible(it)) {
++visible_count;
}
}
if (visible_count == 0) {
const char* empty_text = Locale::get("menu.values.empty");
int ew = font_->width(empty_text);
font_->drawClipped(pixel_data, box_x + (BOX_W - ew) / 2 + x_offset, items_y + 2, empty_text, EMPTY_COLOR, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
font_->drawClipped(pixel_data, box_x + ((BOX_W - ew) / 2) + x_offset, items_y + 2, empty_text, EMPTY_COLOR, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
return;
}
int y_slot = 0; // índex de fila visible (independent de l'índex real de l'ítem)
for (size_t i = 0; i < page.items.size(); i++) {
const Item& item = page.items[i];
if (!isVisible(item)) continue;
int y = items_y + y_slot * ITEM_SPACING;
if (!isVisible(item)) {
continue;
}
int y = items_y + (y_slot * ITEM_SPACING);
++y_slot;
bool selected = (static_cast<int>(i) == page.cursor);
Uint32 label_color = selected ? CURSOR_COLOR : LABEL_COLOR;
@@ -546,7 +584,7 @@ namespace Menu {
// Action: sense valor a la dreta — centrem el label amb el cursor just a l'esquerra.
if (item.kind == ItemKind::Action) {
int lw = font_->width(item.label);
int lx = box_x + (BOX_W - lw) / 2 + x_offset;
int lx = box_x + ((BOX_W - lw) / 2) + x_offset;
if (selected) {
font_->drawClipped(pixel_data, lx - font_->width("> "), y, ">", CURSOR_COLOR, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
}
@@ -567,8 +605,10 @@ namespace Menu {
font_->drawClipped(pixel_data, box_x + BOX_W - ITEM_PAD_X - aw + x_offset, y, arrow, ac, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
} else if (item.kind == ItemKind::KeyBind) {
bool this_capturing = (capturing_ == item.scancode);
const char* text = this_capturing ? Locale::get("menu.values.press_key") : (item.scancode ? SDL_GetScancodeName(*item.scancode) : "");
if (!text || !*text) text = Locale::get("menu.values.unknown");
const char* text = this_capturing ? Locale::get("menu.values.press_key") : ((item.scancode != nullptr) ? SDL_GetScancodeName(*item.scancode) : "");
if ((text == nullptr) || (*text == 0)) {
text = Locale::get("menu.values.unknown");
}
int tw = font_->width(text);
Uint32 tc = this_capturing ? 0xFF00FFFF : (selected ? CURSOR_COLOR : VALUE_COLOR);
font_->drawClipped(pixel_data, box_x + BOX_W - ITEM_PAD_X - tw + x_offset, y, text, tc, clip_x_min, clip_x_max, clip_y_min, clip_y_max);
@@ -582,7 +622,9 @@ namespace Menu {
}
void render(Uint32* pixel_data) {
if (!isVisible() || !font_ || !pixel_data) return;
if (!isVisible() || !font_ || (pixel_data == nullptr)) {
return;
}
// Delta time
Uint32 now = SDL_GetTicks();
@@ -600,7 +642,7 @@ namespace Menu {
}
} else if (open_anim_ < 1.0F) {
open_anim_ += OPEN_SPEED * dt;
if (open_anim_ > 1.0F) open_anim_ = 1.0F;
open_anim_ = std::min(open_anim_, 1.0F);
}
// Avança transició
@@ -626,7 +668,7 @@ namespace Menu {
animated_h_ = static_cast<float>(current_h);
} else {
float t = HEIGHT_RATE * dt;
if (t > 1.0F) t = 1.0F;
t = std::min(t, 1.0F);
animated_h_ += diff * t;
}
}
@@ -643,12 +685,12 @@ namespace Menu {
// Caixa creix verticalment durant l'obertura
int box_h = static_cast<int>(target_h * eased);
if (box_h < 2) box_h = 2;
box_h = std::max(box_h, 2);
int box_x = (SCREEN_W - BOX_W) / 2;
int box_y = (SCREEN_H - box_h) / 2;
// Fons semi-transparent (alpha escalat per l'animació d'obertura)
Uint8 alpha = static_cast<Uint8>(BG_ALPHA * eased);
auto alpha = static_cast<Uint8>(BG_ALPHA * eased);
blendRect(pixel_data, box_x, box_y, BOX_W, box_h, BG_COLOR, alpha);
// El contingut només apareix quan la caixa és prou gran
+25 -19
View File
@@ -102,16 +102,22 @@ namespace Overlay {
// Pinta un rectangle sòlid dins els límits de la pantalla
static void drawRect(Uint32* pixel_data, int rx, int ry, int rw, int rh, Uint32 color) {
for (int row = ry; row < ry + rh; row++) {
if (row < 0 || row >= SCREEN_H) continue;
if (row < 0 || row >= SCREEN_H) {
continue;
}
for (int col = rx; col < rx + rw; col++) {
if (col < 0 || col >= SCREEN_W) continue;
pixel_data[col + row * SCREEN_W] = color;
if (col < 0 || col >= SCREEN_W) {
continue;
}
pixel_data[col + (row * SCREEN_W)] = color;
}
}
}
void render(Uint32* pixel_data) {
if (!font_ || !pixel_data) return;
if (!font_ || (pixel_data == nullptr)) {
return;
}
// Calcula delta time
Uint32 now = SDL_GetTicks();
@@ -148,7 +154,9 @@ namespace Overlay {
break;
}
if (notif.status == Status::FINISHED) continue;
if (notif.status == Status::FINISHED) {
continue;
}
// Posició segons el tipus
int box_x = 0;
@@ -180,7 +188,7 @@ namespace Overlay {
int line_y = box_y + NOTIF_PADDING_V;
for (const auto& line : notif.lines) {
int line_w = font_->width(line.c_str());
int line_x = box_x + (notif.box_w - line_w) / 2; // centrat dins la caixa
int line_x = box_x + ((notif.box_w - line_w) / 2); // centrat dins la caixa
if (notif.style == NotifStyle::SHADOW) {
font_->draw(pixel_data, line_x + 1, line_y + 1, line.c_str(), notif.accent_color);
} else if (notif.style == NotifStyle::OUTLINE) {
@@ -203,7 +211,7 @@ namespace Overlay {
// Mateix lloc: entra fins a 1
if (info_anim_ < 1.0F) {
info_anim_ += INFO_SLIDE_SPEED * dt;
if (info_anim_ > 1.0F) info_anim_ = 1.0F;
info_anim_ = std::min(info_anim_, 1.0F);
}
} else {
// Canvi: si visible_pos està OFF, commuta directament
@@ -225,10 +233,10 @@ namespace Overlay {
float target = seg.visible ? 1.0F : 0.0F;
if (seg.anim < target) {
seg.anim += SEG_SPEED * dt;
if (seg.anim > target) seg.anim = target;
seg.anim = std::min(seg.anim, target);
} else if (seg.anim > target) {
seg.anim -= SEG_SPEED * dt;
if (seg.anim < target) seg.anim = target;
seg.anim = std::max(seg.anim, target);
}
}
@@ -283,9 +291,7 @@ namespace Overlay {
}
// Elimina les acabades
notifications_.erase(
std::remove_if(notifications_.begin(), notifications_.end(), [](const Notification& n) { return n.status == Status::FINISHED; }),
notifications_.end());
std::erase_if(notifications_, [](const Notification& n) { return n.status == Status::FINISHED; });
// Si la notificació d'ESC ha desaparegut, reseteja l'estat
if (esc_waiting_ && notifications_.empty()) {
@@ -293,7 +299,7 @@ namespace Overlay {
}
// Indicador de pausa persistent (cantó superior dret)
if (Director::get() && Director::get()->isPaused()) {
if ((Director::get() != nullptr) && Director::get()->isPaused()) {
const char* pause_text = Locale::get("notifications.pause");
int w = font_->width(pause_text);
int x = SCREEN_W - w - 4;
@@ -357,9 +363,7 @@ namespace Overlay {
}
// Neteja notificacions finalitzades
notifications_.erase(
std::remove_if(notifications_.begin(), notifications_.end(), [](const Notification& n) { return n.status == Status::FINISHED; }),
notifications_.end());
std::erase_if(notifications_, [](const Notification& n) { return n.status == Status::FINISHED; });
// Menú flotant per damunt de tot (isVisible inclou l'animació de tancament)
if (Menu::isVisible()) {
@@ -392,7 +396,7 @@ namespace Overlay {
int max_w = 0;
for (const auto& line : lines) {
int w = font_->width(line.c_str());
if (w > max_w) max_w = w;
max_w = std::max(w, max_w);
}
notif.box_w = max_w + NOTIF_PADDING_H * 2;
int line_h = font_->charHeight();
@@ -414,7 +418,7 @@ namespace Overlay {
void setRenderInfoSegments(const char* s0, const char* s1, const char* s2, const char* s3, unsigned int mono_mask) {
const char* segs[INFO_SEGMENT_COUNT] = {s0, s1, s2, s3};
for (int i = 0; i < INFO_SEGMENT_COUNT; i++) {
info_segments_[i].mono_digits = (mono_mask >> i) & 1u;
info_segments_[i].mono_digits = (((mono_mask >> i) & 1U) != 0u);
if (segs[i] != nullptr && *segs[i] != '\0') {
info_segments_[i].text = segs[i];
info_segments_[i].visible = true;
@@ -425,7 +429,9 @@ namespace Overlay {
}
void startCredits() {
if (credits_phase_ != CreditsPhase::IDLE) return;
if (credits_phase_ != CreditsPhase::IDLE) {
return;
}
credits_phase_ = CreditsPhase::DELAY;
credits_timer_ = 0.0F;
}
+104 -41
View File
@@ -1,5 +1,6 @@
#include "core/rendering/screen.hpp"
#include <algorithm>
#include <cstdio>
#include <iostream>
@@ -76,15 +77,15 @@ Screen::Screen() {
calculateMaxZoom();
if (zoom_ < 1) zoom_ = 1;
if (zoom_ > max_zoom_) zoom_ = max_zoom_;
zoom_ = std::max(zoom_, 1);
zoom_ = std::min(zoom_, max_zoom_);
// Clamp de la resolució interna a [1, max_zoom_]. Llegir del YAML i
// ajustar aquí és l'únic moment en què es fa — el menú re-clampa cada
// canvi. Si la pantalla és més petita que el valor desat (p.ex. canvi
// de monitor), baixem al màxim suportat.
if (Options::video.internal_resolution < 1) Options::video.internal_resolution = 1;
if (Options::video.internal_resolution > max_zoom_) Options::video.internal_resolution = max_zoom_;
Options::video.internal_resolution = std::max(Options::video.internal_resolution, 1);
Options::video.internal_resolution = std::min(Options::video.internal_resolution, max_zoom_);
int w = GAME_WIDTH * zoom_;
int h = Options::video.aspect_ratio_4_3 ? static_cast<int>(GAME_HEIGHT * 1.2F) * zoom_ : GAME_HEIGHT * zoom_;
@@ -124,15 +125,25 @@ Screen::~Screen() {
if (shader_backend_) {
#ifndef NO_SHADERS
auto* gpu = dynamic_cast<Rendering::SDL3GPUShader*>(shader_backend_.get());
if (gpu) gpu->destroy();
if (gpu != nullptr) {
gpu->destroy();
}
#endif
shader_backend_.reset();
}
if (internal_texture_sdl_) SDL_DestroyTexture(internal_texture_sdl_);
if (texture_) SDL_DestroyTexture(texture_);
if (renderer_) SDL_DestroyRenderer(renderer_);
if (window_) SDL_DestroyWindow(window_);
if (internal_texture_sdl_ != nullptr) {
SDL_DestroyTexture(internal_texture_sdl_);
}
if (texture_ != nullptr) {
SDL_DestroyTexture(texture_);
}
if (renderer_ != nullptr) {
SDL_DestroyRenderer(renderer_);
}
if (window_ != nullptr) {
SDL_DestroyWindow(window_);
}
}
void Screen::initShaders() {
@@ -143,7 +154,9 @@ void Screen::initShaders() {
// curtcircuiten cap al fallback SDL_Renderer.
return;
#else
if (!Options::video.gpu_acceleration) return;
if (!Options::video.gpu_acceleration) {
return;
}
shader_backend_ = std::make_unique<Rendering::SDL3GPUShader>();
@@ -282,19 +295,25 @@ void Screen::toggleFullscreen() {
}
void Screen::incZoom() {
if (fullscreen_ || zoom_ >= max_zoom_) return;
if (fullscreen_ || zoom_ >= max_zoom_) {
return;
}
zoom_++;
adjustWindowSize();
}
void Screen::decZoom() {
if (fullscreen_ || zoom_ <= 1) return;
if (fullscreen_ || zoom_ <= 1) {
return;
}
zoom_--;
adjustWindowSize();
}
void Screen::setZoom(int zoom) {
if (zoom < 1 || zoom > max_zoom_ || fullscreen_) return;
if (zoom < 1 || zoom > max_zoom_ || fullscreen_) {
return;
}
zoom_ = zoom;
adjustWindowSize();
}
@@ -310,9 +329,15 @@ auto Screen::toggleSupersampling() -> bool {
// SS només té sentit amb shaders on i pipeline PostFX (el Lanczos downscale
// i el camí SS s'apliquen al pas de PostFX; CRTPI fa el seu propi
// submostreig intern i no usa aquesta via).
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) return false;
if (!Options::video.shader_enabled) return false;
if (shader_backend_->getActiveShader() != Rendering::ShaderType::POSTFX) return false;
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) {
return false;
}
if (!Options::video.shader_enabled) {
return false;
}
if (shader_backend_->getActiveShader() != Rendering::ShaderType::POSTFX) {
return false;
}
Options::video.supersampling = !Options::video.supersampling;
shader_backend_->setOversample(Options::video.supersampling ? 3 : 1);
return true;
@@ -366,9 +391,11 @@ void Screen::cycleTextureFilter(int dir) {
void Screen::changeInternalResolution(int dir) {
int next = Options::video.internal_resolution + (dir >= 0 ? 1 : -1);
if (next < 1) next = 1;
if (next > max_zoom_) next = max_zoom_;
if (next == Options::video.internal_resolution) return;
next = std::max(next, 1);
next = std::min(next, max_zoom_);
if (next == Options::video.internal_resolution) {
return;
}
Options::video.internal_resolution = next;
// Propaga al backend actiu. Al fallback path, la textura es recrea al
@@ -381,8 +408,12 @@ void Screen::changeInternalResolution(int dir) {
}
auto Screen::nextShaderType() -> bool {
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) return false;
if (!Options::video.shader_enabled) return false;
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) {
return false;
}
if (!Options::video.shader_enabled) {
return false;
}
if (shader_backend_->getActiveShader() == Rendering::ShaderType::POSTFX) {
shader_backend_->setActiveShader(Rendering::ShaderType::CRTPI);
@@ -397,16 +428,24 @@ auto Screen::nextShaderType() -> bool {
}
auto Screen::nextPreset() -> bool {
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) return false;
if (!Options::video.shader_enabled) return false;
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) {
return false;
}
if (!Options::video.shader_enabled) {
return false;
}
if (shader_backend_->getActiveShader() == Rendering::ShaderType::POSTFX) {
if (Options::postfx_presets.empty()) return false;
if (Options::postfx_presets.empty()) {
return false;
}
Options::current_postfx_preset = (Options::current_postfx_preset + 1) % static_cast<int>(Options::postfx_presets.size());
Options::video.current_postfx_preset = Options::postfx_presets[Options::current_postfx_preset].name;
applyCurrentPostFXPreset();
} else {
if (Options::crtpi_presets.empty()) return false;
if (Options::crtpi_presets.empty()) {
return false;
}
Options::current_crtpi_preset = (Options::current_crtpi_preset + 1) % static_cast<int>(Options::crtpi_presets.size());
Options::video.current_crtpi_preset = Options::crtpi_presets[Options::current_crtpi_preset].name;
applyCurrentCrtPiPreset();
@@ -420,17 +459,25 @@ auto Screen::prevShaderType() -> bool {
}
auto Screen::prevPreset() -> bool {
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) return false;
if (!Options::video.shader_enabled) return false;
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) {
return false;
}
if (!Options::video.shader_enabled) {
return false;
}
if (shader_backend_->getActiveShader() == Rendering::ShaderType::POSTFX) {
if (Options::postfx_presets.empty()) return false;
if (Options::postfx_presets.empty()) {
return false;
}
int n = static_cast<int>(Options::postfx_presets.size());
Options::current_postfx_preset = (Options::current_postfx_preset - 1 + n) % n;
Options::video.current_postfx_preset = Options::postfx_presets[Options::current_postfx_preset].name;
applyCurrentPostFXPreset();
} else {
if (Options::crtpi_presets.empty()) return false;
if (Options::crtpi_presets.empty()) {
return false;
}
int n = static_cast<int>(Options::crtpi_presets.size());
Options::current_crtpi_preset = (Options::current_crtpi_preset - 1 + n) % n;
Options::video.current_crtpi_preset = Options::crtpi_presets[Options::current_crtpi_preset].name;
@@ -440,13 +487,17 @@ auto Screen::prevPreset() -> bool {
}
auto Screen::getCurrentPresetName() const -> const char* {
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) return "---";
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) {
return "---";
}
if (shader_backend_->getActiveShader() == Rendering::ShaderType::POSTFX) {
if (Options::current_postfx_preset < static_cast<int>(Options::postfx_presets.size()))
if (Options::current_postfx_preset < static_cast<int>(Options::postfx_presets.size())) {
return Options::postfx_presets[Options::current_postfx_preset].name.c_str();
}
} else {
if (Options::current_crtpi_preset < static_cast<int>(Options::crtpi_presets.size()))
if (Options::current_crtpi_preset < static_cast<int>(Options::crtpi_presets.size())) {
return Options::crtpi_presets[Options::current_crtpi_preset].name.c_str();
}
}
return "---";
}
@@ -458,7 +509,9 @@ void Screen::setActiveShader(Rendering::ShaderType type) {
}
void Screen::applyCurrentPostFXPreset() {
if (!shader_backend_ || Options::postfx_presets.empty()) return;
if (!shader_backend_ || Options::postfx_presets.empty()) {
return;
}
const auto& preset = Options::postfx_presets[Options::current_postfx_preset];
Rendering::PostFXParams p;
p.vignette = preset.vignette;
@@ -473,7 +526,9 @@ void Screen::applyCurrentPostFXPreset() {
}
void Screen::applyCurrentCrtPiPreset() {
if (!shader_backend_ || Options::crtpi_presets.empty()) return;
if (!shader_backend_ || Options::crtpi_presets.empty()) {
return;
}
const auto& preset = Options::crtpi_presets[Options::current_crtpi_preset];
Rendering::CrtPiParams p;
p.scanline_weight = preset.scanline_weight;
@@ -498,7 +553,9 @@ auto Screen::isHardwareAccelerated() const -> bool {
}
auto Screen::getActiveShaderName() const -> const char* {
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) return "SENSE GPU";
if (!shader_backend_ || !shader_backend_->isHardwareAccelerated()) {
return "SENSE GPU";
}
return shader_backend_->getActiveShader() == Rendering::ShaderType::POSTFX ? "POSTFX" : "CRT-PI";
}
@@ -534,7 +591,7 @@ void Screen::updateRenderInfo() {
fps_driver.c_str(),
shader_seg.empty() ? nullptr : shader_seg.c_str(),
ss_seg,
time_buf[0] ? time_buf : nullptr,
(time_buf[0] != 0) ? time_buf : nullptr,
0b1001);
}
@@ -544,7 +601,9 @@ void Screen::applyFallbackPresentation() {
SDL_ScaleMode scale = (Options::video.texture_filter == Options::TextureFilter::LINEAR)
? SDL_SCALEMODE_LINEAR
: SDL_SCALEMODE_NEAREST;
if (texture_) SDL_SetTextureScaleMode(texture_, scale);
if (texture_ != nullptr) {
SDL_SetTextureScaleMode(texture_, scale);
}
// Si 4:3 actiu, la finestra ja té aspect 4:3 (alçada × 1.2); STRETCH és
// l'única opció viable al path fallback (el GPU path fa l'upscale 4:3 abans
@@ -578,7 +637,9 @@ void Screen::applyFallbackPresentation() {
}
void Screen::ensureFallbackInternalTexture() {
if (renderer_ == nullptr) return;
if (renderer_ == nullptr) {
return;
}
const int mult = Options::video.internal_resolution;
if (mult <= 1) {
// No cal textura intermèdia — recicla si la teníem.
@@ -589,7 +650,9 @@ void Screen::ensureFallbackInternalTexture() {
}
return;
}
if (internal_texture_sdl_ != nullptr && internal_texture_mult_ == mult) return;
if (internal_texture_sdl_ != nullptr && internal_texture_mult_ == mult) {
return;
}
if (internal_texture_sdl_ != nullptr) {
SDL_DestroyTexture(internal_texture_sdl_);
@@ -620,11 +683,11 @@ void Screen::adjustWindowSize() {
void Screen::calculateMaxZoom() {
SDL_DisplayID display = SDL_GetPrimaryDisplay();
const SDL_DisplayMode* mode = SDL_GetCurrentDisplayMode(display);
if (mode) {
if (mode != nullptr) {
int max_w = mode->w / GAME_WIDTH;
int max_h = mode->h / GAME_HEIGHT;
max_zoom_ = (max_w < max_h) ? max_w : max_h;
if (max_zoom_ < 1) max_zoom_ = 1;
max_zoom_ = std::max(max_zoom_, 1);
}
}
@@ -906,8 +906,8 @@ namespace Rendering {
// ---- Calcular viewport (dimensions lògiques del canvas) ----
// Si 4:3 actiu, effective_height ja és 240 (la textura estirada)
const float logical_w = static_cast<float>(game_width_);
const float logical_h = static_cast<float>(effective_height);
const auto logical_w = static_cast<float>(game_width_);
const auto logical_h = static_cast<float>(effective_height);
float vx = 0.0F;
float vy = 0.0F;
@@ -1276,7 +1276,9 @@ namespace Rendering {
// Recrea la textura intermèdia amb les noves dimensions (320·N × 200·N).
void SDL3GPUShader::setInternalResolution(int multiplier) {
const int NEW = std::max(1, multiplier);
if (NEW == internal_res_) return;
if (NEW == internal_res_) {
return;
}
internal_res_ = NEW;
if (is_initialized_ && device_ != nullptr) {
SDL_WaitForGPUIdle(device_);
@@ -1286,7 +1288,9 @@ namespace Rendering {
void SDL3GPUShader::setStretch4_3(bool enabled) {
stretch_4_3_ = enabled;
if (!is_initialized_ || device_ == nullptr) return;
if (!is_initialized_ || device_ == nullptr) {
return;
}
// Recrear scaled_texture_ perquè tinga les dimensions correctes (amb o sense 4:3)
if (oversample_ > 1 && ss_factor_ > 0) {
@@ -1464,7 +1468,9 @@ namespace Rendering {
SDL_ReleaseGPUTexture(device_, internal_texture_);
internal_texture_ = nullptr;
}
if (internal_res_ <= 1 || device_ == nullptr) return true;
if (internal_res_ <= 1 || device_ == nullptr) {
return true;
}
const int W = game_width_ * internal_res_;
const int H = game_height_ * internal_res_;
+127 -57
View File
@@ -12,7 +12,7 @@
// Forward declarations de gif.h (inclòs des de jdraw8.cpp, no es pot incloure dos vegades)
struct rgb;
extern unsigned char* LoadGif(unsigned char* data, unsigned short* w, unsigned short* h);
extern auto LoadGif(unsigned char* data, unsigned short* w, unsigned short* h) -> unsigned char*;
Text::Text(const char* fnt_file, const char* gif_file) {
loadBitmap(gif_file);
@@ -23,7 +23,9 @@ Text::Text(const char* fnt_file, const char* gif_file) {
auto Text::nextCodepoint(const char*& ptr) -> uint32_t {
auto byte = static_cast<uint8_t>(*ptr);
if (byte == 0) return 0;
if (byte == 0) {
return 0;
}
uint32_t cp = 0;
int extra = 0;
@@ -47,7 +49,9 @@ auto Text::nextCodepoint(const char*& ptr) -> uint32_t {
ptr++;
for (int i = 0; i < extra; i++) {
auto cont = static_cast<uint8_t>(*ptr);
if ((cont & 0xC0) != 0x80) return 0xFFFD;
if ((cont & 0xC0) != 0x80) {
return 0xFFFD;
}
cp = (cp << 6) | (cont & 0x3F);
ptr++;
}
@@ -71,7 +75,9 @@ void Text::loadFont(const char* fnt_file) {
while (std::getline(stream, line)) {
// Ignora comentaris i línies buides
if (line.empty() || line[0] == '#') continue;
if (line.empty() || line[0] == '#') {
continue;
}
// Elimina comentaris inline
auto comment_pos = line.find('#');
@@ -133,9 +139,10 @@ void Text::loadBitmap(const char* gif_file) {
int w = raw[6] | (raw[7] << 8);
int h = raw[8] | (raw[9] << 8);
unsigned short gw = 0, gh = 0;
unsigned short gw = 0;
unsigned short gh = 0;
Uint8* pixels = LoadGif(raw, &gw, &gh);
if (!pixels) {
if (pixels == nullptr) {
std::cerr << "Text: unable to decode GIF: " << gif_file << '\n';
return;
}
@@ -151,14 +158,18 @@ void Text::loadBitmap(const char* gif_file) {
// --- Renderitzat ---
void Text::draw(Uint32* pixel_data, int x, int y, const char* text, Uint32 color) const {
if (bitmap_.empty() || !pixel_data) return;
if (bitmap_.empty() || (pixel_data == nullptr)) {
return;
}
const char* ptr = text;
int cursor_x = x;
while (*ptr) {
while (*ptr != 0) {
uint32_t cp = nextCodepoint(ptr);
if (cp == 0) break;
if (cp == 0) {
break;
}
auto it = glyphs_.find(cp);
if (it == glyphs_.end()) {
@@ -174,21 +185,27 @@ void Text::draw(Uint32* pixel_data, int x, int y, const char* text, Uint32 color
// Pinta glifo pixel a pixel
for (int gy = 0; gy < box_height_; gy++) {
int dst_y = y + gy;
if (dst_y < 0 || dst_y >= SCREEN_HEIGHT) continue;
if (dst_y < 0 || dst_y >= SCREEN_HEIGHT) {
continue;
}
for (int gx = 0; gx < glyph.w; gx++) {
int dst_x = cursor_x + gx;
if (dst_x < 0 || dst_x >= SCREEN_WIDTH) continue;
if (dst_x < 0 || dst_x >= SCREEN_WIDTH) {
continue;
}
int src_x = glyph.x + gx;
int src_y = glyph.y + gy;
if (src_x >= bitmap_width_ || src_y >= bitmap_height_) continue;
if (src_x >= bitmap_width_ || src_y >= bitmap_height_) {
continue;
}
Uint8 pixel = bitmap_[src_x + src_y * bitmap_width_];
Uint8 pixel = bitmap_[src_x + (src_y * bitmap_width_)];
// Píxel no transparent (índex 0 és fons típicament)
if (pixel != 0) {
pixel_data[dst_x + dst_y * SCREEN_WIDTH] = color;
pixel_data[dst_x + (dst_y * SCREEN_WIDTH)] = color;
}
}
}
@@ -204,17 +221,23 @@ void Text::drawCentered(Uint32* pixel_data, int y, const char* text, Uint32 colo
}
void Text::drawClipped(Uint32* pixel_data, int x, int y, const char* text, Uint32 color, int clip_x_min, int clip_x_max, int clip_y_min, int clip_y_max) const {
if (bitmap_.empty() || !pixel_data) return;
if (bitmap_.empty() || (pixel_data == nullptr)) {
return;
}
// Descart ràpid si el glifo sencer cau fora verticalment
if (y + box_height_ <= clip_y_min || y >= clip_y_max) return;
if (y + box_height_ <= clip_y_min || y >= clip_y_max) {
return;
}
const char* ptr = text;
int cursor_x = x;
while (*ptr) {
while (*ptr != 0) {
uint32_t cp = nextCodepoint(ptr);
if (cp == 0) break;
if (cp == 0) {
break;
}
auto it = glyphs_.find(cp);
if (it == glyphs_.end()) {
@@ -235,21 +258,31 @@ void Text::drawClipped(Uint32* pixel_data, int x, int y, const char* text, Uint3
for (int gy = 0; gy < box_height_; gy++) {
int dst_y = y + gy;
if (dst_y < 0 || dst_y >= SCREEN_HEIGHT) continue;
if (dst_y < clip_y_min || dst_y >= clip_y_max) continue;
if (dst_y < 0 || dst_y >= SCREEN_HEIGHT) {
continue;
}
if (dst_y < clip_y_min || dst_y >= clip_y_max) {
continue;
}
for (int gx = 0; gx < glyph.w; gx++) {
int dst_x = cursor_x + gx;
if (dst_x < 0 || dst_x >= SCREEN_WIDTH) continue;
if (dst_x < clip_x_min || dst_x >= clip_x_max) continue;
if (dst_x < 0 || dst_x >= SCREEN_WIDTH) {
continue;
}
if (dst_x < clip_x_min || dst_x >= clip_x_max) {
continue;
}
int src_x = glyph.x + gx;
int src_y = glyph.y + gy;
if (src_x >= bitmap_width_ || src_y >= bitmap_height_) continue;
if (src_x >= bitmap_width_ || src_y >= bitmap_height_) {
continue;
}
Uint8 pixel = bitmap_[src_x + src_y * bitmap_width_];
Uint8 pixel = bitmap_[src_x + (src_y * bitmap_width_)];
if (pixel != 0) {
pixel_data[dst_x + dst_y * SCREEN_WIDTH] = color;
pixel_data[dst_x + (dst_y * SCREEN_WIDTH)] = color;
}
}
}
@@ -259,14 +292,18 @@ void Text::drawClipped(Uint32* pixel_data, int x, int y, const char* text, Uint3
}
void Text::drawMono(Uint32* pixel_data, int x, int y, const char* text, Uint32 color, int cell_w) const {
if (bitmap_.empty() || !pixel_data) return;
if (bitmap_.empty() || (pixel_data == nullptr)) {
return;
}
const char* ptr = text;
int cursor_x = x;
while (*ptr) {
while (*ptr != 0) {
uint32_t cp = nextCodepoint(ptr);
if (cp == 0) break;
if (cp == 0) {
break;
}
auto it = glyphs_.find(cp);
if (it == glyphs_.end()) {
@@ -279,23 +316,29 @@ void Text::drawMono(Uint32* pixel_data, int x, int y, const char* text, Uint32 c
const auto& glyph = it->second;
// Centra el glif dins la cel·la
int glyph_x = cursor_x + (cell_w - glyph.w) / 2;
int glyph_x = cursor_x + ((cell_w - glyph.w) / 2);
for (int gy = 0; gy < box_height_; gy++) {
int dst_y = y + gy;
if (dst_y < 0 || dst_y >= SCREEN_HEIGHT) continue;
if (dst_y < 0 || dst_y >= SCREEN_HEIGHT) {
continue;
}
for (int gx = 0; gx < glyph.w; gx++) {
int dst_x = glyph_x + gx;
if (dst_x < 0 || dst_x >= SCREEN_WIDTH) continue;
if (dst_x < 0 || dst_x >= SCREEN_WIDTH) {
continue;
}
int src_x = glyph.x + gx;
int src_y = glyph.y + gy;
if (src_x >= bitmap_width_ || src_y >= bitmap_height_) continue;
if (src_x >= bitmap_width_ || src_y >= bitmap_height_) {
continue;
}
Uint8 pixel = bitmap_[src_x + src_y * bitmap_width_];
Uint8 pixel = bitmap_[src_x + (src_y * bitmap_width_)];
if (pixel != 0) {
pixel_data[dst_x + dst_y * SCREEN_WIDTH] = color;
pixel_data[dst_x + (dst_y * SCREEN_WIDTH)] = color;
}
}
}
@@ -305,21 +348,27 @@ void Text::drawMono(Uint32* pixel_data, int x, int y, const char* text, Uint32 c
}
void Text::drawMonoDigits(Uint32* pixel_data, int x, int y, const char* text, Uint32 color, int digit_cell_w) const {
if (bitmap_.empty() || !pixel_data) return;
if (bitmap_.empty() || (pixel_data == nullptr)) {
return;
}
const char* ptr = text;
int cursor_x = x;
bool first = true;
while (*ptr) {
while (*ptr != 0) {
uint32_t cp = nextCodepoint(ptr);
if (cp == 0) break;
if (cp == 0) {
break;
}
auto it = glyphs_.find(cp);
if (it == glyphs_.end()) {
it = glyphs_.find('?');
if (it == glyphs_.end()) {
if (!first) cursor_x += 1;
if (!first) {
cursor_x += 1;
}
cursor_x += box_width_;
first = false;
continue;
@@ -329,22 +378,30 @@ void Text::drawMonoDigits(Uint32* pixel_data, int x, int y, const char* text, Ui
const auto& glyph = it->second;
bool is_digit = (cp >= '0' && cp <= '9');
if (!first) cursor_x += 1; // kerning
if (!first) {
cursor_x += 1; // kerning
}
int glyph_x = is_digit ? cursor_x + (digit_cell_w - glyph.w) / 2 : cursor_x;
int glyph_x = is_digit ? cursor_x + ((digit_cell_w - glyph.w) / 2) : cursor_x;
for (int gy = 0; gy < box_height_; gy++) {
int dst_y = y + gy;
if (dst_y < 0 || dst_y >= SCREEN_HEIGHT) continue;
if (dst_y < 0 || dst_y >= SCREEN_HEIGHT) {
continue;
}
for (int gx = 0; gx < glyph.w; gx++) {
int dst_x = glyph_x + gx;
if (dst_x < 0 || dst_x >= SCREEN_WIDTH) continue;
if (dst_x < 0 || dst_x >= SCREEN_WIDTH) {
continue;
}
int src_x = glyph.x + gx;
int src_y = glyph.y + gy;
if (src_x >= bitmap_width_ || src_y >= bitmap_height_) continue;
Uint8 pixel = bitmap_[src_x + src_y * bitmap_width_];
if (src_x >= bitmap_width_ || src_y >= bitmap_height_) {
continue;
}
Uint8 pixel = bitmap_[src_x + (src_y * bitmap_width_)];
if (pixel != 0) {
pixel_data[dst_x + dst_y * SCREEN_WIDTH] = color;
pixel_data[dst_x + (dst_y * SCREEN_WIDTH)] = color;
}
}
}
@@ -358,32 +415,41 @@ auto Text::widthMonoDigits(const char* text, int digit_cell_w) const -> int {
const char* ptr = text;
int w = 0;
bool first = true;
while (*ptr) {
while (*ptr != 0) {
uint32_t cp = nextCodepoint(ptr);
if (cp == 0) break;
if (!first) w += 1; // kerning
if (cp == 0) {
break;
}
if (!first) {
w += 1; // kerning
}
first = false;
bool is_digit = (cp >= '0' && cp <= '9');
if (is_digit) {
w += digit_cell_w;
} else {
auto it = glyphs_.find(cp);
if (it == glyphs_.end()) it = glyphs_.find('?');
if (it != glyphs_.end())
if (it == glyphs_.end()) {
it = glyphs_.find('?');
}
if (it != glyphs_.end()) {
w += it->second.w;
else
} else {
w += box_width_;
}
}
}
return w;
}
auto Text::widthMono(const char* text, int cell_w) const -> int {
auto Text::widthMono(const char* text, int cell_w) -> int {
const char* ptr = text;
int count = 0;
while (*ptr) {
while (*ptr != 0) {
uint32_t cp = nextCodepoint(ptr);
if (cp == 0) break;
if (cp == 0) {
break;
}
count++;
}
return count * cell_w;
@@ -394,16 +460,20 @@ auto Text::width(const char* text) const -> int {
int w = 0;
bool first = true;
while (*ptr) {
while (*ptr != 0) {
uint32_t cp = nextCodepoint(ptr);
if (cp == 0) break;
if (cp == 0) {
break;
}
auto it = glyphs_.find(cp);
if (it == glyphs_.end()) {
it = glyphs_.find('?');
}
if (!first) w += 1; // kerning
if (!first) {
w += 1; // kerning
}
first = false;
if (it != glyphs_.end()) {
+1 -1
View File
@@ -28,7 +28,7 @@ class Text {
// Calcula ancho en píxeles d'un text
[[nodiscard]] auto width(const char* text) const -> int;
// Amplada mono: nombre de codepoints × cell_w
[[nodiscard]] auto widthMono(const char* text, int cell_w) const -> int;
[[nodiscard]] static auto widthMono(const char* text, int cell_w) -> int;
// Amplada mono-dígits: amplada natural, però substituint els dígits per digit_cell_w
[[nodiscard]] auto widthMonoDigits(const char* text, int digit_cell_w) const -> int;
[[nodiscard]] auto charHeight() const -> int { return box_height_; }