aboutsummaryrefslogtreecommitdiff
path: root/Minecraft.Client/Font.cpp
diff options
context:
space:
mode:
authordaoge_cmd <3523206925@qq.com>2026-03-01 12:16:08 +0800
committerdaoge_cmd <3523206925@qq.com>2026-03-01 12:16:08 +0800
commitb691c43c44ff180d10e7d4a9afc83b98551ff586 (patch)
tree3e9849222cbc6ba49f2f1fc6e5fe7179632c7390 /Minecraft.Client/Font.cpp
parentdef8cb415354ac390b7e89052a50605285f1aca9 (diff)
Initial commit
Diffstat (limited to 'Minecraft.Client/Font.cpp')
-rw-r--r--Minecraft.Client/Font.cpp616
1 files changed, 616 insertions, 0 deletions
diff --git a/Minecraft.Client/Font.cpp b/Minecraft.Client/Font.cpp
new file mode 100644
index 00000000..17f72853
--- /dev/null
+++ b/Minecraft.Client/Font.cpp
@@ -0,0 +1,616 @@
+#include "stdafx.h"
+#include "Textures.h"
+#include "Font.h"
+#include "Options.h"
+#include "Tesselator.h"
+#include "..\Minecraft.World\IntBuffer.h"
+#include "..\Minecraft.World\net.minecraft.h"
+#include "..\Minecraft.World\StringHelpers.h"
+#include "..\Minecraft.World\Random.h"
+
+Font::Font(Options *options, const wstring& name, Textures* textures, bool enforceUnicode, TEXTURE_NAME textureName, int cols, int rows, int charWidth, int charHeight, unsigned short charMap[]/* = nullptr */) : textures(textures)
+{
+ int charC = cols * rows; // Number of characters in the font
+
+ charWidths = new int[charC];
+
+ // 4J - added initialisers
+ memset(charWidths, 0, charC);
+
+ enforceUnicodeSheet = false;
+ bidirectional = false;
+ xPos = yPos = 0.0f;
+
+ // Set up member variables
+ m_cols = cols;
+ m_rows = rows;
+ m_charWidth = charWidth;
+ m_charHeight = charHeight;
+ m_textureName = textureName;
+
+ // Build character map
+ if (charMap != NULL)
+ {
+ for(int i = 0; i < charC; i++)
+ {
+ m_charMap.insert(std::make_pair(charMap[i], i));
+ }
+ }
+
+ random = new Random();
+
+ // Load the image
+ BufferedImage *img = textures->readImage(m_textureName, name);
+
+ /* - 4J - TODO
+ try {
+ img = ImageIO.read(Textures.class.getResourceAsStream(name));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ */
+
+ int w = img->getWidth();
+ int h = img->getHeight();
+ intArray rawPixels(w * h);
+ img->getRGB(0, 0, w, h, rawPixels, 0, w);
+
+ for (int i = 0; i < charC; i++)
+ {
+ int xt = i % m_cols;
+ int yt = i / m_cols;
+
+ int x = 7;
+ for (; x >= 0; x--)
+ {
+ int xPixel = xt * 8 + x;
+ bool emptyColumn = true;
+ for (int y = 0; y < 8 && emptyColumn; y++)
+ {
+ int yPixel = (yt * 8 + y) * w;
+ bool emptyPixel = (rawPixels[xPixel + yPixel] >> 24) == 0; // Check the alpha value
+ if (!emptyPixel) emptyColumn = false;
+ }
+ if (!emptyColumn)
+ {
+ break;
+ }
+ }
+
+ if (i == ' ') x = 4 - 2;
+ charWidths[i] = x + 2;
+ }
+
+ delete img;
+
+ // calculate colors
+ for (int colorN = 0; colorN < 32; ++colorN)
+ {
+ int var10 = (colorN >> 3 & 1) * 85;
+ int red = (colorN >> 2 & 1) * 170 + var10;
+ int green = (colorN >> 1 & 1) * 170 + var10;
+ int blue = (colorN >> 0 & 1) * 170 + var10;
+
+ if (colorN == 6)
+ {
+ red += 85;
+ }
+
+ if (options->anaglyph3d)
+ {
+ int tmpRed = (red * 30 + green * 59 + blue * 11) / 100;
+ int tmpGreen = (red * 30 + green * 70) / 100;
+ int tmpBlue = (red * 30 + blue * 70) / 100;
+ red = tmpRed;
+ green = tmpGreen;
+ blue = tmpBlue;
+ }
+
+ if (colorN >= 16)
+ {
+ red /= 4;
+ green /= 4;
+ blue /= 4;
+ }
+
+ colors[colorN] = (red & 255) << 16 | (green & 255) << 8 | (blue & 255);
+ }
+}
+
+#ifndef _XBOX
+// 4J Stu - This dtor clashes with one in xui! We never delete these anyway so take it out for now. Can go back when we have got rid of XUI
+Font::~Font()
+{
+ delete[] charWidths;
+}
+#endif
+
+void Font::renderCharacter(wchar_t c)
+{
+ float xOff = c % m_cols * m_charWidth;
+ float yOff = c / m_cols * m_charWidth;
+
+ float width = charWidths[c] - .01f;
+ float height = m_charHeight - .01f;
+
+ float fontWidth = m_cols * m_charWidth;
+ float fontHeight = m_rows * m_charHeight;
+
+ Tesselator *t = Tesselator::getInstance();
+ // 4J Stu - Changed to a quad so that we can use within a command buffer
+#if 1
+ t->begin();
+ t->tex(xOff / fontWidth, (yOff + 7.99f) / fontHeight);
+ t->vertex(xPos, yPos + height, 0.0f);
+
+ t->tex((xOff + width) / fontWidth, (yOff + 7.99f) / fontHeight);
+ t->vertex(xPos + width, yPos + height, 0.0f);
+
+ t->tex((xOff + width) / fontWidth, yOff / fontHeight);
+ t->vertex(xPos + width, yPos, 0.0f);
+
+ t->tex(xOff / fontWidth, yOff / fontHeight);
+ t->vertex(xPos, yPos, 0.0f);
+
+ t->end();
+#else
+ t->begin(GL_TRIANGLE_STRIP);
+ t->tex(xOff / 128.0F, yOff / 128.0F);
+ t->vertex(xPos, yPos, 0.0f);
+ t->tex(xOff / 128.0F, (yOff + 7.99f) / 128.0F);
+ t->vertex(xPos, yPos + 7.99f, 0.0f);
+ t->tex((xOff + width) / 128.0F, yOff / 128.0F);
+ t->vertex(xPos + width, yPos, 0.0f);
+ t->tex((xOff + width) / 128.0F, (yOff + 7.99f) / 128.0F);
+ t->vertex(xPos + width, yPos + 7.99f, 0.0f);
+ t->end();
+#endif
+
+ xPos += (float) charWidths[c];
+}
+
+void Font::drawShadow(const wstring& str, int x, int y, int color)
+{
+ draw(str, x + 1, y + 1, color, true);
+ draw(str, x, y, color, false);
+}
+
+void Font::drawShadowWordWrap(const wstring &str, int x, int y, int w, int color, int h)
+{
+ drawWordWrapInternal(str, x + 1, y + 1, w, color, true, h);
+ drawWordWrapInternal(str, x, y, w, color, h);
+}
+
+void Font::draw(const wstring& str, int x, int y, int color)
+{
+ draw(str, x, y, color, false);
+}
+
+wstring Font::reorderBidi(const wstring &str)
+{
+ // 4J Not implemented
+ return str;
+}
+
+void Font::draw(const wstring &str, bool dropShadow)
+{
+ // Bind the texture
+ textures->bindTexture(m_textureName);
+
+ bool noise = false;
+ wstring cleanStr = sanitize(str);
+
+ for (int i = 0; i < (int)cleanStr.length(); ++i)
+ {
+ // Map character
+ wchar_t c = cleanStr.at(i);
+
+ if (c == 167 && i + 1 < cleanStr.length())
+ {
+ // 4J - following block was:
+ // int colorN = L"0123456789abcdefk".indexOf(str.toLowerCase().charAt(i + 1));
+ wchar_t ca = cleanStr[i+1];
+ int colorN = 16;
+ if(( ca >= L'0' ) && (ca <= L'9')) colorN = ca - L'0';
+ else if(( ca >= L'a' ) && (ca <= L'f')) colorN = (ca - L'a') + 10;
+ else if(( ca >= L'A' ) && (ca <= L'F')) colorN = (ca - L'A') + 10;
+
+ if (colorN == 16)
+ {
+ noise = true;
+ }
+ else
+ {
+ noise = false;
+ if (colorN < 0 || colorN > 15) colorN = 15;
+
+ if (dropShadow) colorN += 16;
+
+ int color = colors[colorN];
+ glColor3f((color >> 16) / 255.0F, ((color >> 8) & 255) / 255.0F, (color & 255) / 255.0F);
+ }
+
+
+ i += 1;
+ continue;
+ }
+
+ // "noise" for crazy splash screen message
+ if (noise)
+ {
+ int newc;
+ do
+ {
+ newc = random->nextInt(SharedConstants::acceptableLetters.length());
+ } while (charWidths[c + 32] != charWidths[newc + 32]);
+ c = newc;
+ }
+
+ renderCharacter(c);
+ }
+}
+
+void Font::draw(const wstring& str, int x, int y, int color, bool dropShadow)
+{
+ if (!str.empty())
+ {
+ if ((color & 0xFC000000) == 0) color |= 0xFF000000; // force alpha
+ // if not set
+
+ if (dropShadow) // divide RGB by 4, preserve alpha
+ color = (color & 0xfcfcfc) >> 2 | (color & (-1 << 24));
+
+ glColor4f((color >> 16 & 255) / 255.0F, (color >> 8 & 255) / 255.0F, (color & 255) / 255.0F, (color >> 24 & 255) / 255.0F);
+
+ xPos = x;
+ yPos = y;
+ draw(str, dropShadow);
+ }
+}
+
+int Font::width(const wstring& str)
+{
+ wstring cleanStr = sanitize(str);
+
+ if (cleanStr == L"") return 0; // 4J - was NULL comparison
+ int len = 0;
+
+ for (int i = 0; i < cleanStr.length(); ++i)
+ {
+ wchar_t c = cleanStr.at(i);
+
+ if(c == 167)
+ {
+ // Ignore the character used to define coloured text
+ ++i;
+ }
+ else
+ {
+ len += charWidths[c];
+ }
+ }
+
+ return len;
+}
+
+wstring Font::sanitize(const wstring& str)
+{
+ wstring sb = str;
+
+ for (unsigned int i = 0; i < sb.length(); i++)
+ {
+ if (CharacterExists(sb[i]))
+ {
+ sb[i] = MapCharacter(sb[i]);
+ }
+ else
+ {
+ // If this character isn't supported, just show the first character (empty square box character)
+ sb[i] = 0;
+ }
+ }
+ return sb;
+}
+
+int Font::MapCharacter(wchar_t c)
+{
+ if (!m_charMap.empty())
+ {
+ // Don't map space character
+ return c == ' ' ? c : m_charMap[c];
+ }
+ else
+ {
+ return c;
+ }
+}
+
+bool Font::CharacterExists(wchar_t c)
+{
+ if (!m_charMap.empty())
+ {
+ return m_charMap.find(c) != m_charMap.end();
+ }
+ else
+ {
+ return c >= 0 && c <= m_rows*m_cols;
+ }
+}
+
+void Font::drawWordWrap(const wstring &string, int x, int y, int w, int col, int h)
+{
+ //if (bidirectional)
+ //{
+ // string = reorderBidi(string);
+ //}
+ drawWordWrapInternal(string, x, y, w, col, h);
+}
+
+void Font::drawWordWrapInternal(const wstring &string, int x, int y, int w, int col, int h)
+{
+ drawWordWrapInternal(string, x, y, w, col, false, h);
+}
+
+void Font::drawWordWrap(const wstring &string, int x, int y, int w, int col, bool darken, int h)
+{
+ //if (bidirectional)
+ //{
+ // string = reorderBidi(string);
+ //}
+ drawWordWrapInternal(string, x, y, w, col, darken, h);
+}
+
+void Font::drawWordWrapInternal(const wstring& string, int x, int y, int w, int col, bool darken, int h)
+{
+ vector<wstring>lines = stringSplit(string,L'\n');
+ if (lines.size() > 1)
+ {
+ AUTO_VAR(itEnd, lines.end());
+ for (AUTO_VAR(it, lines.begin()); it != itEnd; it++)
+ {
+ // 4J Stu - Don't draw text that will be partially cutoff/overlap something it shouldn't
+ if( (y + this->wordWrapHeight(*it, w)) > h) break;
+ drawWordWrapInternal(*it, x, y, w, col, h);
+ y += this->wordWrapHeight(*it, w);
+ }
+ return;
+ }
+ vector<wstring> words = stringSplit(string,L' ');
+ unsigned int pos = 0;
+ while (pos < words.size())
+ {
+ wstring line = words[pos++] + L" ";
+ while (pos < words.size() && width(line + words[pos]) < w)
+ {
+ line += words[pos++] + L" ";
+ }
+ while (width(line) > w)
+ {
+ int l = 0;
+ while (width(line.substr(0, l + 1)) <= w)
+ {
+ l++;
+ }
+ if (trimString(line.substr(0, l)).length() > 0)
+ {
+ draw(line.substr(0, l), x, y, col);
+ y += 8;
+ }
+ line = line.substr(l);
+
+ // 4J Stu - Don't draw text that will be partially cutoff/overlap something it shouldn't
+ if( (y + 8) > h) break;
+ }
+ // 4J Stu - Don't draw text that will be partially cutoff/overlap something it shouldn't
+ if (trimString(line).length() > 0 && !( (y + 8) > h) )
+ {
+ draw(line, x, y, col);
+ y += 8;
+ }
+ }
+
+}
+
+int Font::wordWrapHeight(const wstring& string, int w)
+{
+ vector<wstring> lines = stringSplit(string,L'\n');
+ if (lines.size() > 1)
+ {
+ int h = 0;
+ AUTO_VAR(itEnd, lines.end());
+ for (AUTO_VAR(it, lines.begin()); it != itEnd; it++)
+ {
+ h += this->wordWrapHeight(*it, w);
+ }
+ return h;
+ }
+ vector<wstring> words = stringSplit(string,L' ');
+ unsigned int pos = 0;
+ int y = 0;
+ while (pos < words.size())
+ {
+ wstring line = words[pos++] + L" ";
+ while (pos < words.size() && width(line + words[pos]) < w)
+ {
+ line += words[pos++] + L" ";
+ }
+ while (width(line) > w)
+ {
+ int l = 0;
+ while (width(line.substr(0, l + 1)) <= w)
+ {
+ l++;
+ }
+ if (trimString(line.substr(0, l)).length() > 0)
+ {
+ y += 8;
+ }
+ line = line.substr(l);
+ }
+ if (trimString(line).length() > 0) {
+ y += 8;
+ }
+ }
+ if (y < 8) y += 8;
+ return y;
+
+}
+
+void Font::setEnforceUnicodeSheet(bool enforceUnicodeSheet)
+{
+ this->enforceUnicodeSheet = enforceUnicodeSheet;
+}
+
+void Font::setBidirectional(bool bidirectional)
+{
+ this->bidirectional = bidirectional;
+}
+
+bool Font::AllCharactersValid(const wstring &str)
+{
+ for (int i = 0; i < (int)str.length(); ++i)
+ {
+ wchar_t c = str.at(i);
+
+ if (c == 167 && i + 1 < str.length())
+ {
+ // skip special color setting
+ i += 1;
+ continue;
+ }
+
+ int index = SharedConstants::acceptableLetters.find(c);
+
+ if ((c != ' ') && !(index > 0 && !enforceUnicodeSheet))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+// Not in use
+/*// 4J - this code is lifted from #if 0 section above, so that we can directly create what would have gone in each of our 256 + 32 command buffers
+void Font::renderFakeCB(IntBuffer *ib)
+{
+ Tesselator *t = Tesselator::getInstance();
+
+ int i;
+
+ for(unsigned int j = 0; j < ib->limit(); j++)
+ {
+ int cb = ib->get(j);
+
+ if( cb < 256 )
+ {
+ i = cb;
+ t->begin();
+ int ix = i % 16 * 8;
+ int iy = i / 16 * 8;
+ // float s = 7.99f;
+ float s = 7.99f;
+
+ float uo = (0.0f) / 128.0f;
+ float vo = (0.0f) / 128.0f;
+
+ t->vertexUV((float)(0), (float)( 0 + s), (float)( 0), (float)( ix / 128.0f + uo), (float)( (iy + s) / 128.0f + vo));
+ t->vertexUV((float)(0 + s), (float)( 0 + s), (float)( 0), (float)( (ix + s) / 128.0f + uo), (float)( (iy + s) / 128.0f + vo));
+ t->vertexUV((float)(0 + s), (float)( 0), (float)( 0), (float)( (ix + s) / 128.0f + uo), (float)( iy / 128.0f + vo));
+ t->vertexUV((float)(0), (float)( 0), (float)( 0), (float)( ix / 128.0f + uo), (float)( iy / 128.0f + vo));
+ // target.colorBlit(texture, x + xo, y, color, ix, iy,
+ // charWidths[chars[i]], 8);
+ t->end();
+
+ glTranslatef((float)charWidths[i], 0, 0);
+ }
+ else
+ {
+ i = cb - 256;
+
+ int br = ((i >> 3) & 1) * 0x55;
+ int r = ((i >> 2) & 1) * 0xaa + br;
+ int g = ((i >> 1) & 1) * 0xaa + br;
+ int b = ((i >> 0) & 1) * 0xaa + br;
+ if (i == 6)
+ {
+ r += 0x55;
+ }
+ bool darken = i >= 16;
+
+ // color = r << 16 | g << 8 | b;
+ if (darken)
+ {
+ r /= 4;
+ g /= 4;
+ b /= 4;
+ }
+ glColor3f(r / 255.0f, g / 255.0f, b / 255.0f);
+ }
+ }
+}
+
+void Font::loadUnicodePage(int page)
+{
+ wchar_t fileName[25];
+ //String fileName = String.format("/1_2_2/font/glyph_%02X.png", page);
+ swprintf(fileName,25,L"/1_2_2/font/glyph_%02X.png",page);
+ BufferedImage *image = new BufferedImage(fileName);
+ //try
+ //{
+ // image = ImageIO.read(Textures.class.getResourceAsStream(fileName.toString()));
+ //}
+ //catch (IOException e)
+ //{
+ // throw new RuntimeException(e);
+ //}
+
+ unicodeTexID[page] = textures->getTexture(image);
+ lastBoundTexture = unicodeTexID[page];
+}
+
+void Font::renderUnicodeCharacter(wchar_t c)
+{
+ if (unicodeWidth[c] == 0)
+ {
+ // System.out.println("no-width char " + c);
+ return;
+ }
+
+ int page = c / 256;
+
+ if (unicodeTexID[page] == 0) loadUnicodePage(page);
+
+ if (lastBoundTexture != unicodeTexID[page])
+ {
+ glBindTexture(GL_TEXTURE_2D, unicodeTexID[page]);
+ lastBoundTexture = unicodeTexID[page];
+ }
+
+ // first column with non-trans pixels
+ int firstLeft = unicodeWidth[c] >> 4;
+ // last column with non-trans pixels
+ int firstRight = unicodeWidth[c] & 0xF;
+
+ float left = firstLeft;
+ float right = firstRight + 1;
+
+ float xOff = c % 16 * 16 + left;
+ float yOff = (c & 0xFF) / 16 * 16;
+ float width = right - left - .02f;
+
+ Tesselator *t = Tesselator::getInstance();
+ t->begin(GL_TRIANGLE_STRIP);
+ t->tex(xOff / 256.0F, yOff / 256.0F);
+ t->vertex(xPos, yPos, 0.0f);
+ t->tex(xOff / 256.0F, (yOff + 15.98f) / 256.0F);
+ t->vertex(xPos, yPos + 7.99f, 0.0f);
+ t->tex((xOff + width) / 256.0F, yOff / 256.0F);
+ t->vertex(xPos + width / 2, yPos, 0.0f);
+ t->tex((xOff + width) / 256.0F, (yOff + 15.98f) / 256.0F);
+ t->vertex(xPos + width / 2, yPos + 7.99f, 0.0f);
+ t->end();
+
+ xPos += (right - left) / 2 + 1;
+}
+*/
+