mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/LCE-Revelations.git
synced 2026-05-22 04:25:32 +00:00
Goal: Allow players to type and display text in any language supported by Unicode, including Chinese, Japanese, Korean, Thai, Arabic, Korean, Hindi, and more. This covers all text surfaces: chat editor, chat messages, signs (in-world and editor), world name/seed, server address/port fields, and all Iggy Flash UI text fields. Multi-language support: Two complementary rendering systems were added to handle Unicode text across the entire client: 1. Iggy UI (Flash-based text fields): A new UIUnicodeBitmapFont class serves Java Minecraft's glyph page PNGs (glyph_00.png-glyph_FF.png) through Iggy's bitmap font provider API. Registered as the global fallback font with metrics matching the Mojangles bitmap font for correct baseline alignment. When the primary bitmap font lacks a glyph, it returns IGGY_GLYPH_INVALID and Iggy seamlessly falls back to the unicode bitmap font. 2. Legacy C++ Font renderer (chat editor, in-world signs): Revived the commented-out unicode glyph page system in Font.cpp. Characters not in the bitmap font texture are rendered from glyph page PNGs loaded on demand, with proper texture switching mid-string. 3. ChatScreen input: Removed the restrictive acceptableLetters filter so all printable Unicode characters are accepted in chat. Languages now supported for text input and rendering: - Japanese (Hiragana, Katakana, Kanji) - Chinese (Simplified and Traditional) - Korean (Hangul) - Thai - Arabic - Hindi (Devanagari) - Russian (Cyrillic) - already worked via bitmap font - Greek - already worked via bitmap font - Polish, Czech, Turkish (Extended Latin) - already worked via bitmap font - Armenian, Georgian, and other scripts covered by glyph pages Security fixes: - Fixed memset under-initialization of Font::charWidths (zeroed 460 bytes instead of 460*sizeof(int)=1840 bytes, leaving entries 115+ uninitialized) - pre-existing bug - Added bounds checks to all UIUnicodeBitmapFont callbacks to reject glyph IDs outside [0, 65535], preventing OOB array access - Added bounds check in Font::width() section-sign fallback path to prevent OOB read on charWidths[] with high codepoints - Blocked Unicode bidirectional override characters (U+202A-202E, U+2066-2069) in chat input to prevent message spoofing Memory leak fix: - Fixed SignTileEntity::load allocating wchar_t[256] with new[] on every sign load without freeing. Replaced with stack allocation. Debug logging: - Added [SIGN] prefixed logging for sign save/update operations - Added [CHAT] prefixed logging for chat send/receive operations Files changed: - UIUnicodeBitmapFont.h/.cpp (new) - Iggy bitmap font for glyph pages - UIBitmapFont.cpp - Return IGGY_GLYPH_INVALID for unknown chars - UIFontData.h/.cpp - Added hasGlyph() method - UIController.h/.cpp - Load and register unicode bitmap fallback font - UITTFFont.h/.cpp - Added registerAsDefaultFonts parameter - Font.h/.cpp - Revived unicode glyph page rendering system - ChatScreen.cpp - Accept all Unicode input, block bidi overrides - Gui.cpp - Chat display debug logging - ClientConnection.cpp - Sign update debug logging - SignTileEntity.cpp - Sign save logging, memory leak fix
165 lines
4.6 KiB
C++
165 lines
4.6 KiB
C++
#include "stdafx.h"
|
|
#include "BufferedImage.h"
|
|
#include "UIFontData.h"
|
|
#include "UIUnicodeBitmapFont.h"
|
|
|
|
UIUnicodeBitmapFont::UIUnicodeBitmapFont(const string &fontname, SFontData &referenceFontData)
|
|
: UIAbstractBitmapFont(fontname)
|
|
{
|
|
m_numGlyphs = 65536;
|
|
m_referenceFontData = &referenceFontData;
|
|
memset(m_glyphPages, 0, sizeof(m_glyphPages));
|
|
memset(m_unicodeWidth, 0, sizeof(m_unicodeWidth));
|
|
|
|
FILE *f = nullptr;
|
|
fopen_s(&f, "Common/res/1_2_2/font/glyph_sizes.bin", "rb");
|
|
if (f)
|
|
{
|
|
fread(m_unicodeWidth, 1, 65536, f);
|
|
fclose(f);
|
|
}
|
|
}
|
|
|
|
UIUnicodeBitmapFont::~UIUnicodeBitmapFont()
|
|
{
|
|
for (int i = 0; i < 256; i++)
|
|
delete[] m_glyphPages[i];
|
|
}
|
|
|
|
void UIUnicodeBitmapFont::loadGlyphPage(int page)
|
|
{
|
|
wchar_t fileName[64];
|
|
swprintf(fileName, 64, L"/1_2_2/font/glyph_%02X.png", page);
|
|
BufferedImage bimg(fileName);
|
|
int *rawData = bimg.getData();
|
|
if (!rawData) return;
|
|
|
|
int size = 256 * 256;
|
|
m_glyphPages[page] = new unsigned char[size];
|
|
for (int i = 0; i < size; i++)
|
|
m_glyphPages[page][i] = (rawData[i] & 0xFF000000) >> 24;
|
|
}
|
|
|
|
IggyFontMetrics *UIUnicodeBitmapFont::GetFontMetrics(IggyFontMetrics *metrics)
|
|
{
|
|
metrics->ascent = m_referenceFontData->m_fAscent;
|
|
metrics->descent = m_referenceFontData->m_fDescent;
|
|
metrics->average_glyph_width_for_tab_stops = 8.0f;
|
|
metrics->largest_glyph_bbox_y1 = metrics->descent;
|
|
return metrics;
|
|
}
|
|
|
|
S32 UIUnicodeBitmapFont::GetCodepointGlyph(U32 codepoint)
|
|
{
|
|
if (codepoint < 65536 && m_unicodeWidth[codepoint] != 0)
|
|
return (S32)codepoint;
|
|
return IGGY_GLYPH_INVALID;
|
|
}
|
|
|
|
IggyGlyphMetrics *UIUnicodeBitmapFont::GetGlyphMetrics(S32 glyph, IggyGlyphMetrics *metrics)
|
|
{
|
|
if (glyph < 0 || glyph >= 65536) { metrics->x0 = metrics->x1 = metrics->advance = metrics->y0 = metrics->y1 = 0; return metrics; }
|
|
int left = m_unicodeWidth[glyph] >> 4;
|
|
int right = (m_unicodeWidth[glyph] & 0xF) + 1;
|
|
float pixelWidth = (right - left) / 2.0f + 1.0f;
|
|
float advance = pixelWidth * m_referenceFontData->m_fAdvPerPixel;
|
|
|
|
metrics->x0 = 0.0f;
|
|
metrics->x1 = advance;
|
|
metrics->advance = advance;
|
|
metrics->y0 = 0.0f;
|
|
metrics->y1 = 1.0f;
|
|
return metrics;
|
|
}
|
|
|
|
rrbool UIUnicodeBitmapFont::IsGlyphEmpty(S32 glyph)
|
|
{
|
|
if (glyph < 0 || glyph >= 65536) return true;
|
|
return m_unicodeWidth[glyph] == 0;
|
|
}
|
|
|
|
F32 UIUnicodeBitmapFont::GetKerningForGlyphPair(S32 first_glyph, S32 second_glyph)
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
rrbool UIUnicodeBitmapFont::CanProvideBitmap(S32 glyph, F32 pixel_scale)
|
|
{
|
|
return glyph >= 0 && glyph < 65536;
|
|
}
|
|
|
|
rrbool UIUnicodeBitmapFont::GetGlyphBitmap(S32 glyph, F32 pixel_scale, IggyBitmapCharacter *bitmap)
|
|
{
|
|
if (glyph < 0 || glyph >= 65536) return false;
|
|
int page = glyph / 256;
|
|
if (!m_glyphPages[page])
|
|
{
|
|
loadGlyphPage(page);
|
|
if (!m_glyphPages[page]) return false;
|
|
}
|
|
|
|
int cx = (glyph % 16) * 16;
|
|
int cy = ((glyph & 0xFF) / 16) * 16;
|
|
|
|
bitmap->pixels_one_per_byte = m_glyphPages[page] + (cy * 256) + cx;
|
|
bitmap->width_in_pixels = 16;
|
|
bitmap->height_in_pixels = 16;
|
|
bitmap->stride_in_bytes = 256;
|
|
|
|
bitmap->top_left_x = 0;
|
|
bitmap->top_left_y = -static_cast<S32>(16) * m_referenceFontData->m_fAscent;
|
|
|
|
bitmap->oversample = 0;
|
|
|
|
// Scale parameters: match UIBitmapFont's approach.
|
|
// truePixelScale = the pixel_scale at which 1 glyph pixel = 1 screen pixel.
|
|
// For 16px glyphs displayed at the same visual size as Mojangles_7 (8px glyphs with advPerPixel 1/10):
|
|
// The reference truePixelScale for Mojangles_7 is 1.0f/m_fAdvPerPixel = 10.0f
|
|
// Since our glyphs are 16px (2x the Mojangles 8px), our truePixelScale is 20.0f
|
|
float truePixelScale = 2.0f / m_referenceFontData->m_fAdvPerPixel;
|
|
|
|
#ifdef _WINDOWS64
|
|
bitmap->pixel_scale_correct = truePixelScale;
|
|
if (pixel_scale < truePixelScale)
|
|
{
|
|
bitmap->pixel_scale_min = 0.0f;
|
|
bitmap->pixel_scale_max = truePixelScale;
|
|
bitmap->point_sample = false;
|
|
}
|
|
else
|
|
{
|
|
bitmap->pixel_scale_min = truePixelScale;
|
|
bitmap->pixel_scale_max = 99.0f;
|
|
bitmap->point_sample = true;
|
|
}
|
|
#else
|
|
float glyphScale = 1.0f;
|
|
while ((0.5f + glyphScale) * truePixelScale < pixel_scale)
|
|
glyphScale++;
|
|
|
|
if (glyphScale <= 1 && pixel_scale < truePixelScale)
|
|
{
|
|
bitmap->pixel_scale_correct = truePixelScale;
|
|
bitmap->pixel_scale_min = 0.0f;
|
|
bitmap->pixel_scale_max = truePixelScale * 1.001f;
|
|
bitmap->point_sample = false;
|
|
}
|
|
else
|
|
{
|
|
float actualScale = pixel_scale / glyphScale;
|
|
bitmap->pixel_scale_correct = actualScale;
|
|
bitmap->pixel_scale_min = truePixelScale;
|
|
bitmap->pixel_scale_max = 99.0f;
|
|
bitmap->point_sample = true;
|
|
}
|
|
#endif
|
|
|
|
bitmap->user_context_for_free = nullptr;
|
|
return true;
|
|
}
|
|
|
|
void UIUnicodeBitmapFont::FreeGlyphBitmap(S32 glyph, F32 pixel_scale, IggyBitmapCharacter *bitmap)
|
|
{
|
|
// Pixel data lives in m_glyphPages -- nothing to free.
|
|
}
|