port(EterLib): TextBar/DibBar/BlockTexture for uiTip TipBoard/BigBoard

TextBar and DibBar verbatim over the FreeType GDI shim (+GetTextExtentPoint32A);
BlockTexture verbatim except Render, which records the clipped quad as a UI
image of the device CPU texture (UIRenderAddImageNamed). Notices now render;
port.text_bar covers block split, pixels, colour, ClearBar and TipBoard clip.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
shenlei
2026-09-30 13:07:24 +09:00
co-authored by Claude Opus 5.5
parent c7bcb9bbbe
commit 6d2d262de8
14 changed files with 900 additions and 87 deletions
+185
View File
@@ -0,0 +1,185 @@
// 40250 EterLib/BlockTexture.cpp. Verbatim except Render: the texture is the device's CPU texture
// (CreateTexture/LockRect as 40250), and the quad 40250 draws with SetPDTStream + DrawIndexedPrimitive
// in the UI pass is recorded as a UI image of that texture with the same corners and UVs.
#include "EterLib/StdAfx.h"
#include "EterLib/BlockTexture.h"
#include "EterLib/GrpBase.h"
#include "EterLib/GrpDIB.h"
#include "EterBase/Stl.h"
#include "EterLib/StateManager.h"
#include "UIRenderCommands.h"
void CBlockTexture::SetClipRect(const RECT & c_rRect)
{
m_bClipEnable = TRUE;
m_clipRect = c_rRect;
}
void CBlockTexture::Render(int ix, int iy)
{
int isx = ix + m_rect.left;
int isy = iy + m_rect.top;
int iex = ix + m_rect.left + m_dwWidth;
int iey = iy + m_rect.top + m_dwHeight;
float su = 0.0f;
float sv = 0.0f;
float eu = 1.0f;
float ev = 1.0f;
if (m_bClipEnable)
{
if (isx > m_clipRect.right)
return;
if (iex < m_clipRect.left)
return;
if (isy > m_clipRect.bottom)
return;
if (iey < m_clipRect.top)
return;
if (m_clipRect.left > isx)
{
int idx = m_clipRect.left - isx;
isx += idx;
su += float(idx) / float(m_dwWidth);
}
if (iex > m_clipRect.right)
{
int idx = iex - m_clipRect.right;
iex -= idx;
eu -= float(idx) / float(m_dwWidth);
}
if (m_clipRect.top > isy)
{
int idy = m_clipRect.top - isy;
isy += idy;
sv += float(idy) / float(m_dwHeight);
}
if (iey > m_clipRect.bottom)
{
int idy = iey - m_clipRect.bottom;
iey -= idy;
ev -= float(idy) / float(m_dwHeight);
}
}
TPDTVertex vertices[4];
vertices[0].position.x = isx - 0.5f;
vertices[0].position.y = isy - 0.5f;
vertices[0].position.z = 0.0f;
vertices[0].texCoord = TTextureCoordinate(su, sv);
vertices[0].diffuse = 0xffffffff;
vertices[1].position.x = iex - 0.5f;
vertices[1].position.y = isy - 0.5f;
vertices[1].position.z = 0.0f;
vertices[1].texCoord = TTextureCoordinate(eu, sv);
vertices[1].diffuse = 0xffffffff;
vertices[2].position.x = isx - 0.5f;
vertices[2].position.y = iey - 0.5f;
vertices[2].position.z = 0.0f;
vertices[2].texCoord = TTextureCoordinate(su, ev);
vertices[2].diffuse = 0xffffffff;
vertices[3].position.x = iex - 0.5f;
vertices[3].position.y = iey - 0.5f;
vertices[3].position.z = 0.0f;
vertices[3].texCoord = TTextureCoordinate(eu, ev);
vertices[3].diffuse = 0xffffffff;
// PORT: 40250 sets the stream, stage-0 texture and FVF and draws the fill-rect triangle list.
float x[4], y[4];
for (int i = 0; i < 4; ++i)
{
x[i] = vertices[i].position.x;
y[i] = vertices[i].position.y;
}
UIRenderAddImageNamed(UIRenderTextureNameFromHandle(m_lpd3dTexture), x, y, su, sv, eu, ev, vertices[0].diffuse);
}
void CBlockTexture::InvalidateRect(const RECT & c_rsrcRect)
{
RECT dstRect = m_rect;
if (c_rsrcRect.right < dstRect.left ||
c_rsrcRect.left > dstRect.right ||
c_rsrcRect.bottom < dstRect.top ||
c_rsrcRect.top > dstRect.bottom)
{
Tracef("InvalidateRect() - Strange rect");
return;
}
// DIBBAR_LONGSIZE_BUGFIX
const RECT clipRect = {
max(c_rsrcRect.left - dstRect.left, 0),
max(c_rsrcRect.top - dstRect.top, 0),
min(c_rsrcRect.right - dstRect.left, dstRect.right - dstRect.left),
min(c_rsrcRect.bottom - dstRect.top, dstRect.bottom - dstRect.top),
};
// END_OF_DIBBAR_LONGSIZE_BUGFIX
DWORD * pdwSrc;
pdwSrc = (DWORD *)m_pDIB->GetPointer();
pdwSrc += dstRect.left + dstRect.top*m_pDIB->GetWidth();
D3DLOCKED_RECT lockedRect;
if (FAILED(m_lpd3dTexture->LockRect(0, &lockedRect, &clipRect, 0)))
{
Tracef("InvalidateRect() - Failed to LockRect");
return;
}
int iclipWidth = clipRect.right - clipRect.left;
int iclipHeight = clipRect.bottom - clipRect.top;
DWORD * pdwDst = (DWORD *)lockedRect.pBits;
DWORD dwDstWidth = lockedRect.Pitch>>2;
DWORD dwSrcWidth = m_pDIB->GetWidth();
for (int i = 0; i < iclipHeight; ++i)
{
for (int i = 0; i < iclipWidth; ++i)
{
if (pdwSrc[i])
pdwDst[i] = pdwSrc[i] | 0xff000000;
else
pdwDst[i] = 0;
}
pdwDst += dwDstWidth;
pdwSrc += dwSrcWidth;
}
m_lpd3dTexture->UnlockRect(0);
}
bool CBlockTexture::Create(CGraphicDib * pDIB, const RECT & c_rRect, DWORD dwWidth, DWORD dwHeight)
{
if (FAILED(ms_lpd3dDevice->CreateTexture(dwWidth, dwHeight, 0, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &m_lpd3dTexture)))
{
Tracef("Failed to create block texture %u, %u\n", dwWidth, dwHeight);
return false;
}
m_pDIB = pDIB;
m_rect = c_rRect;
m_dwWidth = dwWidth;
m_dwHeight = dwHeight;
m_bClipEnable = FALSE;
return true;
}
CBlockTexture::CBlockTexture()
{
m_pDIB = NULL;
m_lpd3dTexture = NULL;
}
CBlockTexture::~CBlockTexture()
{
safe_release(m_lpd3dTexture);
m_lpd3dTexture = NULL;
}
+156 -54
View File
@@ -1,64 +1,166 @@
// Platform skeleton for EterLib/DibBar.h (40250 EterLib/DibBar.cpp), generated by platform_stub.py.
// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation.
// 40250 EterLib/DibBar.cpp, verbatim (RECT initialisers cast to LONG).
#include "EterLib/StdAfx.h"
#include "EterLib/DibBar.h"
#include "EterLib/BlockTexture.h"
#include "../PlatformStub.h"
void CDibBar::Invalidate()
{
// PORT: RECT holds the 32-bit Win32 LONG, so static_cast<long> would narrow here.
RECT rect = { static_cast<LONG>(0), static_cast<LONG>(0), static_cast<LONG>(m_dwWidth), static_cast<LONG>(m_dwHeight)};
std::vector<CBlockTexture *>::iterator itor = m_kVec_pkBlockTexture.begin();
for (; itor != m_kVec_pkBlockTexture.end(); ++itor)
{
CBlockTexture * pTexture = *itor;
pTexture->InvalidateRect(rect);
}
}
void CDibBar::SetClipRect(const RECT & c_rRect)
{
std::vector<CBlockTexture *>::iterator itor = m_kVec_pkBlockTexture.begin();
for (; itor != m_kVec_pkBlockTexture.end(); ++itor)
{
CBlockTexture * pTexture = *itor;
assert(pTexture);
if (!pTexture)
continue;
pTexture->SetClipRect(c_rRect);
}
}
void CDibBar::ClearBar()
{
DWORD * pdwBuf = (DWORD *)m_dib.GetPointer();
memset(pdwBuf, 0, m_dib.GetWidth()*m_dib.GetHeight()*4);
Invalidate();
}
void CDibBar::Render(int ix, int iy)
{
std::vector<CBlockTexture *>::iterator itor = m_kVec_pkBlockTexture.begin();
for (; itor != m_kVec_pkBlockTexture.end(); ++itor)
{
CBlockTexture * pTexture = *itor;
pTexture->Render(ix, iy);
}
}
DWORD CDibBar::__NearTextureSize(DWORD dwSize)
{
if ((dwSize & (dwSize-1)) == 0)
return dwSize;
DWORD dwRet = 2;
while (dwRet < dwSize)
{
dwRet <<= 1;
}
return dwRet;
}
void CDibBar::__DivideTextureSize(DWORD dwSize, DWORD dwMax, DWORD * pdwxStep, DWORD * pdwxCount, DWORD * pdwxRest)
{
if (dwSize<dwMax)
{
*pdwxStep = dwMax;
*pdwxCount = 0;
*pdwxRest = dwSize%dwMax;
return;
}
*pdwxStep = dwMax;
*pdwxCount = dwSize/dwMax;
*pdwxRest = dwSize%dwMax;
}
CBlockTexture * CDibBar::__BuildTextureBlock(DWORD dwxPos, DWORD dwyPos, DWORD dwImageWidth, DWORD dwImageHeight, DWORD dwTextureWidth, DWORD dwTextureHeight)
{
if (dwTextureWidth == 0 || dwTextureHeight == 0)
return NULL;
// PORT: RECT holds the 32-bit Win32 LONG, so static_cast<long> would narrow here.
RECT posRect = { static_cast<LONG>(dwxPos), static_cast<LONG>(dwyPos), static_cast<LONG>(dwxPos+dwImageWidth), static_cast<LONG>(dwyPos+dwImageHeight)};
CBlockTexture * pBlockTexture = new CBlockTexture;
if (!pBlockTexture->Create(&m_dib, posRect, dwTextureWidth, dwTextureHeight))
{
delete pBlockTexture;
return NULL;
}
return pBlockTexture;
}
void CDibBar::__BuildTextureBlockList(DWORD dwWidth, DWORD dwHeight, DWORD dwMax)
{
DWORD dwxStep, dwyStep;
DWORD dwxCount, dwyCount;
DWORD dwxRest, dwyRest;
__DivideTextureSize(dwWidth, dwMax, &dwxStep, &dwxCount, &dwxRest);
__DivideTextureSize(dwHeight, dwMax, &dwyStep, &dwyCount, &dwyRest);
DWORD dwxTexRest = __NearTextureSize(dwxRest);
DWORD dwyTexRest = __NearTextureSize(dwyRest);
for (DWORD y = 0; y < dwyCount; ++y)
{
for (DWORD x = 0; x < dwxCount; ++x)
{
CBlockTexture * pTexture = __BuildTextureBlock(x*dwxStep, y*dwyStep,
dwxStep, dwyStep,
dwMax, dwMax);
if (pTexture)
m_kVec_pkBlockTexture.push_back(pTexture);
}
CBlockTexture * pTexture = __BuildTextureBlock(dwxCount*dwxStep, y*dwyStep,
dwxRest, dwyStep,
dwxTexRest, dwMax);
if (pTexture)
m_kVec_pkBlockTexture.push_back(pTexture);
}
for (DWORD x = 0; x < dwxCount; ++x)
{
CBlockTexture * pTexture = __BuildTextureBlock(x*dwxStep, dwyCount*dwyStep,
dwxStep, dwyRest,
dwMax, dwyTexRest);
if (pTexture)
m_kVec_pkBlockTexture.push_back(pTexture);
}
if (dwxRest > 0 && dwyRest > 0)
{
CBlockTexture * pTexture = __BuildTextureBlock(dwxCount*dwxStep, dwyCount*dwyStep,
dwxRest, dwyRest,
dwxTexRest, dwyTexRest);
if (pTexture)
m_kVec_pkBlockTexture.push_back(pTexture);
}
}
bool CDibBar::Create(HDC hdc, DWORD dwWidth, DWORD dwHeight)
{
if (!m_dib.Create(hdc, dwWidth, dwHeight))
{
Tracef(" Failed to create CDibBar\n");
return false;
}
m_dwWidth = dwWidth;
m_dwHeight = dwHeight;
__BuildTextureBlockList(dwWidth, dwHeight);
OnCreate();
return true;
}
CDibBar::CDibBar()
{
MT_PLATFORM_STUB();
}
CDibBar::~CDibBar()
{
MT_PLATFORM_STUB();
}
auto CDibBar::Create(HDC, DWORD, DWORD) -> bool
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<bool>();
}
auto CDibBar::Invalidate() -> void
{
MT_PLATFORM_STUB();
}
auto CDibBar::SetClipRect(const RECT &) -> void
{
MT_PLATFORM_STUB();
}
auto CDibBar::ClearBar() -> void
{
MT_PLATFORM_STUB();
}
auto CDibBar::Render(int, int) -> void
{
MT_PLATFORM_STUB();
}
auto CDibBar::__NearTextureSize(DWORD) -> DWORD
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<DWORD>();
}
auto CDibBar::__DivideTextureSize(DWORD, DWORD, DWORD *, DWORD *, DWORD *) -> void
{
MT_PLATFORM_STUB();
}
auto CDibBar::__BuildTextureBlock(DWORD, DWORD, DWORD, DWORD, DWORD, DWORD) -> CBlockTexture *
{
MT_PLATFORM_STUB();
return mt_platform_stub_return<CBlockTexture *>();
}
auto CDibBar::__BuildTextureBlockList(DWORD, DWORD, DWORD) -> void
{
MT_PLATFORM_STUB();
}
+68 -32
View File
@@ -1,41 +1,77 @@
// Platform skeleton for EterLib/TextBar.h (40250 EterLib/TextBar.cpp), generated by platform_stub.py.
// Every MT_PLATFORM_STUB() body is unimplemented: replace it with the platform implementation.
// 40250 EterLib/TextBar.cpp, verbatim.
#include "EterLib/StdAfx.h"
#include "EterLib/TextBar.h"
#include "EterLib/Util.h"
#include "../PlatformStub.h"
CTextBar::CTextBar(int, bool)
void CTextBar::__SetFont(int fontSize, bool isBold)
{
MT_PLATFORM_STUB();
int iCodePage = GetDefaultCodePage();
LOGFONT logFont;
memset(&logFont, 0, sizeof(LOGFONT));
logFont.lfHeight = fontSize;
logFont.lfEscapement = 0;
logFont.lfOrientation = 0;
if (isBold)
logFont.lfWeight = FW_BOLD;
else
logFont.lfWeight = FW_NORMAL;
logFont.lfItalic = FALSE;
logFont.lfUnderline = FALSE;
logFont.lfStrikeOut = FALSE;
logFont.lfCharSet = GetCharsetFromCodePage(iCodePage);
logFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
logFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
logFont.lfQuality = ANTIALIASED_QUALITY;
logFont.lfPitchAndFamily = DEFAULT_PITCH;
strcpy(logFont.lfFaceName, GetFontFaceFromCodePage(iCodePage));
m_hFont = CreateFontIndirect(&logFont);
HDC hdc = m_dib.GetDCHandle();
m_hOldFont = (HFONT)SelectObject(hdc, m_hFont);
}
void CTextBar::SetTextColor(int r, int g, int b)
{
HDC hDC = m_dib.GetDCHandle();
::SetTextColor(hDC, RGB(r, g, b));
}
void CTextBar::GetTextExtent(const char * c_szText, SIZE* p_size)
{
HDC hDC = m_dib.GetDCHandle();
GetTextExtentPoint32(hDC, c_szText, strlen(c_szText), p_size);
}
void CTextBar::TextOut(int ix, int iy, const char * c_szText)
{
m_dib.TextOut(ix, iy, c_szText);
Invalidate();
}
void CTextBar::OnCreate()
{
m_dib.SetBkMode(TRANSPARENT);
__SetFont(m_fontSize, m_isBold);
}
CTextBar::CTextBar(int fontSize, bool isBold)
{
m_hOldFont = NULL;
m_fontSize = fontSize;
m_isBold = isBold;
}
CTextBar::~CTextBar()
{
MT_PLATFORM_STUB();
}
auto CTextBar::TextOut(int, int, const char *) -> void
{
MT_PLATFORM_STUB();
}
auto CTextBar::SetTextColor(int, int, int) -> void
{
MT_PLATFORM_STUB();
}
auto CTextBar::GetTextExtent(const char *, SIZE *) -> void
{
MT_PLATFORM_STUB();
}
auto CTextBar::__SetFont(int, bool) -> void
{
MT_PLATFORM_STUB();
}
auto CTextBar::OnCreate() -> void
{
MT_PLATFORM_STUB();
HDC hdc = m_dib.GetDCHandle();
SelectObject(hdc, m_hOldFont);
}
+6 -1
View File
@@ -104,6 +104,11 @@ void UIRenderRestoreClip() {
void UIRenderAddImage(const CGraphicTexture* texture, const float x[4], const float y[4],
float su, float sv, float eu, float ev, std::uint32_t argb, int blend) {
UIRenderAddImageNamed(UIRenderTextureName(texture), x, y, su, sv, eu, ev, argb, blend);
}
void UIRenderAddImageNamed(const std::string& texture_name, const float x[4], const float y[4],
float su, float sv, float eu, float ev, std::uint32_t argb, int blend) {
// 40250 draws the image quad through the device: the UI pass's world transform places it
// (identity except where a caller sets one, e.g. CPythonMiniMap::RenderAtlas's window offset) and a
// stage-0 MODULATE with D3DTA_TFACTOR tints it (the atlas's NPC/warp/waypoint marks).
@@ -139,7 +144,7 @@ void UIRenderAddImage(const CGraphicTexture* texture, const float x[4], const fl
command.y1 = std::min({command.qy[0], command.qy[1], command.qy[2], command.qy[3]});
command.x2 = std::max({command.qx[0], command.qx[1], command.qx[2], command.qx[3]});
command.y2 = std::max({command.qy[0], command.qy[1], command.qy[2], command.qy[3]});
command.text = UIRenderTextureName(texture);
command.text = texture_name;
command.quad = true;
command.su = su; command.sv = sv; command.eu = eu; command.ev = ev;
command.blend = blend;
+3
View File
@@ -63,6 +63,9 @@ bool MtCpuMemoryTexture(const std::string& name, UIMemoryTexture* out);
// Godot canvas puts them on .5, so the quad is shifted back by +0.5 here.
void UIRenderAddImage(const CGraphicTexture* texture, const float x[4], const float y[4],
float su, float sv, float eu, float ev, std::uint32_t argb, int blend = 0);
// The same quad for a texture named as UIRenderTextureNameFromHandle (a raw device texture: CBlockTexture).
void UIRenderAddImageNamed(const std::string& texture_name, const float x[4], const float y[4],
float su, float sv, float eu, float ev, std::uint32_t argb, int blend = 0);
void UIRenderSetSize(int width, int height);
void UIRenderGetSize(unsigned* width, unsigned* height);
+13
View File
@@ -455,3 +455,16 @@ BOOL TextOutA(HDC hdc, int x, int y, LPCSTR lpString, int c)
MultiByteToWideChar(CP_ACP, 0, lpString, c, wide.data(), n);
return TextOutW(hdc, x, y, wide.data(), n);
}
BOOL GetTextExtentPoint32A(HDC hdc, LPCSTR lpString, int c, LPSIZE psizl)
{
const int n = MultiByteToWideChar(CP_ACP, 0, lpString, c, nullptr, 0);
if (n <= 0)
{
if (c != 0 || !psizl) return FALSE;
return GetTextExtentPoint32W(hdc, nullptr, 0, psizl);
}
std::vector<WCHAR> wide(static_cast<size_t>(n));
MultiByteToWideChar(CP_ACP, 0, lpString, c, wide.data(), n);
return GetTextExtentPoint32W(hdc, wide.data(), n, psizl);
}
+4
View File
@@ -194,6 +194,10 @@ if(BUILD_TESTING AND CMAKE_SYSTEM_NAME STREQUAL CMAKE_HOST_SYSTEM_NAME)
target_link_libraries(port_text_test PRIVATE port_platform)
add_test(NAME port.text COMMAND $<TARGET_FILE:port_text_test>)
set_tests_properties(port.text PROPERTIES SKIP_RETURN_CODE 77)
add_executable(port_text_bar_test ${PROJECT_SOURCE_DIR}/tests/port/port_text_bar_test.cpp)
target_link_libraries(port_text_bar_test PRIVATE port_platform)
add_test(NAME port.text_bar COMMAND $<TARGET_FILE:port_text_bar_test>)
set_tests_properties(port.text_bar PROPERTIES SKIP_RETURN_CODE 77)
# MilesLib: the 40250 sound managers over the Miles emulation (platform/MilesLib/MilesAil.cpp).
add_executable(port_miles_test ${PROJECT_SOURCE_DIR}/tests/port/port_miles_test.cpp)
+26
View File
@@ -0,0 +1,26 @@
#pragma once
#include "GrpBase.h"
class CGraphicDib;
class CBlockTexture : public CGraphicBase
{
public:
CBlockTexture();
virtual ~CBlockTexture();
bool Create(CGraphicDib * pDIB, const RECT & c_rRect, DWORD dwWidth, DWORD dwHeight);
void SetClipRect(const RECT & c_rRect);
void Render(int ix, int iy);
void InvalidateRect(const RECT & c_rsrcRect);
protected:
CGraphicDib * m_pDIB;
RECT m_rect;
RECT m_clipRect;
BOOL m_bClipEnable;
DWORD m_dwWidth;
DWORD m_dwHeight;
LPDIRECT3DTEXTURE8 m_lpd3dTexture;
};
+2
View File
@@ -58,6 +58,8 @@ COLORREF SetTextColor(HDC hdc, COLORREF color);
COLORREF SetBkColor(HDC hdc, COLORREF color);
int SetBkMode(HDC hdc, int mode);
BOOL GetTextExtentPoint32W(HDC hdc, LPCWSTR lpString, int c, LPSIZE psizl);
BOOL GetTextExtentPoint32A(HDC hdc, LPCSTR lpString, int c, LPSIZE psizl);
#define GetTextExtentPoint32 GetTextExtentPoint32A
BOOL GetCharABCWidthsFloatW(HDC hdc, UINT iFirst, UINT iLast, LPABCFLOAT lpABC);
BOOL TextOutW(HDC hdc, int x, int y, LPCWSTR lpString, int c);
BOOL TextOutA(HDC hdc, int x, int y, LPCSTR lpString, int c);