metaforce/Runtime/GuiSys/CTextExecuteBuffer.cpp

470 lines
14 KiB
C++
Raw Normal View History

2016-03-19 03:58:01 +00:00
#include "CTextExecuteBuffer.hpp"
#include "CTextRenderBuffer.hpp"
#include "CFontRenderState.hpp"
#include "CFontImageDef.hpp"
#include "CInstruction.hpp"
2016-03-20 00:32:30 +00:00
#include "CRasterFont.hpp"
#include "CWordBreakTables.hpp"
2016-03-19 03:58:01 +00:00
#include "Graphics/CGraphicsPalette.hpp"
2016-03-20 06:37:08 +00:00
#include "Graphics/CTexture.hpp"
2016-03-19 03:58:01 +00:00
namespace urde
{
2017-01-29 03:58:16 +00:00
CTextRenderBuffer CTextExecuteBuffer::BuildRenderBuffer(CGuiWidget::EGuiModelDrawFlags df) const
2016-03-19 03:58:01 +00:00
{
2017-01-29 03:58:16 +00:00
CTextRenderBuffer ret(CTextRenderBuffer::EMode::AllocTally, df);
2016-03-19 03:58:01 +00:00
{
CFontRenderState rendState;
for (const std::shared_ptr<CInstruction>& inst : x0_instList)
inst->Invoke(rendState, &ret);
}
2016-03-22 08:15:00 +00:00
ret.SetMode(CTextRenderBuffer::EMode::BufferFill);
2016-03-19 03:58:01 +00:00
{
CFontRenderState rendState;
for (const std::shared_ptr<CInstruction>& inst : x0_instList)
inst->Invoke(rendState, &ret);
}
return ret;
}
2016-12-31 00:51:51 +00:00
CTextRenderBuffer CTextExecuteBuffer::BuildRenderBufferPage(InstList::const_iterator start,
InstList::const_iterator pgStart,
2017-01-29 03:58:16 +00:00
InstList::const_iterator pgEnd,
CGuiWidget::EGuiModelDrawFlags df) const
2016-12-31 00:51:51 +00:00
{
2017-01-29 03:58:16 +00:00
CTextRenderBuffer ret(CTextRenderBuffer::EMode::AllocTally, df);
2016-12-31 00:51:51 +00:00
{
CFontRenderState rendState;
for (auto it = start ; it != pgStart ; ++it)
{
const std::shared_ptr<CInstruction>& inst = *it;
inst->PageInvoke(rendState, &ret);
}
for (auto it = pgStart ; it != pgEnd ; ++it)
{
const std::shared_ptr<CInstruction>& inst = *it;
inst->Invoke(rendState, &ret);
}
}
ret.SetMode(CTextRenderBuffer::EMode::BufferFill);
{
CFontRenderState rendState;
for (auto it = start ; it != pgStart ; ++it)
{
const std::shared_ptr<CInstruction>& inst = *it;
inst->PageInvoke(rendState, &ret);
}
for (auto it = pgStart ; it != pgEnd ; ++it)
{
const std::shared_ptr<CInstruction>& inst = *it;
inst->Invoke(rendState, &ret);
}
}
return ret;
}
2017-01-29 03:58:16 +00:00
std::list<CTextRenderBuffer> CTextExecuteBuffer::BuildRenderBufferPages(const zeus::CVector2i& extent,
CGuiWidget::EGuiModelDrawFlags df) const
2016-12-31 00:51:51 +00:00
{
std::list<CTextRenderBuffer> ret;
for (auto it = x0_instList.begin() ; it != x0_instList.end() ;)
{
2017-01-29 03:58:16 +00:00
CTextRenderBuffer rbuf(CTextRenderBuffer::EMode::AllocTally, df);
2016-12-31 00:51:51 +00:00
{
CFontRenderState rstate;
for (auto it2 = x0_instList.begin() ; it2 != x0_instList.end() ; ++it2)
2016-12-31 00:51:51 +00:00
{
const std::shared_ptr<CInstruction>& inst2 = *it2;
inst2->Invoke(rstate, &rbuf);
}
}
rbuf.SetMode(CTextRenderBuffer::EMode::BufferFill);
InstList::const_iterator pageEnd = it;
{
CFontRenderState rstate;
bool seekingToPage = true;
for (auto it2 = x0_instList.begin() ; it2 != x0_instList.end() ; ++it2)
2016-12-31 00:51:51 +00:00
{
const std::shared_ptr<CInstruction>& inst2 = *it2;
if (it2 == it)
seekingToPage = false;
if (seekingToPage)
2016-12-31 00:51:51 +00:00
{
inst2->PageInvoke(rstate, &rbuf);
}
else
{
inst2->Invoke(rstate, &rbuf);
if (!rbuf.HasSpaceAvailable(zeus::CVector2i{}, extent))
break;
++pageEnd;
}
}
}
2017-01-29 03:58:16 +00:00
ret.push_back(BuildRenderBufferPage(x0_instList.cbegin(), it, pageEnd, df));
2016-12-31 00:51:51 +00:00
it = pageEnd;
}
return ret;
}
2016-03-20 00:32:30 +00:00
std::vector<CToken> CTextExecuteBuffer::GetAssets() const
{
size_t totalAssets = 0;
for (const std::shared_ptr<CInstruction>& inst : x0_instList)
totalAssets += inst->GetAssetCount();
std::vector<CToken> ret;
ret.reserve(totalAssets);
for (const std::shared_ptr<CInstruction>& inst : x0_instList)
inst->GetAssets(ret);
return ret;
}
2017-01-24 07:41:33 +00:00
void CTextExecuteBuffer::AddString(const char16_t* str, int count)
2016-03-20 00:32:30 +00:00
{
2016-12-16 23:05:29 +00:00
if (!xa4_curLine)
2016-03-20 00:32:30 +00:00
StartNewLine();
2017-01-24 07:41:33 +00:00
const char16_t* charCur = str;
const char16_t* wordCur = str;
2016-03-20 00:32:30 +00:00
for (int ac=0 ; *charCur && (ac < count || count == -1) ; ++charCur, ++ac)
{
2017-01-24 07:41:33 +00:00
if (*charCur == u'\n' || *charCur == u' ')
2016-03-20 00:32:30 +00:00
{
AddStringFragment(wordCur, charCur - wordCur);
wordCur = charCur + 1;
2017-01-24 07:41:33 +00:00
if (*charCur == u'\n')
2016-03-20 00:32:30 +00:00
{
StartNewLine();
}
else
{
StartNewWord();
int w, h;
2017-01-24 07:41:33 +00:00
char16_t space = u' ';
2017-01-30 04:16:20 +00:00
x18_textState.x48_font->GetSize(x18_textState.x0_drawStrOpts, w, h, &space, 1);
if (xa0_curBlock->x14_dir == ETextDirection::Horizontal)
2016-03-20 00:32:30 +00:00
{
2016-12-16 23:05:29 +00:00
xa4_curLine->x8_curX += w;
xbc_spaceDistance = w;
2016-03-20 00:32:30 +00:00
}
else
{
2016-12-16 23:05:29 +00:00
xa4_curLine->xc_curY += h;
xbc_spaceDistance = h;
2016-03-20 00:32:30 +00:00
}
}
}
}
if (charCur > wordCur)
AddStringFragment(wordCur, charCur - wordCur);
}
2017-01-24 07:41:33 +00:00
void CTextExecuteBuffer::AddStringFragment(const char16_t* str, int len)
2016-03-20 00:32:30 +00:00
{
2017-01-30 04:16:20 +00:00
if (xa0_curBlock->x14_dir == ETextDirection::Horizontal)
2016-03-20 00:32:30 +00:00
for (int i=0 ; i<len ;)
i += WrapOneLTR(str + i, len - i);
}
2017-01-24 07:41:33 +00:00
int CTextExecuteBuffer::WrapOneLTR(const char16_t* str, int len)
2016-03-20 00:32:30 +00:00
{
2016-12-16 23:05:29 +00:00
if (!x18_textState.x48_font)
2016-03-20 00:32:30 +00:00
return len;
2016-12-16 23:05:29 +00:00
CRasterFont* font = x18_textState.x48_font.GetObj();
2016-03-20 00:32:30 +00:00
int rem = len;
int w, h;
2017-01-30 04:16:20 +00:00
x18_textState.x48_font->GetSize(x18_textState.x0_drawStrOpts, w, h, str, len);
2016-03-20 00:32:30 +00:00
2016-12-16 23:05:29 +00:00
if (x18_textState.x7c_enableWordWrap)
2016-03-20 00:32:30 +00:00
{
2016-12-16 23:05:29 +00:00
if (w + xa4_curLine->x8_curX > xa0_curBlock->xc_blockExtentX &&
2018-01-06 06:50:42 +00:00
xa4_curLine->x4_wordCount >= 1 &&
2016-12-16 23:05:29 +00:00
xb0_curX + w < xa0_curBlock->xc_blockExtentX)
2016-03-20 00:32:30 +00:00
{
MoveWordLTR();
}
2016-12-16 23:05:29 +00:00
if (w + xa4_curLine->x8_curX > xa0_curBlock->xc_blockExtentX && len > 1)
2016-03-20 00:32:30 +00:00
{
2017-01-24 07:41:33 +00:00
const char16_t* strEnd = str + len;
2016-03-20 00:32:30 +00:00
int aRank = 5;
do
{
--rem;
--strEnd;
int endRank = 4;
if (len > 2)
endRank = CWordBreakTables::GetEndRank(*(strEnd - 1));
int beginRank = CWordBreakTables::GetBeginRank(*strEnd);
if (endRank < aRank && endRank <= beginRank)
{
aRank = endRank;
}
else
{
2017-01-30 04:16:20 +00:00
x18_textState.x48_font->GetSize(x18_textState.x0_drawStrOpts, w, h, str, rem);
2016-03-20 00:32:30 +00:00
}
2016-12-16 23:05:29 +00:00
} while (w + xa4_curLine->x8_curX > xa0_curBlock->xc_blockExtentX && rem > 1);
2016-03-20 00:32:30 +00:00
}
}
2016-12-16 23:05:29 +00:00
xac_curY = std::max(xac_curY, font->GetMonoHeight());
2016-03-20 00:32:30 +00:00
2016-12-16 23:05:29 +00:00
xa4_curLine->TestLargestFont(font->GetMonoWidth(),
2016-03-20 00:32:30 +00:00
font->GetMonoHeight(),
font->GetBaseline());
2016-12-16 23:05:29 +00:00
xa4_curLine->x8_curX += w;
xa0_curBlock->x2c_lineX = std::max(xa0_curBlock->x2c_lineX, xa4_curLine->x8_curX);
xb0_curX += w;
2016-03-20 00:32:30 +00:00
x0_instList.emplace(x0_instList.cend(), std::make_shared<CTextInstruction>(str, rem));
2016-03-20 00:32:30 +00:00
if (rem != len)
StartNewLine();
return rem;
}
void CTextExecuteBuffer::MoveWordLTR()
{
2017-12-21 03:19:54 +00:00
xa4_curLine->x8_curX -= (xb0_curX + xbc_spaceDistance);
xa4_curLine->xc_curY = std::min(xa4_curLine->xc_curY, xb8_curWordY);
2016-12-16 23:05:29 +00:00
xbc_spaceDistance = 0;
--xa4_curLine->x4_wordCount;
2016-03-20 00:32:30 +00:00
TerminateLineLTR();
2016-12-16 23:05:29 +00:00
xa4_curLine = static_cast<CLineInstruction*>(x0_instList.emplace(xa8_curWordIt,
2017-01-30 04:16:20 +00:00
std::make_shared<CLineInstruction>(x18_textState.x80_just, x18_textState.x84_vjust, xc0_imageBaseline))->get());
2016-03-20 00:32:30 +00:00
2017-12-21 03:19:54 +00:00
// Dunno what's up with this in the original; seems fine without
//x0_instList.emplace(xa8_curWordIt, std::make_shared<CWordInstruction>());
2016-03-20 00:32:30 +00:00
2016-12-16 23:05:29 +00:00
++xa0_curBlock->x34_lineCount;
2016-03-20 00:32:30 +00:00
}
2016-03-19 03:58:01 +00:00
void CTextExecuteBuffer::StartNewLine()
{
2016-12-16 23:05:29 +00:00
if (xa4_curLine)
2016-03-19 03:58:01 +00:00
TerminateLine();
2016-12-16 23:05:29 +00:00
xa8_curWordIt = x0_instList.emplace(x0_instList.cend(),
2017-01-30 04:16:20 +00:00
std::make_shared<CLineInstruction>(x18_textState.x80_just, x18_textState.x84_vjust, xc0_imageBaseline));
xa4_curLine = static_cast<CLineInstruction*>(xa8_curWordIt->get());
2016-12-16 23:05:29 +00:00
xbc_spaceDistance = 0;
2016-03-19 03:58:01 +00:00
StartNewWord();
2016-12-16 23:05:29 +00:00
++xa0_curBlock->x34_lineCount;
2016-03-20 00:32:30 +00:00
}
void CTextExecuteBuffer::StartNewWord()
{
xa8_curWordIt = x0_instList.emplace(x0_instList.cend(), std::make_shared<CWordInstruction>());
2016-12-16 23:05:29 +00:00
xb0_curX = 0;
xac_curY = 0;
xb4_curWordX = xa4_curLine->x8_curX;
xb8_curWordY = xa4_curLine->xc_curY;
2016-12-16 23:05:29 +00:00
++xa4_curLine->x4_wordCount;
2016-03-20 00:32:30 +00:00
}
void CTextExecuteBuffer::TerminateLine()
{
2017-01-30 04:16:20 +00:00
if (xa0_curBlock->x14_dir == ETextDirection::Horizontal)
2016-03-20 00:32:30 +00:00
TerminateLineLTR();
}
void CTextExecuteBuffer::TerminateLineLTR()
{
if (!xa4_curLine->xc_curY /*&& x18_textState.IsFinishedLoading()*/)
2016-03-20 00:32:30 +00:00
{
2017-01-30 04:16:20 +00:00
xa4_curLine->xc_curY = std::max(xa4_curLine->GetHeight(),
x18_textState.x48_font->GetCarriageAdvance());
2016-03-20 00:32:30 +00:00
}
2016-12-16 23:05:29 +00:00
if (xa0_curBlock->x1c_vertJustification == EVerticalJustification::Full)
2016-03-20 00:32:30 +00:00
{
2016-12-16 23:05:29 +00:00
xa0_curBlock->x30_lineY += xa4_curLine->xc_curY;
2016-03-20 00:32:30 +00:00
}
else
{
2016-12-16 23:05:29 +00:00
xa0_curBlock->x30_lineY += x18_textState.x78_extraLineSpace +
xa4_curLine->xc_curY * x18_textState.x74_lineSpacing;
2016-03-20 00:32:30 +00:00
}
}
void CTextExecuteBuffer::AddPopState()
{
x0_instList.emplace(x0_instList.cend(), std::make_shared<CPopStateInstruction>());
2016-03-20 00:32:30 +00:00
2016-12-16 23:05:29 +00:00
x18_textState = xc4_stateStack.back();
xc4_stateStack.pop_back();
2016-03-20 00:32:30 +00:00
2016-12-16 23:05:29 +00:00
if (!xa4_curLine->x8_curX)
2016-03-20 00:32:30 +00:00
{
2017-01-30 04:16:20 +00:00
xa4_curLine->x28_just = x18_textState.x80_just;
xa4_curLine->x2c_vjust = x18_textState.x84_vjust;
2016-03-20 00:32:30 +00:00
}
}
void CTextExecuteBuffer::AddPushState()
{
x0_instList.emplace(x0_instList.cend(), std::make_shared<CPushStateInstruction>());
2016-12-16 23:05:29 +00:00
xc4_stateStack.push_back(x18_textState);
2016-03-20 00:32:30 +00:00
}
void CTextExecuteBuffer::AddVerticalJustification(EVerticalJustification vjust)
{
2016-12-16 23:05:29 +00:00
x18_textState.x84_vjust = vjust;
if (!xa4_curLine)
2016-03-20 00:32:30 +00:00
return;
2016-12-16 23:05:29 +00:00
if (xa4_curLine->x8_curX)
2016-03-20 00:32:30 +00:00
return;
2017-01-30 04:16:20 +00:00
xa4_curLine->x2c_vjust = vjust;
2016-03-20 00:32:30 +00:00
}
void CTextExecuteBuffer::AddJustification(EJustification just)
{
2016-12-16 23:05:29 +00:00
x18_textState.x80_just = just;
if (!xa4_curLine)
2016-03-20 00:32:30 +00:00
return;
2016-12-16 23:05:29 +00:00
if (xa4_curLine->x8_curX)
2016-03-20 00:32:30 +00:00
return;
2017-01-30 04:16:20 +00:00
xa4_curLine->x28_just = just;
2016-03-20 00:32:30 +00:00
}
void CTextExecuteBuffer::AddLineExtraSpace(s32 space)
{
x0_instList.emplace(x0_instList.cend(), std::make_shared<CLineExtraSpaceInstruction>(space));
2016-12-16 23:05:29 +00:00
x18_textState.x78_extraLineSpace = space;
2016-03-20 00:32:30 +00:00
}
void CTextExecuteBuffer::AddLineSpacing(float spacing)
{
x0_instList.emplace(x0_instList.cend(), std::make_shared<CLineSpacingInstruction>(spacing));
2016-12-16 23:05:29 +00:00
x18_textState.x74_lineSpacing = spacing;
2016-03-20 00:32:30 +00:00
}
void CTextExecuteBuffer::AddRemoveColorOverride(int idx)
{
x0_instList.emplace(x0_instList.cend(), std::make_shared<CRemoveColorOverrideInstruction>(idx));
2016-03-20 00:32:30 +00:00
}
void CTextExecuteBuffer::AddColorOverride(int idx, const CTextColor& color)
{
x0_instList.emplace(x0_instList.cend(), std::make_shared<CColorOverrideInstruction>(idx, color));
2016-03-20 00:32:30 +00:00
}
void CTextExecuteBuffer::AddColor(EColorType tp, const CTextColor& color)
{
x0_instList.emplace(x0_instList.cend(), std::make_shared<CColorInstruction>(tp, color));
2016-03-20 00:32:30 +00:00
}
void CTextExecuteBuffer::AddImage(const CFontImageDef& image)
{
2016-12-16 23:05:29 +00:00
if (!xa4_curLine)
2016-03-20 00:32:30 +00:00
StartNewLine();
2016-12-16 23:05:29 +00:00
if (xa0_curBlock)
2016-03-20 00:32:30 +00:00
{
2016-03-20 06:37:08 +00:00
const CTexture* tex = image.x4_texs[0].GetObj();
2017-01-30 06:58:59 +00:00
int width = tex->GetWidth() * image.x14_cropFactor.x;
int height = tex->GetHeight() * image.x14_cropFactor.y;
2016-03-20 06:37:08 +00:00
2017-11-18 03:35:37 +00:00
if (x18_textState.x7c_enableWordWrap &&
xa4_curLine->x8_curX + width > xa0_curBlock->xc_blockExtentX && xa4_curLine->x4_wordCount > 1)
2017-01-30 04:16:20 +00:00
StartNewLine();
xa4_curLine->TestLargestImage(width, height, image.CalculateBaseline());
xa4_curLine->x8_curX += width;
if (xa4_curLine->x8_curX > width)
xa0_curBlock->x2c_lineX = xa4_curLine->x8_curX;
2016-03-20 00:32:30 +00:00
}
2016-03-20 06:37:08 +00:00
x0_instList.emplace(x0_instList.cend(), std::make_shared<CImageInstruction>(image));
2016-03-20 00:32:30 +00:00
}
2016-03-21 00:25:53 +00:00
void CTextExecuteBuffer::AddFont(const TToken<CRasterFont>& font)
{
x0_instList.emplace(x0_instList.cend(), std::make_shared<CFontInstruction>(font));
2016-12-16 23:05:29 +00:00
x18_textState.x48_font = font;
2016-03-21 00:25:53 +00:00
2016-12-16 23:05:29 +00:00
if (xa0_curBlock)
xa0_curBlock->TestLargestFont(font->GetMonoWidth(),
2016-03-21 00:25:53 +00:00
font->GetMonoHeight(),
font->GetBaseline());
2016-12-16 23:05:29 +00:00
if (xa4_curLine)
xa4_curLine->TestLargestFont(font->GetMonoWidth(),
2016-03-21 00:25:53 +00:00
font->GetMonoHeight(),
font->GetBaseline());
}
2016-03-20 00:32:30 +00:00
void CTextExecuteBuffer::EndBlock()
{
2016-12-16 23:05:29 +00:00
if (xa4_curLine)
2016-03-20 06:37:08 +00:00
TerminateLine();
2016-12-16 23:05:29 +00:00
xa4_curLine = nullptr;
xa0_curBlock = nullptr;
2016-03-20 00:32:30 +00:00
}
2017-01-30 04:16:20 +00:00
void CTextExecuteBuffer::BeginBlock(s32 offX, s32 offY, s32 extX, s32 extY,
bool imageBaseline, ETextDirection dir, EJustification just,
2016-03-20 06:37:08 +00:00
EVerticalJustification vjust)
2016-03-20 00:32:30 +00:00
{
2017-01-30 04:16:20 +00:00
xc0_imageBaseline = imageBaseline;
xa0_curBlock = static_cast<CBlockInstruction*>(x0_instList.emplace(x0_instList.cend(),
2017-01-30 04:16:20 +00:00
std::make_shared<CBlockInstruction>(offX, offY, extX, extY, dir, just, vjust))->get());
2016-03-20 06:37:08 +00:00
2016-12-16 23:05:29 +00:00
if (x18_textState.x48_font)
2016-03-20 06:37:08 +00:00
{
2016-12-16 23:05:29 +00:00
CRasterFont* font = x18_textState.x48_font.GetObj();
2016-03-20 06:37:08 +00:00
s32 baseline = font->GetBaseline();
s32 monoH = font->GetMonoHeight();
s32 monoW = font->GetMonoWidth();
xa0_curBlock->TestLargestFont(monoW, monoH, baseline);
2016-03-20 06:37:08 +00:00
}
x18_textState.x0_drawStrOpts.x0_direction = dir;
2016-12-16 23:05:29 +00:00
x18_textState.x80_just = just;
x18_textState.x84_vjust = vjust;
2016-03-19 03:58:01 +00:00
}
void CTextExecuteBuffer::Clear()
{
x0_instList.clear();
2016-03-20 00:32:30 +00:00
x18_textState = CSaveableState();
2016-12-16 23:05:29 +00:00
xa0_curBlock = nullptr;
xa4_curLine = nullptr;
xa8_curWordIt = x0_instList.begin();
xb4_curWordX = 0;
xb8_curWordY = 0;
2016-12-16 23:05:29 +00:00
xbc_spaceDistance = 0;
2016-03-19 03:58:01 +00:00
}
}