CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/Font/PGF.cpp
Views: 1401
1
// Copyright (c) 2012- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
// ============== NOTE!!!!
19
20
// Thanks to the JPCSP project! This sceFont implementation is basically a C++ take on JPCSP's font code.
21
// Some parts, especially in this file, were simply copied, so I guess this really makes this file GPL3.
22
23
#include <algorithm>
24
#include "Common/Serialize/Serializer.h"
25
#include "Common/Serialize/SerializeFuncs.h"
26
#include "Core/MemMap.h"
27
#include "Core/Reporting.h"
28
#include "Core/Font/PGF.h"
29
#include "Core/HLE/HLE.h"
30
31
#include "GPU/GPUInterface.h"
32
#include "GPU/GPUState.h"
33
34
// These fonts, created by ttf2pgf, don't have complete glyph info and need to be identified.
35
static bool isJPCSPFont(const char *fontName) {
36
return !strcmp(fontName, "Liberation Sans") || !strcmp(fontName, "Liberation Serif") || !strcmp(fontName, "Sazanami") || !strcmp(fontName, "UnDotum") || !strcmp(fontName, "Microsoft YaHei");
37
}
38
39
// Gets a number of bits from an offset.
40
static int getBits(int numBits, const u8 *buf, size_t pos) {
41
_dbg_assert_msg_(numBits <= 32, "Unable to return more than 32 bits, %d requested", numBits);
42
43
const size_t wordpos = pos >> 5;
44
const u32_le *wordbuf = (const u32_le *)buf;
45
const u8 bitoff = pos & 31;
46
47
// Might just be in one, has to be within two.
48
if (bitoff + numBits < 32) {
49
const u32 mask = (1 << numBits) - 1;
50
return (wordbuf[wordpos] >> bitoff) & mask;
51
} else {
52
int v = wordbuf[wordpos] >> bitoff;
53
54
const u8 done = 32 - bitoff;
55
const u8 remaining = numBits - done;
56
if (remaining > 0) {
57
const u32 mask = (1 << remaining) - 1;
58
v |= (wordbuf[wordpos + 1] & mask) << done;
59
}
60
return v;
61
}
62
}
63
64
static inline int consumeBits(int numBits, const u8 *buf, size_t &pos) {
65
int v = getBits(numBits, buf, pos);
66
pos += numBits;
67
return v;
68
}
69
70
static std::vector<int> getTable(const u8 *buf, int bpe, size_t length) {
71
std::vector<int> vec;
72
vec.resize(length);
73
for (size_t i = 0; i < length; i++) {
74
vec[i] = getBits(bpe, buf, bpe * i);
75
}
76
return vec;
77
}
78
79
PGF::PGF()
80
: fontData(0) {
81
82
}
83
84
PGF::~PGF() {
85
delete [] fontData;
86
}
87
88
struct GlyphFromPGF1State {
89
int x;
90
int y;
91
int w;
92
int h;
93
int left;
94
int top;
95
int flags;
96
int shadowID;
97
int advanceH;
98
int advanceV;
99
int dimensionWidth, dimensionHeight;
100
int xAdjustH, xAdjustV;
101
int yAdjustH, yAdjustV;
102
u32 ptr;
103
104
operator Glyph() {
105
Glyph ret;
106
ret.w = w;
107
ret.h = h;
108
ret.left = left;
109
ret.top = top;
110
ret.flags = flags;
111
// Wasn't read before.
112
ret.shadowFlags = 0;
113
ret.shadowID = shadowID;
114
ret.advanceH = advanceH;
115
ret.advanceV = advanceV;
116
ret.dimensionWidth = dimensionWidth;
117
ret.dimensionHeight = dimensionHeight;
118
ret.xAdjustH = xAdjustH;
119
ret.xAdjustV = xAdjustV;
120
ret.yAdjustH = yAdjustH;
121
ret.yAdjustV = yAdjustV;
122
ret.ptr = ptr;
123
return ret;
124
}
125
};
126
127
void PGF::DoState(PointerWrap &p) {
128
auto s = p.Section("PGF", 1, 2);
129
if (!s)
130
return;
131
132
Do(p, header);
133
Do(p, rev3extra);
134
135
// Don't savestate size_t directly, 32-bit and 64-bit are different.
136
u32 fontDataSizeTemp = (u32)fontDataSize;
137
Do(p, fontDataSizeTemp);
138
fontDataSize = (size_t)fontDataSizeTemp;
139
if (p.mode == p.MODE_READ) {
140
delete [] fontData;
141
if (fontDataSize) {
142
fontData = new u8[fontDataSize];
143
DoArray(p, fontData, (int)fontDataSize);
144
}
145
} else if (fontDataSize) {
146
DoArray(p, fontData, (int)fontDataSize);
147
}
148
Do(p, fileName);
149
150
DoArray(p, dimensionTable, ARRAY_SIZE(dimensionTable));
151
DoArray(p, xAdjustTable, ARRAY_SIZE(xAdjustTable));
152
DoArray(p, yAdjustTable, ARRAY_SIZE(yAdjustTable));
153
DoArray(p, advanceTable, ARRAY_SIZE(advanceTable));
154
DoArray(p, charmapCompressionTable1, ARRAY_SIZE(charmapCompressionTable1));
155
DoArray(p, charmapCompressionTable2, ARRAY_SIZE(charmapCompressionTable2));
156
157
Do(p, charmap_compr);
158
Do(p, charmap);
159
if (s == 1) {
160
std::vector<GlyphFromPGF1State> oldGlyphs;
161
Do(p, oldGlyphs);
162
glyphs.resize(oldGlyphs.size());
163
for (size_t i = 0; i < oldGlyphs.size(); ++i) {
164
glyphs[i] = oldGlyphs[i];
165
}
166
Do(p, oldGlyphs);
167
shadowGlyphs.resize(oldGlyphs.size());
168
for (size_t i = 0; i < oldGlyphs.size(); ++i) {
169
shadowGlyphs[i] = oldGlyphs[i];
170
}
171
} else {
172
Do(p, glyphs);
173
Do(p, shadowGlyphs);
174
}
175
Do(p, firstGlyph);
176
}
177
178
bool PGF::ReadPtr(const u8 *ptr, size_t dataSize) {
179
const u8 *const startPtr = ptr;
180
181
if (dataSize < sizeof(header)) {
182
return false;
183
}
184
185
DEBUG_LOG(Log::sceFont, "Reading %d bytes of PGF header", (int)sizeof(header));
186
memcpy(&header, ptr, sizeof(header));
187
ptr += sizeof(header);
188
189
fileName = header.fontName;
190
191
if (header.revision == 3) {
192
memcpy(&rev3extra, ptr, sizeof(rev3extra));
193
rev3extra.compCharMapLength1 &= 0xFFFF;
194
rev3extra.compCharMapLength2 &= 0xFFFF;
195
ptr += sizeof(rev3extra);
196
}
197
198
const u32_le *wptr = (const u32_le *)ptr;
199
dimensionTable[0].resize(header.dimTableLength);
200
dimensionTable[1].resize(header.dimTableLength);
201
for (int i = 0; i < header.dimTableLength; i++) {
202
dimensionTable[0][i] = *wptr++;
203
dimensionTable[1][i] = *wptr++;
204
}
205
206
xAdjustTable[0].resize(header.xAdjustTableLength);
207
xAdjustTable[1].resize(header.xAdjustTableLength);
208
for (int i = 0; i < header.xAdjustTableLength; i++) {
209
xAdjustTable[0][i] = *wptr++;
210
xAdjustTable[1][i] = *wptr++;
211
}
212
213
yAdjustTable[0].resize(header.yAdjustTableLength);
214
yAdjustTable[1].resize(header.yAdjustTableLength);
215
for (int i = 0; i < header.yAdjustTableLength; i++) {
216
yAdjustTable[0][i] = *wptr++;
217
yAdjustTable[1][i] = *wptr++;
218
}
219
220
advanceTable[0].resize(header.advanceTableLength);
221
advanceTable[1].resize(header.advanceTableLength);
222
for (int i = 0; i < header.advanceTableLength; i++) {
223
advanceTable[0][i] = *wptr++;
224
advanceTable[1][i] = *wptr++;
225
}
226
227
const u8 *uptr = (const u8 *)wptr;
228
229
int shadowCharMapSize = ((header.shadowMapLength * header.shadowMapBpe + 31) & ~31) / 8;
230
const u8 *shadowCharMap = uptr;
231
uptr += shadowCharMapSize;
232
233
if (uptr < startPtr || uptr >= startPtr + dataSize) {
234
return false;
235
}
236
237
const u16_le *sptr = (const u16_le *)uptr;
238
if (header.revision == 3) {
239
charmapCompressionTable1[0].resize(rev3extra.compCharMapLength1);
240
charmapCompressionTable1[1].resize(rev3extra.compCharMapLength1);
241
for (int i = 0; i < rev3extra.compCharMapLength1; i++) {
242
charmapCompressionTable1[0][i] = *sptr++;
243
charmapCompressionTable1[1][i] = *sptr++;
244
}
245
246
charmapCompressionTable2[0].resize(rev3extra.compCharMapLength2);
247
charmapCompressionTable2[1].resize(rev3extra.compCharMapLength2);
248
for (int i = 0; i < rev3extra.compCharMapLength2; i++) {
249
charmapCompressionTable2[0][i] = *sptr++;
250
charmapCompressionTable2[1][i] = *sptr++;
251
}
252
}
253
254
uptr = (const u8 *)sptr;
255
256
int charMapSize = ((header.charMapLength * header.charMapBpe + 31) & ~31) / 8;
257
const u8 *charMap = uptr;
258
uptr += charMapSize;
259
260
int charPointerSize = (((header.charPointerLength * header.charPointerBpe + 31) & ~31) / 8);
261
const u8 *charPointerTable = uptr;
262
uptr += charPointerSize;
263
264
if (uptr < startPtr || uptr >= startPtr + dataSize) {
265
return false;
266
}
267
268
// PGF Fontdata.
269
u32 fontDataOffset = (u32)(uptr - startPtr);
270
271
fontDataSize = dataSize - fontDataOffset;
272
fontData = new u8[fontDataSize];
273
memcpy(fontData, uptr, fontDataSize);
274
275
// charmap.resize();
276
charmap.resize(header.charMapLength);
277
int charmap_compr_len = header.revision == 3 ? 7 : 1;
278
charmap_compr.resize(charmap_compr_len * 4);
279
glyphs.resize(header.charPointerLength);
280
shadowGlyphs.resize(header.charPointerLength);
281
firstGlyph = header.firstGlyph;
282
283
// Parse out the char map (array where each entry is an irregular number of bits)
284
// BPE = bits per entry, I think.
285
for (int i = 0; i < header.charMapLength; i++) {
286
charmap[i] = getBits(header.charMapBpe, charMap, i * header.charMapBpe);
287
// This check seems a little odd.
288
if ((size_t)charmap[i] >= glyphs.size())
289
charmap[i] = 65535;
290
}
291
292
std::vector<int> charPointers = getTable(charPointerTable, header.charPointerBpe, glyphs.size());
293
std::vector<int> shadowMap = getTable(shadowCharMap, header.shadowMapBpe, (s32)header.shadowMapLength);
294
295
// Pregenerate glyphs.
296
for (size_t i = 0; i < glyphs.size(); i++) {
297
ReadCharGlyph(fontData, charPointers[i] * 4 * 8 /* ??? */, glyphs[i]);
298
}
299
300
// And shadow glyphs.
301
for (size_t i = 0; i < glyphs.size(); i++) {
302
size_t shadowId = glyphs[i].shadowID;
303
if (shadowId < shadowMap.size()) {
304
size_t charId = shadowMap[shadowId];
305
if (charId < shadowGlyphs.size()) {
306
// TODO: check for pre existing shadow glyph
307
ReadShadowGlyph(fontData, charPointers[charId] * 4 * 8 /* ??? */, shadowGlyphs[charId]);
308
}
309
}
310
}
311
312
return true;
313
}
314
315
int PGF::GetCharIndex(int charCode, const std::vector<int> &charmapCompressed) {
316
int charIndex = 0;
317
for (size_t i = 0; i < charmapCompressed.size(); i += 2) {
318
if (charCode >= charmapCompressed[i] && charCode < charmapCompressed[i] + charmapCompressed[i + 1]) {
319
charIndex += charCode - charmapCompressed[i];
320
return charIndex;
321
}
322
charIndex += charmapCompressed[i + 1];
323
}
324
return -1;
325
}
326
327
bool PGF::GetCharInfo(int charCode, PGFCharInfo *charInfo, int altCharCode, int glyphType) const {
328
Glyph glyph;
329
memset(charInfo, 0, sizeof(*charInfo));
330
331
if (!GetCharGlyph(charCode, glyphType, glyph)) {
332
if (charCode < firstGlyph) {
333
// Character not in font, return zeroed charInfo as on real PSP.
334
return false;
335
}
336
if (!GetCharGlyph(altCharCode, glyphType, glyph)) {
337
return false;
338
}
339
}
340
341
charInfo->bitmapWidth = glyph.w;
342
charInfo->bitmapHeight = glyph.h;
343
charInfo->bitmapLeft = glyph.left;
344
charInfo->bitmapTop = glyph.top;
345
charInfo->sfp26Width = glyph.dimensionWidth;
346
charInfo->sfp26Height = glyph.dimensionHeight;
347
charInfo->sfp26Ascender = glyph.yAdjustH;
348
// Font y goes upwards. If top is 10 and height is 11, the descender is approx. -1 (below 0.)
349
charInfo->sfp26Descender = charInfo->sfp26Ascender - (s32)charInfo->sfp26Height;
350
charInfo->sfp26BearingHX = glyph.xAdjustH;
351
charInfo->sfp26BearingHY = glyph.yAdjustH;
352
charInfo->sfp26BearingVX = glyph.xAdjustV;
353
charInfo->sfp26BearingVY = glyph.yAdjustV;
354
charInfo->sfp26AdvanceH = glyph.advanceH;
355
charInfo->sfp26AdvanceV = glyph.advanceV;
356
charInfo->shadowFlags = glyph.shadowFlags;
357
charInfo->shadowId = glyph.shadowID;
358
return true;
359
}
360
361
void PGF::GetFontInfo(PGFFontInfo *fi) const {
362
fi->maxGlyphWidthI = header.maxSize[0];
363
fi->maxGlyphHeightI = header.maxSize[1];
364
fi->maxGlyphAscenderI = header.maxAscender;
365
fi->maxGlyphDescenderI = header.maxDescender;
366
fi->maxGlyphLeftXI = header.maxLeftXAdjust;
367
fi->maxGlyphBaseYI = header.maxBaseYAdjust;
368
fi->minGlyphCenterXI = header.minCenterXAdjust;
369
fi->maxGlyphTopYI = header.maxTopYAdjust;
370
fi->maxGlyphAdvanceXI = header.maxAdvance[0];
371
fi->maxGlyphAdvanceYI = header.maxAdvance[1];
372
fi->maxGlyphWidthF = (float)header.maxSize[0] / 64.0f;
373
fi->maxGlyphHeightF = (float)header.maxSize[1] / 64.0f;
374
fi->maxGlyphAscenderF = (float)header.maxAscender / 64.0f;
375
fi->maxGlyphDescenderF = (float)header.maxDescender / 64.0f;
376
fi->maxGlyphLeftXF = (float)header.maxLeftXAdjust / 64.0f;
377
fi->maxGlyphBaseYF = (float)header.maxBaseYAdjust / 64.0f;
378
fi->minGlyphCenterXF = (float)header.minCenterXAdjust / 64.0f;
379
fi->maxGlyphTopYF = (float)header.maxTopYAdjust / 64.0f;
380
fi->maxGlyphAdvanceXF = (float)header.maxAdvance[0] / 64.0f;
381
fi->maxGlyphAdvanceYF = (float)header.maxAdvance[1] / 64.0f;
382
383
fi->maxGlyphWidth = header.maxGlyphWidth;
384
fi->maxGlyphHeight = header.maxGlyphHeight;
385
fi->numGlyphs = header.charPointerLength;
386
fi->shadowMapLength = 0; // header.shadowMapLength; TODO
387
388
fi->BPP = header.bpp;
389
}
390
391
bool PGF::ReadShadowGlyph(const u8 *fontdata, size_t charPtr, Glyph &glyph) {
392
// Most of the glyph info is from the char data.
393
if (!ReadCharGlyph(fontdata, charPtr, glyph))
394
return false;
395
396
// Skip over the char data.
397
if (charPtr + 96 > fontDataSize * 8)
398
return false;
399
charPtr += getBits(14, fontdata, charPtr) * 8;
400
if (charPtr + 96 > fontDataSize * 8)
401
return false;
402
403
// Skip size.
404
charPtr += 14;
405
406
glyph.w = consumeBits(7, fontdata, charPtr);
407
glyph.h = consumeBits(7, fontdata, charPtr);
408
409
glyph.left = consumeBits(7, fontdata, charPtr);
410
if (glyph.left >= 64) {
411
glyph.left -= 128;
412
}
413
414
glyph.top = consumeBits(7, fontdata, charPtr);
415
if (glyph.top >= 64) {
416
glyph.top -= 128;
417
}
418
419
glyph.ptr = (u32)(charPtr / 8);
420
return true;
421
}
422
423
bool PGF::ReadCharGlyph(const u8 *fontdata, size_t charPtr, Glyph &glyph) {
424
// Skip size.
425
charPtr += 14;
426
427
glyph.w = consumeBits(7, fontdata, charPtr);
428
glyph.h = consumeBits(7, fontdata, charPtr);
429
430
glyph.left = consumeBits(7, fontdata, charPtr);
431
if (glyph.left >= 64) {
432
glyph.left -= 128;
433
}
434
435
glyph.top = consumeBits(7, fontdata, charPtr);
436
if (glyph.top >= 64) {
437
glyph.top -= 128;
438
}
439
440
glyph.flags = consumeBits(6, fontdata, charPtr);
441
442
glyph.shadowFlags = consumeBits(2, fontdata, charPtr) << (2 + 3);
443
glyph.shadowFlags |= consumeBits(2, fontdata, charPtr) << 3;
444
glyph.shadowFlags |= consumeBits(3, fontdata, charPtr);
445
446
glyph.shadowID = consumeBits(9, fontdata, charPtr);
447
448
if ((glyph.flags & FONT_PGF_METRIC_DIMENSION_INDEX) == FONT_PGF_METRIC_DIMENSION_INDEX)
449
{
450
int dimensionIndex = consumeBits(8, fontdata, charPtr);
451
452
if (dimensionIndex < header.dimTableLength) {
453
glyph.dimensionWidth = dimensionTable[0][dimensionIndex];
454
glyph.dimensionHeight = dimensionTable[1][dimensionIndex];
455
}
456
457
if (dimensionIndex == 0 && isJPCSPFont(fileName.c_str())) {
458
// Fonts created by ttf2pgf do not contain complete Glyph information.
459
// Provide default values.
460
glyph.dimensionWidth = glyph.w << 6;
461
glyph.dimensionHeight = glyph.h << 6;
462
}
463
}
464
else
465
{
466
glyph.dimensionWidth = consumeBits(32, fontdata, charPtr);
467
glyph.dimensionHeight = consumeBits(32, fontdata, charPtr);
468
}
469
470
if ((glyph.flags & FONT_PGF_METRIC_BEARING_X_INDEX) == FONT_PGF_METRIC_BEARING_X_INDEX)
471
{
472
int xAdjustIndex = consumeBits(8, fontdata, charPtr);
473
474
if (xAdjustIndex < header.xAdjustTableLength) {
475
glyph.xAdjustH = xAdjustTable[0][xAdjustIndex];
476
glyph.xAdjustV = xAdjustTable[1][xAdjustIndex];
477
}
478
479
if (xAdjustIndex == 0 && isJPCSPFont(fileName.c_str()))
480
{
481
// Fonts created by ttf2pgf do not contain complete Glyph information.
482
// Provide default values.
483
glyph.xAdjustH = glyph.left << 6;
484
glyph.xAdjustV = glyph.left << 6;
485
}
486
}
487
else
488
{
489
glyph.xAdjustH = consumeBits(32, fontdata, charPtr);
490
glyph.xAdjustV = consumeBits(32, fontdata, charPtr);
491
}
492
493
if ((glyph.flags & FONT_PGF_METRIC_BEARING_Y_INDEX) == FONT_PGF_METRIC_BEARING_Y_INDEX)
494
{
495
int yAdjustIndex = consumeBits(8, fontdata, charPtr);
496
497
if (yAdjustIndex < header.yAdjustTableLength) {
498
glyph.yAdjustH = yAdjustTable[0][yAdjustIndex];
499
glyph.yAdjustV = yAdjustTable[1][yAdjustIndex];
500
}
501
502
if (yAdjustIndex == 0 && isJPCSPFont(fileName.c_str()))
503
{
504
// Fonts created by ttf2pgf do not contain complete Glyph information.
505
// Provide default values.
506
glyph.yAdjustH = glyph.top << 6;
507
glyph.yAdjustV = glyph.top << 6;
508
}
509
}
510
else
511
{
512
glyph.yAdjustH = consumeBits(32, fontdata, charPtr);
513
glyph.yAdjustV = consumeBits(32, fontdata, charPtr);
514
}
515
516
if ((glyph.flags & FONT_PGF_METRIC_ADVANCE_INDEX) == FONT_PGF_METRIC_ADVANCE_INDEX)
517
{
518
int advanceIndex = consumeBits(8, fontdata, charPtr);
519
520
if (advanceIndex < header.advanceTableLength) {
521
glyph.advanceH = advanceTable[0][advanceIndex];
522
glyph.advanceV = advanceTable[1][advanceIndex];
523
}
524
}
525
else
526
{
527
glyph.advanceH = consumeBits(32, fontdata, charPtr);
528
glyph.advanceV = consumeBits(32, fontdata, charPtr);
529
}
530
531
glyph.ptr = (u32)(charPtr / 8);
532
return true;
533
}
534
535
bool PGF::GetCharGlyph(int charCode, int glyphType, Glyph &glyph) const {
536
if (charCode < firstGlyph)
537
return false;
538
charCode -= firstGlyph;
539
if (charCode < (int)charmap.size()) {
540
charCode = charmap[charCode];
541
}
542
if (glyphType == FONT_PGF_CHARGLYPH) {
543
if (charCode >= (int)glyphs.size())
544
return false;
545
glyph = glyphs[charCode];
546
} else {
547
if (charCode >= (int)shadowGlyphs.size())
548
return false;
549
glyph = shadowGlyphs[charCode];
550
}
551
return true;
552
}
553
554
void PGF::DrawCharacter(const GlyphImage *image, int clipX, int clipY, int clipWidth, int clipHeight, int charCode, int altCharCode, int glyphType) const {
555
Glyph glyph;
556
if (!GetCharGlyph(charCode, glyphType, glyph)) {
557
if (charCode < firstGlyph) {
558
// Don't draw anything if the character is before the first available glyph.
559
return;
560
}
561
// No Glyph available for this charCode, try to use the alternate char.
562
charCode = altCharCode;
563
if (!GetCharGlyph(charCode, glyphType, glyph)) {
564
return;
565
}
566
}
567
568
if (glyph.w <= 0 || glyph.h <= 0) {
569
DEBUG_LOG(Log::sceFont, "Glyph with negative size, not rendering");
570
return;
571
}
572
573
if (((glyph.flags & FONT_PGF_BMP_OVERLAY) != FONT_PGF_BMP_H_ROWS) &&
574
((glyph.flags & FONT_PGF_BMP_OVERLAY) != FONT_PGF_BMP_V_ROWS)) {
575
ERROR_LOG_REPORT(Log::sceFont, "Nonsense glyph bitmap direction flag");
576
return;
577
}
578
579
size_t bitPtr = glyph.ptr * 8;
580
int numberPixels = glyph.w * glyph.h;
581
int pixelIndex = 0;
582
583
int x = image->xPos64 >> 6;
584
int y = image->yPos64 >> 6;
585
u8 xFrac = image->xPos64 & 0x3F;
586
u8 yFrac = image->yPos64 & 0x3F;
587
588
// Negative means don't clip on that side.
589
if (clipX < 0)
590
clipX = 0;
591
if (clipY < 0)
592
clipY = 0;
593
if (clipWidth < 0)
594
clipWidth = 8192;
595
if (clipHeight < 0)
596
clipHeight = 8192;
597
598
// Use a buffer so we can apply subpixel rendering.
599
// TODO: Cache this buffer per glyph? Maybe even transpose it first?
600
std::vector<u8> decodedPixels;
601
decodedPixels.resize(numberPixels);
602
603
while (pixelIndex < numberPixels && bitPtr + 8 < fontDataSize * 8) {
604
// This is some kind of nibble based RLE compression.
605
int nibble = consumeBits(4, fontData, bitPtr);
606
607
int count;
608
int value = 0;
609
if (nibble < 8) {
610
value = consumeBits(4, fontData, bitPtr);
611
count = nibble + 1;
612
} else {
613
count = 16 - nibble;
614
}
615
616
for (int i = 0; i < count && pixelIndex < numberPixels; i++) {
617
if (nibble >= 8) {
618
value = consumeBits(4, fontData, bitPtr);
619
}
620
621
decodedPixels[pixelIndex++] = value | (value << 4);
622
}
623
}
624
625
auto samplePixel = [&](int xx, int yy) -> u8 {
626
if (xx < 0 || yy < 0 || xx >= glyph.w || yy >= glyph.h) {
627
return 0;
628
}
629
630
int index;
631
if ((glyph.flags & FONT_PGF_BMP_OVERLAY) == FONT_PGF_BMP_H_ROWS) {
632
index = yy * glyph.w + xx;
633
} else {
634
index = xx * glyph.h + yy;
635
}
636
637
return decodedPixels[index];
638
};
639
640
int renderX1 = std::max(clipX, x) - x;
641
int renderY1 = std::max(clipY, y) - y;
642
// We can render up to frac beyond the glyph w/h, so add 1px if necessary.
643
int renderX2 = std::min(clipX + clipWidth - x, glyph.w + (xFrac > 0 ? 1 : 0));
644
int renderY2 = std::min(clipY + clipHeight - y, glyph.h + (yFrac > 0 ? 1 : 0));
645
646
if (xFrac == 0 && yFrac == 0) {
647
for (int yy = renderY1; yy < renderY2; ++yy) {
648
for (int xx = renderX1; xx < renderX2; ++xx) {
649
u8 pixelColor = samplePixel(xx, yy);
650
SetFontPixel(image->bufferPtr, image->bytesPerLine, image->bufWidth, image->bufHeight, x + xx, y + yy, pixelColor, (FontPixelFormat)(u32)image->pixelFormat);
651
}
652
}
653
} else {
654
for (int yy = renderY1; yy < renderY2; ++yy) {
655
for (int xx = renderX1; xx < renderX2; ++xx) {
656
// First, blend horizontally. Tests show we blend swizzled to 8 bit.
657
u32 horiz1 = samplePixel(xx - 1, yy - 1) * xFrac + samplePixel(xx, yy - 1) * (64 - xFrac);
658
u32 horiz2 = samplePixel(xx - 1, yy + 0) * xFrac + samplePixel(xx, yy + 0) * (64 - xFrac);
659
// Now blend those together vertically.
660
u32 blended = horiz1 * yFrac + horiz2 * (64 - yFrac);
661
662
// We multiplied an 8 bit value by 64 twice, so now we have a 20 bit value.
663
u8 pixelColor = blended >> 12;
664
SetFontPixel(image->bufferPtr, image->bytesPerLine, image->bufWidth, image->bufHeight, x + xx, y + yy, pixelColor, (FontPixelFormat)(u32)image->pixelFormat);
665
}
666
}
667
}
668
669
gpu->InvalidateCache(image->bufferPtr, image->bytesPerLine * image->bufHeight, GPU_INVALIDATE_SAFE);
670
}
671
672
void PGF::SetFontPixel(u32 base, int bpl, int bufWidth, int bufHeight, int x, int y, u8 pixelColor, FontPixelFormat pixelformat) const {
673
if (x < 0 || x >= bufWidth || y < 0 || y >= bufHeight) {
674
return;
675
}
676
677
static const u8 fontPixelSizeInBytes[] = { 0, 0, 1, 3, 4 }; // 0 means 2 pixels per byte
678
if (pixelformat < 0 || pixelformat > PSP_FONT_PIXELFORMAT_32) {
679
ERROR_LOG_REPORT_ONCE(pfgbadformat, Log::sceFont, "Invalid image format in image: %d", (int)pixelformat);
680
return;
681
}
682
int pixelBytes = fontPixelSizeInBytes[pixelformat];
683
int bufMaxWidth = (pixelBytes == 0 ? bpl * 2 : bpl / pixelBytes);
684
if (x >= bufMaxWidth) {
685
return;
686
}
687
688
int framebufferAddr = base + (y * bpl) + (pixelBytes == 0 ? x / 2 : x * pixelBytes);
689
690
switch (pixelformat) {
691
case PSP_FONT_PIXELFORMAT_4:
692
case PSP_FONT_PIXELFORMAT_4_REV:
693
{
694
// We always get a 8-bit value, so take only the top 4 bits.
695
const u8 pix4 = pixelColor >> 4;
696
697
int oldColor = Memory::Read_U8(framebufferAddr);
698
int newColor;
699
if ((x & 1) != pixelformat) {
700
newColor = (pix4 << 4) | (oldColor & 0xF);
701
} else {
702
newColor = (oldColor & 0xF0) | pix4;
703
}
704
Memory::Write_U8(newColor, framebufferAddr);
705
break;
706
}
707
case PSP_FONT_PIXELFORMAT_8:
708
{
709
Memory::Write_U8(pixelColor, framebufferAddr);
710
break;
711
}
712
case PSP_FONT_PIXELFORMAT_24:
713
{
714
// Each channel has the same value.
715
Memory::Write_U8(pixelColor, framebufferAddr + 0);
716
Memory::Write_U8(pixelColor, framebufferAddr + 1);
717
Memory::Write_U8(pixelColor, framebufferAddr + 2);
718
break;
719
}
720
case PSP_FONT_PIXELFORMAT_32:
721
{
722
// Spread the 8 bits out into one write of 32 bits.
723
u32 pix32 = pixelColor;
724
pix32 |= pix32 << 8;
725
pix32 |= pix32 << 16;
726
Memory::Write_U32(pix32, framebufferAddr);
727
break;
728
}
729
}
730
}
731
732