aboutsummaryrefslogtreecommitdiff
path: root/Minecraft.Client/Common/UI/UIBitmapFont.cpp
blob: 2b1518c4ca19105e876b248fc583335e8e6cf941 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
#include "stdafx.h"

#include "BufferedImage.h"
#include "UIFontData.h"

#include <unordered_set>

#include "UIBitmapFont.h"


/////////////////////////////
// UI Abstract Bitmap Font //
/////////////////////////////

UIAbstractBitmapFont::~UIAbstractBitmapFont()
{
	if (m_registered) IggyFontRemoveUTF8( m_fontname.c_str(),-1,IGGY_FONTFLAG_none );
	delete m_bitmapFontProvider;
}


UIAbstractBitmapFont::UIAbstractBitmapFont(const string &fontname)
{
	m_fontname = fontname;

	m_registered = false;

	m_bitmapFontProvider = new IggyBitmapFontProvider();
	m_bitmapFontProvider->get_font_metrics = &UIAbstractBitmapFont::GetFontMetrics_Callback;
	m_bitmapFontProvider->get_glyph_for_codepoint = &UIAbstractBitmapFont::GetCodepointGlyph_Callback;
	m_bitmapFontProvider->get_glyph_metrics = &UIAbstractBitmapFont::GetGlyphMetrics_Callback;
	m_bitmapFontProvider->is_empty = &UIAbstractBitmapFont::IsGlyphEmpty_Callback;
	m_bitmapFontProvider->get_kerning = &UIAbstractBitmapFont::GetKerningForGlyphPair_Callback;
	m_bitmapFontProvider->can_bitmap = &UIAbstractBitmapFont::CanProvideBitmap_Callback;
	m_bitmapFontProvider->get_bitmap = &UIAbstractBitmapFont::GetGlyphBitmap_Callback;
	m_bitmapFontProvider->free_bitmap = &UIAbstractBitmapFont::FreeGlyphBitmap_Callback;
	m_bitmapFontProvider->userdata = this;
}

void UIAbstractBitmapFont::registerFont()
{
	if (!m_registered)
	{
		// 4J-JEV: These only need registering the once when we first use this font in Iggy.
		m_bitmapFontProvider->num_glyphs = m_numGlyphs;
		IggyFontInstallBitmapUTF8( m_bitmapFontProvider, m_fontname.c_str(), -1, IGGY_FONTFLAG_none );
		m_registered = true;
	}

	// 4J-JEV: Reset the font redirect to these fonts (we must do this everytime in-case we switched away elsewhere).
	IggyFontSetIndirectUTF8( m_fontname.c_str(), -1, IGGY_FONTFLAG_all, m_fontname.c_str(), -1, IGGY_FONTFLAG_none );
}

IggyFontMetrics * RADLINK UIAbstractBitmapFont::GetFontMetrics_Callback(void *user_context,IggyFontMetrics *metrics)
{
	return static_cast<UIAbstractBitmapFont *>(user_context)->GetFontMetrics(metrics);
}

S32 RADLINK UIAbstractBitmapFont::GetCodepointGlyph_Callback(void *user_context,U32 codepoint)
{
	return static_cast<UIAbstractBitmapFont *>(user_context)->GetCodepointGlyph(codepoint);
}

IggyGlyphMetrics * RADLINK UIAbstractBitmapFont::GetGlyphMetrics_Callback(void *user_context,S32 glyph,IggyGlyphMetrics *metrics)
{
	return static_cast<UIAbstractBitmapFont *>(user_context)->GetGlyphMetrics(glyph,metrics);
}

rrbool RADLINK UIAbstractBitmapFont::IsGlyphEmpty_Callback(void *user_context,S32 glyph)
{
	return static_cast<UIAbstractBitmapFont *>(user_context)->IsGlyphEmpty(glyph);
}

F32 RADLINK UIAbstractBitmapFont::GetKerningForGlyphPair_Callback(void *user_context,S32 first_glyph,S32 second_glyph)
{
	return static_cast<UIAbstractBitmapFont *>(user_context)->GetKerningForGlyphPair(first_glyph,second_glyph);
}

rrbool RADLINK UIAbstractBitmapFont::CanProvideBitmap_Callback(void *user_context,S32 glyph,F32 pixel_scale)
{
	return static_cast<UIAbstractBitmapFont *>(user_context)->CanProvideBitmap(glyph,pixel_scale);
}

