mirror of https://github.com/AxioDL/metaforce.git
241 lines
7.2 KiB
C++
241 lines
7.2 KiB
C++
#include "Runtime/GuiSys/CRasterFont.hpp"
|
|
|
|
#include <algorithm>
|
|
|
|
#include "Runtime/CSimplePool.hpp"
|
|
#include "Runtime/Graphics/CGX.hpp"
|
|
#include "Runtime/Graphics/CTexture.hpp"
|
|
#include "Runtime/GuiSys/CDrawStringOptions.hpp"
|
|
#include "Runtime/GuiSys/CTextRenderBuffer.hpp"
|
|
|
|
namespace metaforce {
|
|
CRasterFont::CRasterFont(metaforce::CInputStream& in, metaforce::IObjectStore& store) {
|
|
u32 magic = 0;
|
|
in.Get(reinterpret_cast<u8*>(&magic), 4);
|
|
if (magic != SBIG('FONT'))
|
|
return;
|
|
|
|
u32 version = in.ReadLong();
|
|
x4_monoWidth = in.ReadLong();
|
|
x8_monoHeight = in.ReadLong();
|
|
|
|
if (version >= 1)
|
|
x8c_baseline = in.ReadLong();
|
|
else
|
|
x8c_baseline = x8_monoHeight;
|
|
|
|
if (version >= 2)
|
|
x90_lineMargin = in.ReadLong();
|
|
|
|
bool tmp1 = in.ReadBool();
|
|
bool tmp2 = in.ReadBool();
|
|
|
|
u32 tmp3 = in.ReadLong();
|
|
u32 tmp4 = in.ReadLong();
|
|
std::string name = in.Get<std::string>();
|
|
u32 txtrId = (version == 5 ? in.ReadLongLong() : in.ReadLong());
|
|
x30_fontInfo = CFontInfo(tmp1, tmp2, tmp3, tmp4, name.c_str());
|
|
x80_texture = store.GetObj({FOURCC('TXTR'), txtrId});
|
|
x2c_mode = CTexture::EFontType(in.ReadLong());
|
|
|
|
u32 glyphCount = in.ReadLong();
|
|
xc_glyphs.reserve(glyphCount);
|
|
|
|
for (u32 i = 0; i < glyphCount; ++i) {
|
|
char16_t chr = in.ReadShort();
|
|
float startU = in.ReadFloat();
|
|
float startV = in.ReadFloat();
|
|
float endU = in.ReadFloat();
|
|
float endV = in.ReadFloat();
|
|
s32 layer = 0;
|
|
s32 a, b, c, cellWidth, cellHeight, baseline, kernStart;
|
|
if (version < 4) {
|
|
a = in.ReadInt32();
|
|
b = in.ReadInt32();
|
|
c = in.ReadInt32();
|
|
cellWidth = in.ReadInt32();
|
|
cellHeight = in.ReadInt32();
|
|
baseline = in.ReadInt32();
|
|
kernStart = in.ReadInt32();
|
|
} else {
|
|
layer = in.ReadInt8();
|
|
a = in.ReadInt8();
|
|
b = in.ReadInt8();
|
|
c = in.ReadInt8();
|
|
cellWidth = in.ReadInt8();
|
|
cellHeight = in.ReadInt8();
|
|
baseline = in.ReadInt8();
|
|
kernStart = in.ReadInt16();
|
|
}
|
|
xc_glyphs.emplace_back(
|
|
chr, CGlyph(a, b, c, startU, startV, endU, endV, cellWidth, cellHeight, baseline, kernStart, layer));
|
|
}
|
|
|
|
std::sort(xc_glyphs.begin(), xc_glyphs.end(), [=](auto& a, auto& b) -> bool { return a.first < b.first; });
|
|
|
|
u32 kernCount = in.ReadLong();
|
|
x1c_kerning.reserve(kernCount);
|
|
|
|
for (u32 i = 0; i < kernCount; ++i) {
|
|
char16_t first = in.ReadShort();
|
|
char16_t second = in.ReadShort();
|
|
s32 howMuch = in.ReadInt32();
|
|
x1c_kerning.emplace_back(first, second, howMuch);
|
|
}
|
|
|
|
if (magic == SBIG('FONT') && version <= 4)
|
|
x0_initialized = true;
|
|
}
|
|
|
|
const CGlyph* CRasterFont::InternalGetGlyph(char16_t chr) const {
|
|
const auto iter =
|
|
std::find_if(xc_glyphs.cbegin(), xc_glyphs.cend(), [chr](const auto& entry) { return entry.first == chr; });
|
|
|
|
if (iter == xc_glyphs.cend()) {
|
|
return nullptr;
|
|
}
|
|
|
|
return &iter->second;
|
|
}
|
|
|
|
void CRasterFont::SinglePassDrawString(const CDrawStringOptions& opts, int x, int y, int& xout, int& yout,
|
|
CTextRenderBuffer* renderBuf, const char16_t* str, s32 length) const {
|
|
if (!x0_initialized)
|
|
return;
|
|
|
|
const char16_t* chr = str;
|
|
const CGlyph* prevGlyph = nullptr;
|
|
while (*chr != u'\0') {
|
|
const CGlyph* glyph = GetGlyph(*chr);
|
|
if (glyph) {
|
|
if (opts.x0_direction == ETextDirection::Horizontal) {
|
|
x += glyph->GetLeftPadding();
|
|
|
|
if (prevGlyph != nullptr) {
|
|
x += KernLookup(x1c_kerning, prevGlyph->GetKernStart(), *chr);
|
|
}
|
|
|
|
int left = 0;
|
|
int top = 0;
|
|
|
|
if (renderBuf) {
|
|
left += x;
|
|
top += y - glyph->GetBaseline();
|
|
renderBuf->AddCharacter(zeus::CVector2i(left, top), *chr, opts.x4_colors[2]);
|
|
}
|
|
x += glyph->GetRightPadding() + glyph->GetAdvance();
|
|
}
|
|
}
|
|
prevGlyph = glyph;
|
|
chr++;
|
|
if (length == -1)
|
|
continue;
|
|
|
|
if ((chr - str) >= length)
|
|
break;
|
|
}
|
|
|
|
xout = x;
|
|
yout = y;
|
|
}
|
|
|
|
void CRasterFont::DrawSpace(const CDrawStringOptions& opts, int x, int y, int& xout, int& yout, int len) const {
|
|
if (opts.x0_direction != ETextDirection::Horizontal)
|
|
return;
|
|
|
|
xout = x + len;
|
|
yout = y;
|
|
}
|
|
|
|
void CRasterFont::DrawString(const CDrawStringOptions& opts, int x, int y, int& xout, int& yout,
|
|
CTextRenderBuffer* renderBuf, const char16_t* str, int len) const {
|
|
if (!x0_initialized)
|
|
return;
|
|
|
|
if (renderBuf != nullptr) {
|
|
CGraphicsPalette pal(EPaletteFormat::RGB5A3, 4);
|
|
u16* data = pal.Lock();
|
|
data[0] = bswap16(zeus::CColor(0.f, 0.f, 0.f, 0.f).toRGB5A3());
|
|
data[1] = bswap16(opts.x4_colors[0].toRGB5A3());
|
|
data[2] = bswap16(opts.x4_colors[1].toRGB5A3());
|
|
data[3] = bswap16(zeus::CColor(0.f, 0.f, 0.f, 0.f).toRGB5A3());
|
|
pal.UnLock();
|
|
renderBuf->AddPaletteChange(pal);
|
|
}
|
|
|
|
SinglePassDrawString(opts, x, y, xout, yout, renderBuf, str, len);
|
|
}
|
|
|
|
void CRasterFont::GetSize(const CDrawStringOptions& opts, int& width, int& height, const char16_t* str, int len) const {
|
|
width = 0;
|
|
height = 0;
|
|
|
|
const char16_t* chr = str;
|
|
const CGlyph* prevGlyph = nullptr;
|
|
int prevWidth = 0;
|
|
while (*chr != u'\0') {
|
|
const CGlyph* glyph = GetGlyph(*chr);
|
|
|
|
if (glyph) {
|
|
if (opts.x0_direction == ETextDirection::Horizontal) {
|
|
int advance = 0;
|
|
if (prevGlyph)
|
|
advance = KernLookup(x1c_kerning, prevGlyph->GetKernStart(), *chr);
|
|
|
|
int curWidth = prevWidth + (glyph->GetLeftPadding() + glyph->GetAdvance() + glyph->GetRightPadding() + advance);
|
|
int curHeight = glyph->GetBaseline() - (x8_monoHeight + glyph->GetCellHeight());
|
|
|
|
width = curWidth;
|
|
prevWidth = curWidth;
|
|
|
|
if (curHeight > height)
|
|
height = curHeight;
|
|
}
|
|
}
|
|
|
|
prevGlyph = glyph;
|
|
chr++;
|
|
if (len == -1)
|
|
continue;
|
|
|
|
if ((chr - str) >= len)
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool CRasterFont::IsFinishedLoading() const {
|
|
if (!x80_texture || !x80_texture.IsLoaded())
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
void CRasterFont::SetupRenderState() {
|
|
constexpr std::array skDescList = {
|
|
GXVtxDescList{GX_VA_POS, GX_DIRECT},
|
|
GXVtxDescList{GX_VA_TEX0, GX_DIRECT},
|
|
GXVtxDescList{GX_VA_NULL, GX_NONE},
|
|
};
|
|
|
|
x80_texture->Load(GX_TEXMAP0, EClampMode::Clamp);
|
|
CGX::SetTevKAlphaSel(GX_TEVSTAGE0, GX_TEV_KASEL_K0_A);
|
|
CGX::SetTevKColorSel(GX_TEVSTAGE0, GX_TEV_KCSEL_K0);
|
|
CGX::SetTevColorIn(GX_TEVSTAGE0, GX_CC_ZERO, GX_CC_TEXC, GX_CC_KONST, GX_CC_ZERO);
|
|
CGX::SetTevAlphaIn(GX_TEVSTAGE0, GX_CA_ZERO, GX_CA_TEXA, GX_CA_KONST, GX_CA_ZERO);
|
|
CGX::SetStandardTevColorAlphaOp(GX_TEVSTAGE0);
|
|
CGX::SetTevDirect(GX_TEVSTAGE0);
|
|
CGX::SetVtxDescv(skDescList.data());
|
|
CGX::SetNumChans(0);
|
|
CGX::SetNumTexGens(1);
|
|
CGX::SetNumTevStages(1);
|
|
CGX::SetNumIndStages(0);
|
|
CGX::SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL);
|
|
CGX::SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY, false, GX_PTIDENTITY);
|
|
}
|
|
std::unique_ptr<IObj> FRasterFontFactory([[maybe_unused]] const SObjectTag& tag, CInputStream& in,
|
|
const CVParamTransfer& vparms, [[maybe_unused]] CObjectReference* selfRef) {
|
|
CSimplePool* sp = vparms.GetOwnedObj<CSimplePool*>();
|
|
return TToken<CRasterFont>::GetIObjObjectFor(std::make_unique<CRasterFont>(in, *sp));
|
|
}
|
|
|
|
} // namespace metaforce
|