Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 87 additions & 49 deletions RetroFE/Source/Graphics/Component/ReloadableGlobalHiscores.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ ReloadableGlobalHiscores::ReloadableGlobalHiscores(
int displayOffset,
FontManager* font,
float baseColumnPadding,
float baseRowPadding)
float baseRowPadding,
std::string renderType,
float qrDelaySec,
float qrFadeSec)
: Component(p)
, fontInst_(font)
, textFormat_(std::move(textFormat))
Expand Down Expand Up @@ -81,7 +84,29 @@ ReloadableGlobalHiscores::ReloadableGlobalHiscores(
, qrDelaySec_(2.0f)
, qrFadeSec_(1.0f)
, qrPlacement_(QrPlacement::TopLeft)
, qrMarginPx_(8) {
, qrMarginPx_(8)
, renderMode_(RenderMode::Both)
, renderTable_(true)
, renderQr_(true) {

const std::string mode = Utils::toLower(Utils::trimEnds(renderType));
if (mode == "qr") {
renderMode_ = RenderMode::QrOnly;
renderTable_ = false;
renderQr_ = true;
}
else if (mode == "table") {
renderMode_ = RenderMode::TableOnly;
renderTable_ = true;
renderQr_ = false;
}

if (std::isfinite(qrDelaySec) && qrDelaySec >= 0.0f) {
qrDelaySec_ = qrDelaySec;
}
if (std::isfinite(qrFadeSec) && qrFadeSec >= 0.0f) {
qrFadeSec_ = qrFadeSec;
}
}

ReloadableGlobalHiscores::~ReloadableGlobalHiscores() {
Expand Down Expand Up @@ -1167,59 +1192,62 @@ void ReloadableGlobalHiscores::reloadTexture() {
return;
}

std::vector<SDL_Texture*> qrTextures(Nvisible, nullptr);
std::vector<std::pair<int, int>> qrSizes(Nvisible, { 0,0 });

// --- Resolve the list of QR IDs for the selected game ---
std::vector<std::string> gameIds;
if (!selectedItem->iscoredId.empty()) {
Utils::listToVector(selectedItem->iscoredId, gameIds, ',');
}
if (renderQr_) {
std::vector<std::string> gameIds;
if (!selectedItem->iscoredId.empty()) {
Utils::listToVector(selectedItem->iscoredId, gameIds, ',');
}

// Check QR cache validity by IDs
bool qrCacheValid = (cachedQrGameIds_.size() == gameIds.size());
if (qrCacheValid) {
for (size_t i = 0; i < gameIds.size(); ++i) {
if (i >= cachedQrGameIds_.size() || cachedQrGameIds_[i] != gameIds[i]) {
qrCacheValid = false; break;
// Check QR cache validity by IDs
bool qrCacheValid = (cachedQrGameIds_.size() == gameIds.size());
if (qrCacheValid) {
for (size_t i = 0; i < gameIds.size(); ++i) {
if (i >= cachedQrGameIds_.size() || cachedQrGameIds_[i] != gameIds[i]) {
qrCacheValid = false; break;
}
}
}
}

// (Re)load QR textures only if IDs changed
if (!qrCacheValid) {
for (SDL_Texture* tex : cachedQrTextures_) if (tex) SDL_DestroyTexture(tex);
cachedQrTextures_.clear();
cachedQrSizes_.clear();
cachedQrGameIds_.clear();

cachedQrTextures_.resize(gameIds.size(), nullptr);
cachedQrSizes_.resize(gameIds.size(), { 0,0 });

for (size_t i = 0; i < gameIds.size(); ++i) {
if (gameIds[i].empty()) continue;
const std::string path = Configuration::absolutePath + "/iScored/qr/" + gameIds[i] + ".png";
if (SDL_Surface* surf = IMG_Load(path.c_str())) {
if (SDL_Texture* tex = SDL_CreateTextureFromSurface(renderer, surf)) {
SDL_SetTextureScaleMode(tex, SDL_ScaleModeNearest);
SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND); // set once
cachedQrTextures_[i] = tex;
cachedQrSizes_[i] = { surf->w, surf->h };
// (Re)load QR textures only if IDs changed
if (!qrCacheValid) {
for (SDL_Texture* tex : cachedQrTextures_) if (tex) SDL_DestroyTexture(tex);
cachedQrTextures_.clear();
cachedQrSizes_.clear();
cachedQrGameIds_.clear();

cachedQrTextures_.resize(gameIds.size(), nullptr);
cachedQrSizes_.resize(gameIds.size(), { 0,0 });

for (size_t i = 0; i < gameIds.size(); ++i) {
if (gameIds[i].empty()) continue;
const std::string path = Configuration::absolutePath + "/iScored/qr/" + gameIds[i] + ".png";
if (SDL_Surface* surf = IMG_Load(path.c_str())) {
if (SDL_Texture* tex = SDL_CreateTextureFromSurface(renderer, surf)) {
SDL_SetTextureScaleMode(tex, SDL_ScaleModeNearest);
SDL_SetTextureBlendMode(tex, SDL_BLENDMODE_BLEND); // set once
cachedQrTextures_[i] = tex;
cachedQrSizes_[i] = { surf->w, surf->h };
}
SDL_FreeSurface(surf);
}
SDL_FreeSurface(surf);
}
}

cachedQrGameIds_ = gameIds;
// IDs changed → sizes likely changed somewhere; mark all dirty
for (auto& L : cachedTableLayouts_) L.qrDstDirty = true;
}
cachedQrGameIds_ = gameIds;
// IDs changed → sizes likely changed somewhere; mark all dirty
for (auto& L : cachedTableLayouts_) L.qrDstDirty = true;
}

// Map cached QR textures/sizes to the visible range
std::vector<SDL_Texture*> qrTextures(Nvisible, nullptr);
std::vector<std::pair<int, int>> qrSizes(Nvisible, { 0,0 });
for (int t = 0; t < Nvisible; ++t) {
const int gi = startIdx + t;
if (gi < (int)cachedQrTextures_.size()) {
qrTextures[t] = cachedQrTextures_[gi];
qrSizes[t] = cachedQrSizes_[gi];
// Map cached QR textures/sizes to the visible range
for (int t = 0; t < Nvisible; ++t) {
const int gi = startIdx + t;
if (gi < (int)cachedQrTextures_.size()) {
qrTextures[t] = cachedQrTextures_[gi];
qrSizes[t] = cachedQrSizes_[gi];
}
}
}

Expand Down Expand Up @@ -1515,7 +1543,17 @@ void ReloadableGlobalHiscores::reloadTexture() {
L.qrDstDirty = true; // size change → re-place
}
if (L.qrDstDirty) {
L.qrDst = placeQrInPanel(L.panelRect, L.qrSrcW, L.qrSrcH);
if (renderMode_ == RenderMode::QrOnly) {
L.qrDst = {
0.0f,
0.0f,
static_cast<float>(compositeW),
static_cast<float>(compositeH)
};
}
else {
L.qrDst = placeQrInPanel(L.panelRect, L.qrSrcW, L.qrSrcH);
}
L.qrDstDirty = false;
}
}
Expand All @@ -1538,12 +1576,12 @@ void ReloadableGlobalHiscores::reloadTexture() {
SDL_RenderClear(renderer);
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);

if (tableTexture_) {
if (renderTable_ && tableTexture_) {
SDL_RenderCopy(renderer, tableTexture_, nullptr, nullptr);
}

// Add QRs on top (if QR phase allows)
if (qrPhase_ == QrPhase::FadingIn || qrPhase_ == QrPhase::Visible) {
if (renderQr_ && (qrPhase_ == QrPhase::FadingIn || qrPhase_ == QrPhase::Visible)) {
float qrAlphaF = 1.0f;
if (qrPhase_ == QrPhase::FadingIn && qrFadeSec_ > 0.f) {
qrAlphaF = std::clamp(qrT_ / qrFadeSec_, 0.f, 1.f);
Expand Down
14 changes: 13 additions & 1 deletion RetroFE/Source/Graphics/Component/ReloadableGlobalHiscores.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ class ReloadableGlobalHiscores : public Component {
ReloadableGlobalHiscores(Configuration& config, std::string textFormat,
Page& p, int displayOffset,
FontManager* font,
float baseColumnPadding, float baseRowPadding);
float baseColumnPadding, float baseRowPadding,
std::string renderType,
float qrDelaySec,
float qrFadeSec);
~ReloadableGlobalHiscores() override;

bool update(float dt) override;
Expand Down Expand Up @@ -72,6 +75,12 @@ class ReloadableGlobalHiscores : public Component {
LeftMiddle
};

enum class RenderMode {
Both,
QrOnly,
TableOnly
};


// Per-table layout cache struct
struct TableLayout {
Expand Down Expand Up @@ -202,6 +211,9 @@ class ReloadableGlobalHiscores : public Component {
// --- QR configuration ---
QrPlacement qrPlacement_; // where to place QR codes
int qrMarginPx_; // margin between QR and panel
RenderMode renderMode_; // both/table-only/qr-only
bool renderTable_;
bool renderQr_;

// Layout cache (populated in Stage 1, reused in Stage 2)
std::vector<TableLayout> cachedTableLayouts_;
Expand Down
7 changes: 6 additions & 1 deletion RetroFE/Source/Graphics/PageBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -775,6 +775,8 @@ void PageBuilder::loadReloadableImages(const xml_node<>* layout, const std::stri
xml_attribute<> const* locationXml = componentXml->first_attribute("location");
xml_attribute<> const* baseColumnPaddingXml = componentXml->first_attribute("baseColumnPadding");
xml_attribute<> const* baseRowPaddingXml = componentXml->first_attribute("baseRowPadding");
xml_attribute<> const* qrDelaySecXml = componentXml->first_attribute("qrDelaySec");
xml_attribute<> const* qrFadeSecXml = componentXml->first_attribute("qrFadeSec");
xml_attribute<> const* maxRowsXml = componentXml->first_attribute("maxRows");
xml_attribute<> const* excludedColumnsXml = componentXml->first_attribute("excludedColumns");

Expand Down Expand Up @@ -878,12 +880,15 @@ void PageBuilder::loadReloadableImages(const xml_node<>* layout, const std::stri
else if (tagName == "reloadableGlobalHiscores") {
FontManager* font = addFont(componentXml, nullptr, cMonitor);
std::string textFormat = textFormatXml ? textFormatXml->value() : "";
std::string renderType = type ? type->value() : "";
float baseColumnPadding = baseColumnPaddingXml ? Utils::convertFloat(baseColumnPaddingXml->value()) : 1.5f;
float baseRowPadding = baseRowPaddingXml ? Utils::convertFloat(baseRowPaddingXml->value()) : 0.5f;
float qrDelaySec = qrDelaySecXml ? Utils::convertFloat(qrDelaySecXml->value()) : 2.0f;
float qrFadeSec = qrFadeSecXml ? Utils::convertFloat(qrFadeSecXml->value()) : 1.0f;
std::string excludedColumns = excludedColumnsXml ? excludedColumnsXml->value() : "";

c = new ReloadableGlobalHiscores(config_, textFormat, *page, selectedOffset,
font, baseColumnPadding, baseRowPadding);
font, baseColumnPadding, baseRowPadding, renderType, qrDelaySec, qrFadeSec);
}

else if (tagName == "musicPlayer") {
Expand Down