rrbool RADLINK UIAbstractBitmapFont::GetGlyphBitmap_Callback(void *user_context,S32 glyph,F32 pixel_scale,IggyBitmapCharacter *bitmap)
{
	return static_cast<UIAbstractBitmapFont *>(user_context)->GetGlyphBitmap(glyph,pixel_scale,bitmap);
}

void RADLINK UIAbstractBitmapFont::FreeGlyphBitmap_Callback(void *user_context,S32 glyph,F32 pixel_scale,IggyBitmapCharacter *bitmap)
{
	return static_cast<UIAbstractBitmapFont *>(user_context)->FreeGlyphBitmap(glyph,pixel_scale,bitmap);
}

UIBitmapFont::UIBitmapFont(	SFontData &sfontdata )
	:	UIAbstractBitmapFont( sfontdata.m_strFontName )
{
	m_numGlyphs = sfontdata.m_uiGlyphCount;

	BufferedImage bimg(sfontdata.m_wstrFilename);
	int *bimgData = bimg.getData();
	
	m_cFontData = new CFontData(sfontdata, bimgData);
	
	//delete [] bimgData;
}

UIBitmapFont::~UIBitmapFont()
{
	m_cFontData->release();
}

//Callback function type for returning vertical font metrics
IggyFontMetrics *UIBitmapFont::GetFontMetrics(IggyFontMetrics *metrics)
{
	//Description
	// Vertical metrics for a font 
	//Members
	// ascent - extent of characters above baseline (positive) 
	// descent - extent of characters below baseline (positive)  
	// line_gap - spacing between one row's descent and the next line's ascent 
	// average_glyph_width_for_tab_stops - spacing of "average" character for computing default tab stops 
	// largest_glyph_bbox_y1 - lowest point below baseline of any character in the font 

	metrics->ascent = m_cFontData->getFontData()->m_fAscent;
	metrics->descent = m_cFontData->getFontData()->m_fDescent;
	
	metrics->average_glyph_width_for_tab_stops = 8.0f;

	// This is my best guess, there's no reference to a specific glyph here 
	// so aren't these just exactly the same.
	metrics->largest_glyph_bbox_y1 = metrics->descent;

	// metrics->line_gap; // 4J-JEV: Sean said this does nothing.

	return metrics;
}

//Callback function type for mapping 32-bit unicode code point to internal font glyph number; use IGGY_GLYPH_INVALID to mean "invalid character"
S32 UIBitmapFont::GetCodepointGlyph(U32 codepoint)
{
	// 4J-JEV: Change "right single quotation marks" to apostrophies.
	if (codepoint == 0x2019) codepoint = 0x27;

	return m_cFontData->getGlyphId(codepoint);
}

//Callback function type for returning horizontal metrics for each glyph 
IggyGlyphMetrics * UIBitmapFont::GetGlyphMetrics(S32 glyph,IggyGlyphMetrics *metrics)
{
	// 4J-JEV: Information about 'Glyph Metrics'.
	// http://freetype.sourceforge.net/freetype2/docs/glyphs/glyphs-3.html - Overview.
	// http://en.wikipedia.org/wiki/Kerning#Kerning_values - 'Font Units'

	//Description
	// Horizontal metrics for a glyph 
	//Members
	// x0 y0 x1 y1 - bounding box 
	// advance - horizontal distance to move character origin after drawing this glyph 


	/* 4J-JEV:		*IMPORTANT*
	 *
	 * I believe these are measured wrt the scale mentioned in GetGlyphBitmap
	 * i.e. 1.0f == pixel_scale,
	 *
	 * However we do not have that information here, then all these values need to be 
	 * the same for every scale in this font.
	 *
	 * We have 2 scales of bitmap glyph, and we can only scale these up by powers of 2 
	 * otherwise the fonts will become blurry. The appropriate glyph is chosen in
	 * 'GetGlyphBitmap' however we need to set the horizontal sizes here.
	 */

	float glyphAdvance = m_cFontData->getAdvance(glyph);

	// 4J-JEV:	Anything outside this measurement will be
	// cut off if it's at the start or end of the row.
	metrics->x0 = 0.0f;

	if ( m_cFontData->glyphIsWhitespace(glyph) )
		metrics->x1 = 0.0f;
	else
		metrics->x1 = glyphAdvance;

	// The next Glyph just starts right after this one.
	metrics->advance = glyphAdvance;

	//app.DebugPrintf("[UIBitmapFont] GetGlyphMetrics:\n\tmetrics->advance == %f,\n", metrics->advance);

	// These don't do anything either.
	metrics->y0 = 0.0f;	metrics->y1 = 1.0f;

	return metrics;
}

