/******************************************************************** 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) { field_0 = 0; 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; }