mcpe/source/Renderer/Font.cpp
2023-07-30 22:41:09 +03:00

195 lines
4 KiB
C++

/********************************************************************
Minecraft: Pocket Edition - Decompilation Project
Copyright (C) 2023 iProgramInCpp
The following code is licensed under the BSD 1 clause license.
SPDX-License-Identifier: BSD-1-Clause
********************************************************************/
#include "Font.hpp"
#include "Tesselator.hpp"
constexpr char COLOR_START_CHAR = '\xa7';
Font::Font(Options* pOpts, const std::string& fileName, Textures* pTexs)
{
m_fileName = fileName;
m_pOptions = pOpts;
m_pTextures = pTexs;
init(pOpts);
}
void Font::init(Options* pOpts)
{
GLuint texID = m_pTextures->loadTexture(m_fileName, true);
Texture* pTexture = m_pTextures->getTemporaryTextureData(texID);
if (!pTexture) return;
for (int i = 0; i < 256; i++) // character number
{
// note: the 'widthMax' behavior is assumed. It might not be like that exactly
int widthMax = 0;
if (i == 32) // space
{
widthMax = 2;
}
else
{
for (int j = 7; j >= 0; j--) // x position
{
int x = (i % 16), y = (i / 16);
int pixelDataIndex = pTexture->m_width * 8 * y + 8 * x + j;
for (int k = 0; k < 8; k++)
{
if ((uint8_t)pTexture->m_pixels[pixelDataIndex] != 0)
{
if (widthMax < j)
widthMax = j;
}
pixelDataIndex += pTexture->m_width;
}
}
}
m_charWidthInt[i] = widthMax + 2;
m_charWidthFloat[i] = float (widthMax) + 2;
}
}
void Font::buildChar(unsigned char chr, float x, float y)
{
Tesselator& t = Tesselator::instance;
float u = float((chr % 16) * 8);
float v = float((chr / 16) * 8);
constexpr float D128 = (1.0f / 128.0f);
#define CO (7.99f)
t.vertexUV(x, y + CO, 0.0f, u * D128, (v + CO) * D128);
t.vertexUV(x + CO, y + CO, 0.0f, (u + CO) * D128, (v + CO) * D128);
t.vertexUV(x + CO, y, 0.0f, (u + CO) * D128, v * D128);
t.vertexUV(x, y, 0.0f, u * D128, v * D128);
#undef CO
}
void Font::draw(const std::string& str, int x, int y, int color)
{
draw(str, x, y, color, false);
}
void Font::drawShadow(const std::string& str, int x, int y, int color)
{
draw(str, x + 1, y + 1, color, true);
draw(str, x, y, color, false);
}
void Font::draw(const std::string& str, int x, int y, int color, bool bShadow)
{
drawSlow(str, x, y, color, bShadow);
}
void Font::drawSlow(const std::string& str, int x, int y, int colorI, bool bShadow)
{
if (str.empty()) return;
uint32_t color = colorI;
if (bShadow)
color = (color & 0xFF000000U) + ((color & 0xFCFCFCu) >> 2);
m_pTextures->loadAndBindTexture(m_fileName);
uint32_t red = (color >> 16) & 0xFF;
uint32_t grn = (color >> 8) & 0xFF;
uint32_t blu = (color >> 0) & 0xFF;
uint32_t alp = (color >> 24) & 0xFF;
float alpf = float(alp) / 255.0f;
if (alpf == 0.0f)
alpf = 1.0f;
glColor4f(float(red) / 255.0f, float(grn) / 255.0f, float(blu) / 255.0f, alpf);
glPushMatrix();
Tesselator& t = Tesselator::instance;
t.begin();
glTranslatef(float(x), float(y), 0.0f);
float cXPos = 0.0f, cYPos = 0.0f;
for (int i = 0; i < int(str.size()); i++)
{
if (str[i] == '\n')
{
cYPos += 12.0f;
cXPos = 0;
continue;
}
uint8_t x = uint8_t(str[i]);
buildChar(x, cXPos, cYPos);
cXPos += m_charWidthFloat[x];
}
t.draw();
glPopMatrix();
}
void Font::onGraphicsReset()
{
init(m_pOptions);
}
int Font::height(const std::string& str)
{
if (str.empty()) return 0;
int res = 0; // note: starting at 0 looks wrong
for (int i = 0; i < int(str.size()); i++)
if (str[i] == '\n')
res += 12;
return res;
}
int Font::width(const std::string& str)
{
int maxLineWidth = 0, currentLineWidth = 0;
for (int i = 0; i < int(str.size()); i++)
{
char chr = str[i];
if (chr == COLOR_START_CHAR)
{
// skip the color code as well
i++;
continue;
}
if (chr == '\n')
{
if (maxLineWidth < currentLineWidth)
maxLineWidth = currentLineWidth;
currentLineWidth = 0;
}
currentLineWidth += m_charWidthInt[uint8_t(str[i])];
}
if (maxLineWidth < currentLineWidth)
maxLineWidth = currentLineWidth;
return maxLineWidth;
}