Somewhat fixed ui
This commit is contained in:
139
src/uisystem.cpp
139
src/uisystem.cpp
@@ -4,6 +4,7 @@
|
|||||||
#include <psyqo/primitives/common.hh>
|
#include <psyqo/primitives/common.hh>
|
||||||
#include <psyqo/primitives/misc.hh>
|
#include <psyqo/primitives/misc.hh>
|
||||||
#include <psyqo/primitives/rectangles.hh>
|
#include <psyqo/primitives/rectangles.hh>
|
||||||
|
#include <psyqo/primitives/sprites.hh>
|
||||||
#include <psyqo/primitives/triangles.hh>
|
#include <psyqo/primitives/triangles.hh>
|
||||||
|
|
||||||
namespace psxsplash {
|
namespace psxsplash {
|
||||||
@@ -38,7 +39,9 @@ void UISystem::loadFromSplashpack(uint8_t* data, uint16_t canvasCount,
|
|||||||
|
|
||||||
uint8_t* ptr = data + tableOffset;
|
uint8_t* ptr = data + tableOffset;
|
||||||
|
|
||||||
// ── Parse font descriptors (16 bytes each, before canvas data) ──
|
// ── Parse font descriptors (112 bytes each, before canvas data) ──
|
||||||
|
// Layout: glyphW(1) glyphH(1) vramX(2) vramY(2) textureH(2)
|
||||||
|
// dataOffset(4) dataSize(4) advanceWidths(96)
|
||||||
if (fontCount > UI_MAX_FONTS - 1) fontCount = UI_MAX_FONTS - 1;
|
if (fontCount > UI_MAX_FONTS - 1) fontCount = UI_MAX_FONTS - 1;
|
||||||
m_fontCount = fontCount;
|
m_fontCount = fontCount;
|
||||||
for (int fi = 0; fi < fontCount; fi++) {
|
for (int fi = 0; fi < fontCount; fi++) {
|
||||||
@@ -51,7 +54,30 @@ void UISystem::loadFromSplashpack(uint8_t* data, uint16_t canvasCount,
|
|||||||
uint32_t dataOff = *reinterpret_cast<uint32_t*>(ptr + 8);
|
uint32_t dataOff = *reinterpret_cast<uint32_t*>(ptr + 8);
|
||||||
fd.pixelDataSize = *reinterpret_cast<uint32_t*>(ptr + 12);
|
fd.pixelDataSize = *reinterpret_cast<uint32_t*>(ptr + 12);
|
||||||
fd.pixelData = (dataOff != 0) ? (data + dataOff) : nullptr;
|
fd.pixelData = (dataOff != 0) ? (data + dataOff) : nullptr;
|
||||||
ptr += 16;
|
// Read 96 advance width bytes
|
||||||
|
for (int i = 0; i < 96; i++) {
|
||||||
|
fd.advanceWidths[i] = ptr[16 + i];
|
||||||
|
}
|
||||||
|
ptr += 112;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Skip past font pixel data to reach canvas descriptors ──
|
||||||
|
// The binary layout is: [font descriptors] [font pixel data] [canvas descriptors]
|
||||||
|
// ptr is currently past the font descriptors. We need to skip the pixel data block.
|
||||||
|
// Pixel data positions are stored as absolute offsets in the descriptors.
|
||||||
|
if (fontCount > 0) {
|
||||||
|
uint32_t fontDataEnd = 0;
|
||||||
|
for (int fi = 0; fi < fontCount; fi++) {
|
||||||
|
if (m_fontDescs[fi].pixelData != nullptr && m_fontDescs[fi].pixelDataSize > 0) {
|
||||||
|
uint32_t endPos = (uint32_t)(m_fontDescs[fi].pixelData - data) + m_fontDescs[fi].pixelDataSize;
|
||||||
|
if (endPos > fontDataEnd) fontDataEnd = endPos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fontDataEnd > 0) {
|
||||||
|
// Align to 4 bytes (matching the writer's AlignToFourBytes)
|
||||||
|
fontDataEnd = (fontDataEnd + 3) & ~3u;
|
||||||
|
ptr = data + fontDataEnd;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Parse canvas descriptors ──
|
// ── Parse canvas descriptors ──
|
||||||
@@ -61,7 +87,7 @@ void UISystem::loadFromSplashpack(uint8_t* data, uint16_t canvasCount,
|
|||||||
// Canvas descriptor table: 12 bytes per entry
|
// Canvas descriptor table: 12 bytes per entry
|
||||||
// struct { uint32_t dataOffset; uint8_t nameLen; uint8_t sortOrder;
|
// struct { uint32_t dataOffset; uint8_t nameLen; uint8_t sortOrder;
|
||||||
// uint8_t elementCount; uint8_t flags; uint32_t nameOffset; }
|
// uint8_t elementCount; uint8_t flags; uint32_t nameOffset; }
|
||||||
uint8_t* tablePtr = ptr; // starts right after font descriptors
|
uint8_t* tablePtr = ptr; // starts after font descriptors AND pixel data
|
||||||
m_canvasCount = canvasCount;
|
m_canvasCount = canvasCount;
|
||||||
m_elementCount = 0;
|
m_elementCount = 0;
|
||||||
|
|
||||||
@@ -190,7 +216,7 @@ void UISystem::loadFromSplashpack(uint8_t* data, uint16_t canvasCount,
|
|||||||
void UISystem::resolveLayout(const UIElement& el,
|
void UISystem::resolveLayout(const UIElement& el,
|
||||||
int16_t& outX, int16_t& outY,
|
int16_t& outX, int16_t& outY,
|
||||||
int16_t& outW, int16_t& outH) const {
|
int16_t& outW, int16_t& outH) const {
|
||||||
// Anchor gives the origin point in screen space (8.8 fixed → pixel)
|
// Anchor gives the origin point in screen space (8.8 fixed -> pixel)
|
||||||
int ax = ((int)el.anchorMinX * VRAM_RES_WIDTH) >> 8;
|
int ax = ((int)el.anchorMinX * VRAM_RES_WIDTH) >> 8;
|
||||||
int ay = ((int)el.anchorMinY * VRAM_RES_HEIGHT) >> 8;
|
int ay = ((int)el.anchorMinY * VRAM_RES_HEIGHT) >> 8;
|
||||||
outX = (int16_t)(ax + el.x);
|
outX = (int16_t)(ax + el.x);
|
||||||
@@ -227,7 +253,7 @@ psyqo::PrimPieces::TPageAttr UISystem::makeTPage(const UIImageData& img) {
|
|||||||
psyqo::PrimPieces::TPageAttr tpage;
|
psyqo::PrimPieces::TPageAttr tpage;
|
||||||
tpage.setPageX(img.texpageX);
|
tpage.setPageX(img.texpageX);
|
||||||
tpage.setPageY(img.texpageY);
|
tpage.setPageY(img.texpageY);
|
||||||
// Color mode from bitDepth: 0→Tex4Bits, 1→Tex8Bits, 2→Tex16Bits
|
// Color mode from bitDepth: 0->Tex4Bits, 1->Tex8Bits, 2->Tex16Bits
|
||||||
switch (img.bitDepth) {
|
switch (img.bitDepth) {
|
||||||
case 0:
|
case 0:
|
||||||
tpage.set(psyqo::Prim::TPageAttr::Tex4Bits);
|
tpage.set(psyqo::Prim::TPageAttr::Tex4Bits);
|
||||||
@@ -332,13 +358,17 @@ void UISystem::renderElement(UIElement& el,
|
|||||||
}
|
}
|
||||||
|
|
||||||
case UIElementType::Text: {
|
case UIElementType::Text: {
|
||||||
// Queue text for phase 2 (after gpu.chain)
|
uint8_t fi = el.textData.fontIndex;
|
||||||
if (m_pendingTextCount < UI_MAX_ELEMENTS) {
|
if (fi > 0 && fi <= m_fontCount) {
|
||||||
uint8_t fi = (el.type == UIElementType::Text) ? el.textData.fontIndex : 0;
|
// Custom font: render proportionally into OT
|
||||||
|
renderProportionalText(fi - 1, x, y,
|
||||||
|
el.colorR, el.colorG, el.colorB,
|
||||||
|
el.textBuf, ot, balloc);
|
||||||
|
} else if (m_pendingTextCount < UI_MAX_ELEMENTS) {
|
||||||
|
// System font: queue for renderText phase (chainprintf)
|
||||||
m_pendingTexts[m_pendingTextCount++] = {
|
m_pendingTexts[m_pendingTextCount++] = {
|
||||||
x, y,
|
x, y,
|
||||||
el.colorR, el.colorG, el.colorB,
|
el.colorR, el.colorG, el.colorB,
|
||||||
fi,
|
|
||||||
el.textBuf
|
el.textBuf
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -372,9 +402,7 @@ void UISystem::renderOT(psyqo::GPU& gpu,
|
|||||||
void UISystem::renderText(psyqo::GPU& gpu) {
|
void UISystem::renderText(psyqo::GPU& gpu) {
|
||||||
for (int i = 0; i < m_pendingTextCount; i++) {
|
for (int i = 0; i < m_pendingTextCount; i++) {
|
||||||
auto& pt = m_pendingTexts[i];
|
auto& pt = m_pendingTexts[i];
|
||||||
psyqo::FontBase* font = resolveFont(pt.fontIndex);
|
m_systemFont->chainprintf(gpu,
|
||||||
if (!font) continue;
|
|
||||||
font->chainprintf(gpu,
|
|
||||||
{{.x = pt.x, .y = pt.y}},
|
{{.x = pt.x, .y = pt.y}},
|
||||||
{{.r = pt.r, .g = pt.g, .b = pt.b}},
|
{{.r = pt.r, .g = pt.g, .b = pt.b}},
|
||||||
"%s", pt.text);
|
"%s", pt.text);
|
||||||
@@ -386,8 +414,8 @@ void UISystem::renderText(psyqo::GPU& gpu) {
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
psyqo::FontBase* UISystem::resolveFont(uint8_t fontIndex) {
|
psyqo::FontBase* UISystem::resolveFont(uint8_t fontIndex) {
|
||||||
if (fontIndex == 0 || fontIndex > m_fontCount) return m_systemFont;
|
// Only used for system font now; custom fonts go through renderProportionalText
|
||||||
return &m_customFonts[fontIndex - 1];
|
return m_systemFont;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UISystem::uploadFonts(psyqo::GPU& gpu) {
|
void UISystem::uploadFonts(psyqo::GPU& gpu) {
|
||||||
@@ -402,13 +430,88 @@ void UISystem::uploadFonts(psyqo::GPU& gpu) {
|
|||||||
(int16_t)fd.vramX, (int16_t)fd.vramY,
|
(int16_t)fd.vramX, (int16_t)fd.vramY,
|
||||||
64, (int16_t)fd.textureH);
|
64, (int16_t)fd.textureH);
|
||||||
|
|
||||||
// Initialize the Font<2> instance for this custom font
|
// Upload white CLUT at font CLUT position (entry 0=transparent, entry 1=white).
|
||||||
m_customFonts[i].initialize(gpu,
|
// Sprite color tinting will produce the desired text color.
|
||||||
{{.x = (int16_t)fd.vramX, .y = (int16_t)fd.vramY}},
|
static const uint16_t whiteCLUT[2] = { 0x0000, 0x7FFF };
|
||||||
{{.x = (int16_t)fd.glyphW, .y = (int16_t)fd.glyphH}});
|
Renderer::GetInstance().VramUpload(
|
||||||
|
whiteCLUT,
|
||||||
|
(int16_t)fd.vramX, (int16_t)fd.vramY,
|
||||||
|
2, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Proportional text rendering (custom fonts)
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
void UISystem::renderProportionalText(int fontIdx, int16_t x, int16_t y,
|
||||||
|
uint8_t r, uint8_t g, uint8_t b,
|
||||||
|
const char* text,
|
||||||
|
psyqo::OrderingTable<Renderer::ORDERING_TABLE_SIZE>& ot,
|
||||||
|
psyqo::BumpAllocator<Renderer::BUMP_ALLOCATOR_SIZE>& balloc) {
|
||||||
|
UIFontDesc& fd = m_fontDescs[fontIdx];
|
||||||
|
int glyphsPerRow = 256 / fd.glyphW;
|
||||||
|
uint8_t baseV = fd.vramY & 0xFF;
|
||||||
|
|
||||||
|
// TPage for this font's texture page
|
||||||
|
psyqo::PrimPieces::TPageAttr tpageAttr;
|
||||||
|
tpageAttr.setPageX(fd.vramX >> 6);
|
||||||
|
tpageAttr.setPageY(fd.vramY >> 8);
|
||||||
|
tpageAttr.set(psyqo::Prim::TPageAttr::Tex4Bits);
|
||||||
|
tpageAttr.setDithering(false);
|
||||||
|
|
||||||
|
// CLUT reference for this font
|
||||||
|
psyqo::Vertex clutPos = {{.x = (int16_t)fd.vramX, .y = (int16_t)fd.vramY}};
|
||||||
|
psyqo::PrimPieces::ClutIndex clutIdx(clutPos);
|
||||||
|
|
||||||
|
psyqo::Color color = {.r = r, .g = g, .b = b};
|
||||||
|
|
||||||
|
// First: insert all glyph sprites at depth 0
|
||||||
|
int16_t cursorX = x;
|
||||||
|
while (*text) {
|
||||||
|
uint8_t c = (uint8_t)*text++;
|
||||||
|
if (c < 32 || c > 127) c = '?';
|
||||||
|
uint8_t charIdx = c - 32;
|
||||||
|
|
||||||
|
uint8_t advance = fd.advanceWidths[charIdx];
|
||||||
|
|
||||||
|
if (c == ' ') {
|
||||||
|
cursorX += advance;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int charRow = charIdx / glyphsPerRow;
|
||||||
|
int charCol = charIdx % glyphsPerRow;
|
||||||
|
uint8_t u = (uint8_t)(charCol * fd.glyphW);
|
||||||
|
uint8_t v = (uint8_t)(baseV + charRow * fd.glyphH);
|
||||||
|
|
||||||
|
auto& frag = balloc.allocateFragment<psyqo::Prim::Sprite>();
|
||||||
|
frag.primitive.position = {.x = cursorX, .y = y};
|
||||||
|
// Use advance as sprite width for proportional sizing.
|
||||||
|
// The glyph is left-aligned in the cell, so showing advance-width
|
||||||
|
// pixels captures the glyph content with correct spacing.
|
||||||
|
int16_t spriteW = (advance > 0 && advance < fd.glyphW) ? (int16_t)advance : (int16_t)fd.glyphW;
|
||||||
|
frag.primitive.size = {.x = spriteW, .y = (int16_t)fd.glyphH};
|
||||||
|
frag.primitive.setColor(color);
|
||||||
|
psyqo::PrimPieces::TexInfo texInfo;
|
||||||
|
texInfo.u = u;
|
||||||
|
texInfo.v = v;
|
||||||
|
texInfo.clut = clutIdx;
|
||||||
|
frag.primitive.texInfo = texInfo;
|
||||||
|
ot.insert(frag, 0);
|
||||||
|
|
||||||
|
cursorX += advance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then: insert TPage AFTER sprites at the same depth.
|
||||||
|
// OT uses head insertion (LIFO), so TPage ends up rendering BEFORE the sprites.
|
||||||
|
// This ensures each text element's TPage is active for its own sprites only,
|
||||||
|
// even when multiple fonts are on screen simultaneously.
|
||||||
|
auto& tpFrag = balloc.allocateFragment<psyqo::Prim::TPage>();
|
||||||
|
tpFrag.primitive.attr = tpageAttr;
|
||||||
|
ot.insert(tpFrag, 0);
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Canvas API
|
// Canvas API
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ struct UIFontDesc {
|
|||||||
uint16_t textureH;
|
uint16_t textureH;
|
||||||
const uint8_t* pixelData; // raw 4bpp, points into splashpack
|
const uint8_t* pixelData; // raw 4bpp, points into splashpack
|
||||||
uint32_t pixelDataSize;
|
uint32_t pixelDataSize;
|
||||||
|
uint8_t advanceWidths[96]; // per-char advance (ASCII 0x20-0x7F) in pixels
|
||||||
};
|
};
|
||||||
|
|
||||||
class UISystem {
|
class UISystem {
|
||||||
@@ -77,17 +78,17 @@ public:
|
|||||||
void loadFromSplashpack(uint8_t* data, uint16_t canvasCount,
|
void loadFromSplashpack(uint8_t* data, uint16_t canvasCount,
|
||||||
uint8_t fontCount, uint32_t tableOffset);
|
uint8_t fontCount, uint32_t tableOffset);
|
||||||
|
|
||||||
/// Upload custom font textures to VRAM and initialize Font<> instances.
|
/// Upload custom font textures to VRAM.
|
||||||
/// Must be called AFTER loadFromSplashpack and BEFORE first render.
|
/// Must be called AFTER loadFromSplashpack and BEFORE first render.
|
||||||
void uploadFonts(psyqo::GPU& gpu);
|
void uploadFonts(psyqo::GPU& gpu);
|
||||||
|
|
||||||
// Phase 1: Insert OT primitives for boxes, images, progress bars.
|
// Phase 1: Insert OT primitives for boxes, images, progress bars, and custom font text.
|
||||||
// Called BEFORE gpu.chain(ot) from inside the renderer.
|
// Called BEFORE gpu.chain(ot) from inside the renderer.
|
||||||
void renderOT(psyqo::GPU& gpu,
|
void renderOT(psyqo::GPU& gpu,
|
||||||
psyqo::OrderingTable<Renderer::ORDERING_TABLE_SIZE>& ot,
|
psyqo::OrderingTable<Renderer::ORDERING_TABLE_SIZE>& ot,
|
||||||
psyqo::BumpAllocator<Renderer::BUMP_ALLOCATOR_SIZE>& balloc);
|
psyqo::BumpAllocator<Renderer::BUMP_ALLOCATOR_SIZE>& balloc);
|
||||||
|
|
||||||
// Phase 2: Emit text via psyqo font chaining.
|
// Phase 2: Emit system font text via psyqo font chaining.
|
||||||
// Called AFTER gpu.chain(ot).
|
// Called AFTER gpu.chain(ot).
|
||||||
void renderText(psyqo::GPU& gpu);
|
void renderText(psyqo::GPU& gpu);
|
||||||
|
|
||||||
@@ -96,7 +97,7 @@ public:
|
|||||||
void setCanvasVisible(int idx, bool v);
|
void setCanvasVisible(int idx, bool v);
|
||||||
bool isCanvasVisible(int idx) const;
|
bool isCanvasVisible(int idx) const;
|
||||||
|
|
||||||
// Element API — returns flat handle into m_elements, or -1
|
// Element API - returns flat handle into m_elements, or -1
|
||||||
int findElement(int canvasIdx, const char* name) const;
|
int findElement(int canvasIdx, const char* name) const;
|
||||||
void setElementVisible(int handle, bool v);
|
void setElementVisible(int handle, bool v);
|
||||||
bool isElementVisible(int handle) const;
|
bool isElementVisible(int handle) const;
|
||||||
@@ -119,8 +120,6 @@ public:
|
|||||||
private:
|
private:
|
||||||
psyqo::Font<>* m_systemFont = nullptr;
|
psyqo::Font<>* m_systemFont = nullptr;
|
||||||
|
|
||||||
// Custom fonts: up to 3, each with 4 fragments for DMA chaining
|
|
||||||
psyqo::Font<4> m_customFonts[UI_MAX_FONTS - 1];
|
|
||||||
UIFontDesc m_fontDescs[UI_MAX_FONTS - 1]; // descriptors from splashpack
|
UIFontDesc m_fontDescs[UI_MAX_FONTS - 1]; // descriptors from splashpack
|
||||||
int m_fontCount = 0; // number of custom fonts (0-3)
|
int m_fontCount = 0; // number of custom fonts (0-3)
|
||||||
|
|
||||||
@@ -129,12 +128,12 @@ private:
|
|||||||
int m_canvasCount = 0;
|
int m_canvasCount = 0;
|
||||||
int m_elementCount = 0;
|
int m_elementCount = 0;
|
||||||
|
|
||||||
// Per-frame pending text list (filled during renderOT, flushed in renderText)
|
// Pending text for system font only (custom fonts render in OT)
|
||||||
struct PendingText { int16_t x, y; uint8_t r, g, b; uint8_t fontIndex; const char* text; };
|
struct PendingText { int16_t x, y; uint8_t r, g, b; const char* text; };
|
||||||
PendingText m_pendingTexts[UI_MAX_ELEMENTS];
|
PendingText m_pendingTexts[UI_MAX_ELEMENTS];
|
||||||
int m_pendingTextCount = 0;
|
int m_pendingTextCount = 0;
|
||||||
|
|
||||||
/// Resolve which Font to use for a given fontIndex.
|
/// Resolve which Font to use for system font (fontIndex 0).
|
||||||
psyqo::FontBase* resolveFont(uint8_t fontIndex);
|
psyqo::FontBase* resolveFont(uint8_t fontIndex);
|
||||||
|
|
||||||
void resolveLayout(const UIElement& el,
|
void resolveLayout(const UIElement& el,
|
||||||
@@ -145,6 +144,12 @@ private:
|
|||||||
psyqo::OrderingTable<Renderer::ORDERING_TABLE_SIZE>& ot,
|
psyqo::OrderingTable<Renderer::ORDERING_TABLE_SIZE>& ot,
|
||||||
psyqo::BumpAllocator<Renderer::BUMP_ALLOCATOR_SIZE>& balloc);
|
psyqo::BumpAllocator<Renderer::BUMP_ALLOCATOR_SIZE>& balloc);
|
||||||
|
|
||||||
|
void renderProportionalText(int fontIdx, int16_t x, int16_t y,
|
||||||
|
uint8_t r, uint8_t g, uint8_t b,
|
||||||
|
const char* text,
|
||||||
|
psyqo::OrderingTable<Renderer::ORDERING_TABLE_SIZE>& ot,
|
||||||
|
psyqo::BumpAllocator<Renderer::BUMP_ALLOCATOR_SIZE>& balloc);
|
||||||
|
|
||||||
static psyqo::PrimPieces::TPageAttr makeTPage(const UIImageData& img);
|
static psyqo::PrimPieces::TPageAttr makeTPage(const UIImageData& img);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user