//Callback function type that should return true iff the glyph has no visible elements
rrbool UIBitmapFont::IsGlyphEmpty (S32 glyph)
{
	if (m_cFontData->glyphIsWhitespace(glyph))	return true;
	return false;//app.DebugPrintf("Is glyph %d empty? %s\n",glyph,isEmpty?"TRUE":"FALSE");
}

//Callback function type for returning the kerning amount for a given pair of glyphs 
F32 UIBitmapFont::GetKerningForGlyphPair(S32 first_glyph,S32 second_glyph)
{
	//UIBitmapFont *uiFont = (UIBitmapFont *) user_context;
	//app.DebugPrintf("Get kerning for glyph pair %d,%d\n",first_glyph,second_glyph);

	// 4J-JEV: Yet another field that doesn't do anything.
	// Only set out of paranoia.
	return 0.0f;
}

//Callback function type used for reporting whether a bitmap supports a given glyph at the given scale
rrbool UIBitmapFont::CanProvideBitmap(S32 glyph,F32 pixel_scale)
{
	//app.DebugPrintf("Can provide bitmap for glyph %d at scale %f? %s\n",glyph,pixel_scale,canProvideBitmap?"TRUE":"FALSE");
	return true;
}

//	Description
//	Callback function type used for getting the bitmap for a given glyph
//	Parameters
//	glyph  The glyph to compute/get the bitmap for  
//	pixel_scale  The scale factor (pseudo point size) requested by the textfield,adjusted for display resolution  
//	bitmap  The structure to store the bitmap into  
rrbool UIBitmapFont::GetGlyphBitmap(S32 glyph,F32 pixel_scale,IggyBitmapCharacter *bitmap)
{
	//Description
	// Data structure used to return to Iggy the bitmap to use for a glyph 
	//Members
	// pixels_one_per_byte - pixels startin with the top-left-most; 0 is transparent and 255 is opaque 
	// width_in_pixels - this is the width of the bitmap data 
	// height_in_pixels - this is the height of the bitmap data 
	// stride_in_bytes - the distance from one row to the next 
	// oversample - this is the amount of oversampling (0 or 1 = not oversample,2 = 2x oversampled,4 = 4x oversampled) 
	// point_sample - if true,the bitmap will be drawn with point sampling; if false,it will be drawn with bilinear 
	// top_left_x - the offset of the top left corner from the character origin 
	// top_left_y - the offset of the top left corner from the character origin 
	// pixel_scale_correct - the pixel_scale at which this character should be displayed at displayed_width_in_pixels 
	// pixel_scale_min - the smallest pixel_scale to allow using this character (scaled down) 
	// pixel_scale_max - the largest pixels cale to allow using this character (scaled up) 
	// user_context_for_free - you can use this to store data to access on the corresponding free call 
	
	int row = 0,col = 0;
	m_cFontData->getPos(glyph,row,col);

	// Skip to glyph start.
	bitmap->pixels_one_per_byte = m_cFontData->topLeftPixel(row,col);

	// Choose a reasonable glyph scale.
	float glyphScale = 1.0f, truePixelScale = 1.0f / m_cFontData->getFontData()->m_fAdvPerPixel;
	while ( (0.5f + glyphScale) * truePixelScale < pixel_scale)
		glyphScale++;

	// Debug: log each unique (font, pixel_scale) pair
	{
		static std::unordered_set<int> s_loggedScaleKeys;
		// Encode font pointer + quantized scale into a key to log each combo once
		int scaleKey = (int)(pixel_scale * 100.0f) ^ (int)(uintptr_t)m_cFontData;
		if (s_loggedScaleKeys.find(scaleKey) == s_loggedScaleKeys.end() && s_loggedScaleKeys.size() < 50) {
			s_loggedScaleKeys.insert(scaleKey);
			float tps = truePixelScale;
			app.DebugPrintf("[FONT-DBG] GetGlyphBitmap: font=%s glyph=%d pixel_scale=%.3f truePixelScale=%.1f glyphScale=%.0f\n",
				m_cFontData->getFontName().c_str(), glyph, pixel_scale, tps, glyphScale);
		}
	}

	// 4J-JEV: Debug code to check which font sizes are being used.
#if (!defined _CONTENT_PACKAGE) && (VERBOSE_FONT_OUTPUT > 0)

	struct DebugData 
	{
		string name;
		long scale;
		long mul;

		bool operator==(const DebugData& dd) const
		{
			if ( name.compare(dd.name) != 0 )	return false;
			else if (scale != dd.scale)			return false;
			else if (mul != dd.mul)				return false;
			else								return true;
		}
	};

	static long long lastPrint = System::currentTimeMillis();
	static unordered_set<DebugData> debug_fontSizesRequested;

	{
		DebugData dData = { m_cFontData->getFontName(), (long) pixel_scale, (long) glyphScale };
		debug_fontSizesRequested.insert(dData);

		if ( (lastPrint - System::currentTimeMillis()) > VERBOSE_FONT_OUTPUT )
		{
			app.DebugPrintf("<UIBitmapFont> Requested font/sizes:\n");

			unordered_set<DebugData>::iterator itr;
			for (	itr = debug_fontSizesRequested.begin();
					itr != debug_fontSizesRequested.end();
					itr++
				)
			{
				app.DebugPrintf("<UIBitmapFont>\t- %s:%i\t(x%i)\n",  itr->name.c_str(), itr->scale, itr->mul);
			}
			
			lastPrint = System::currentTimeMillis();
			debug_fontSizesRequested.clear();
		}
	}
#endif

	// It is not necessary to shrink the glyph width here
	// as its already been done in 'GetGlyphMetrics' by:
	// > metrics->x1 = m_kerningTable[glyph] * ratio;
	bitmap->width_in_pixels = m_cFontData->getFontData()->m_uiGlyphWidth;
	bitmap->height_in_pixels = m_cFontData->getFontData()->m_uiGlyphHeight;

	/* 4J-JEV: This is to do with glyph placement,
	 * and not the position in the archive.
	 * I don't know why the 0.65 is needed, or what it represents,
	 * although it doesn't look like its the baseline.
	 */
	bitmap->top_left_x = 0;

	// 4J-PB - this was chopping off the top of the characters, so accented ones were losing a couple of pixels at the top
	// DaveK has reduced the height of the accented capitalised characters, and we've dropped this from 0.65 to 0.64 
	bitmap->top_left_y = -static_cast<S32>(m_cFontData->getFontData()->m_uiGlyphHeight) * m_cFontData->getFontData()->m_fAscent;

	bitmap->oversample = 0;

#ifdef _WINDOWS64
	// On Windows64 the window can be any size, producing fractional
	// pixel_scale values that don't align to integer multiples of
	// truePixelScale.  The original console code cached glyphs with a
	// broad [truePixelScale, 99] range in the "normal" branch, which
	// works on consoles (fixed 1080p — font sizes are exact multiples)
	// but causes cache pollution on Windows: the first glyph cached in
	// that range sets pixel_scale_correct for ALL subsequent requests,
	// so different font sizes get scaled by wrong ratios, producing
	// mixed letter sizes on screen.
	//
	// Fix: always use pixel_scale_correct = truePixelScale so every
	// cache entry is consistent.  Two ranges: downscale (bilinear for
	// smooth reduction) and upscale (point_sample for crisp pixel-art).
	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
	if (glyphScale <= 1 && pixel_scale < truePixelScale)
	{
		// Small display: pixel_scale is less than the native glyph size.
		// Report the bitmap at its true native scale so Iggy downscales it
		// to match the layout metrics (bilinear for smooth downscaling).
		bitmap->pixel_scale_correct = truePixelScale;
		bitmap->pixel_scale_min = 0.0f;
		bitmap->pixel_scale_max = truePixelScale * 1.001f;
		bitmap->point_sample = false;
	}
	else
	{
		// Normal/upscale case: integer-multiple scaling for pixel-art look.
		// Console-only — fixed resolution means pixel_scale values are exact
		// integer multiples of truePixelScale, so cache sharing is safe.
		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

	// 4J-JEV: Nothing to do with glyph placement,
	// entirely to do with cropping your glyph out of an archive.
	bitmap->stride_in_bytes = m_cFontData->getFontData()->m_uiGlyphMapX;

	// 4J-JEV: Additional information needed to release memory afterwards.
	bitmap->user_context_for_free = nullptr;

	return true;
}

//Callback function type for freeing a bitmap shape returned by GetGlyphBitmap
void UIBitmapFont::FreeGlyphBitmap(S32 glyph,F32 pixel_scale,IggyBitmapCharacter *bitmap)
{
	// We don't need to free anything,it just comes from the archive.
	//app.DebugPrintf("Free bitmap for glyph %d at scale %f\n",glyph,pixel_scale);
}