diff options
| author | daoge_cmd <3523206925@qq.com> | 2026-03-01 12:16:08 +0800 |
|---|---|---|
| committer | daoge_cmd <3523206925@qq.com> | 2026-03-01 12:16:08 +0800 |
| commit | b691c43c44ff180d10e7d4a9afc83b98551ff586 (patch) | |
| tree | 3e9849222cbc6ba49f2f1fc6e5fe7179632c7390 /Minecraft.Client/Font.cpp | |
| parent | def8cb415354ac390b7e89052a50605285f1aca9 (diff) | |
Initial commit
Diffstat (limited to 'Minecraft.Client/Font.cpp')
| -rw-r--r-- | Minecraft.Client/Font.cpp | 616 |
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; +} +*/ + |
