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