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 #include <algorithm>
19 
20 #include "ext/xxhash.h"
21 #include "Common/Data/Color/RGBAUtil.h"
22 #include "Common/File/VFS/VFS.h"
23 #include "Common/Data/Format/ZIMLoad.h"
24 #include "Common/Data/Format/PNGLoad.h"
25 #include "Common/Data/Encoding/Utf8.h"
26 #include "Common/Render/TextureAtlas.h"
27 #include "Common/Render/Text/draw_text.h"
28 #include "Common/Serialize/Serializer.h"
29 #include "Common/Serialize/SerializeFuncs.h"
30 #include "Common/StringUtils.h"
31 #include "Core/HDRemaster.h"
32 #include "Core/Host.h"
33 #include "GPU/ge_constants.h"
34 #include "GPU/GPUState.h"
35 #include "GPU/GPUInterface.h"
36 #include "Core/FileSystems/MetaFileSystem.h"
37 #include "Core/Util/PPGeDraw.h"
38 #include "Core/HLE/sceKernel.h"
39 #include "Core/HLE/sceKernelMemory.h"
40 #include "Core/HLE/sceGe.h"
41 #include "Core/MemMapHelpers.h"
42 #include "Core/System.h"
43 
44 static Atlas g_ppge_atlas;
45 static Draw::DrawContext *g_draw = nullptr;
46 
47 static u32 atlasPtr;
48 static int atlasWidth;
49 static int atlasHeight;
50 static uint64_t atlasHash;
51 static bool atlasRequiresReset;
52 
53 struct PPGeVertex {
54 	u16_le u, v;
55 	u32_le color;
56 	float_le x, y, z;
57 };
58 
59 struct PPGeRemasterVertex {
60 	float_le u, v;
61 	u32_le color;
62 	float_le x, y, z;
63 };
64 
65 static PSPPointer<PspGeListArgs> listArgs;
66 static u32 listArgsSize = sizeof(PspGeListArgs);
67 static u32 savedContextPtr;
68 static u32 savedContextSize = 512 * 4;
69 
70 // Display list writer
71 static u32 dlPtr;
72 static u32 dlWritePtr;
73 static u32 dlSize = 0x10000; // should be enough for a frame of gui...
74 
75 static u32 dataPtr;
76 static u32 dataWritePtr;
77 static u32 dataSize = 0x10000; // should be enough for a frame of gui...
78 
79 static PSPPointer<u16_le> palette;
80 static u32 paletteSize = sizeof(u16) * 16;
81 
82 // Vertex collector
83 static u32 vertexStart;
84 static u32 vertexCount;
85 
86 // Used for formatting text
87 struct AtlasCharVertex
88 {
89 	float x;
90 	float y;
91 	const AtlasChar *c;
92 };
93 
94 struct AtlasTextMetrics
95 {
96 	float x;
97 	float y;
98 	float maxWidth;
99 	float lineHeight;
100 	float scale;
101 	int numLines;
102 
103 };
104 
105 typedef std::vector<AtlasCharVertex> AtlasCharLine;
106 typedef std::vector<AtlasCharLine> AtlasLineArray;
107 
108 static AtlasCharLine char_one_line;
109 static AtlasLineArray char_lines;
110 static AtlasTextMetrics char_lines_metrics;
111 
112 static bool textDrawerInited = false;
113 static TextDrawer *textDrawer = nullptr;
114 struct PPGeTextDrawerCacheKey {
operator <PPGeTextDrawerCacheKey115 	bool operator < (const PPGeTextDrawerCacheKey &other) const {
116 		if (align != other.align)
117 			return align < other.align;
118 		if (wrapWidth != other.wrapWidth)
119 			return wrapWidth < other.wrapWidth;
120 		return text < other.text;
121 	}
122 	std::string text;
123 	int align;
124 	float wrapWidth;
125 };
126 struct PPGeTextDrawerImage {
127 	TextStringEntry entry;
128 	u32 ptr;
129 };
130 static std::map<PPGeTextDrawerCacheKey, PPGeTextDrawerImage> textDrawerImages;
131 
PPGeSetDrawContext(Draw::DrawContext * draw)132 void PPGeSetDrawContext(Draw::DrawContext *draw) {
133 	g_draw = draw;
134 }
135 
136 // Overwrite the current text lines buffer so it can be drawn later.
137 void PPGePrepareText(const char *text, float x, float y, PPGeAlign align, float scale, float lineHeightScale,
138 	int WrapType = PPGE_LINE_NONE, int wrapWidth = 0);
139 
140 // These functions must be called between PPGeBegin and PPGeEnd.
141 
142 // Draw currently buffered text using the state from PPGeGetTextBoundingBox() call.
143 // Clears the buffer and state when done.
144 void PPGeDrawCurrentText(u32 color = 0xFFFFFFFF);
145 
146 static void PPGeDecimateTextImages(int age = 97);
147 
148 void PPGeSetTexture(u32 dataAddr, int width, int height);
149 
150 //only 0xFFFFFF of data is used
WriteCmd(u8 cmd,u32 data)151 static void WriteCmd(u8 cmd, u32 data) {
152 	Memory::Write_U32((cmd << 24) | (data & 0xFFFFFF), dlWritePtr);
153 	dlWritePtr += 4;
154 	_dbg_assert_(dlWritePtr <= dlPtr + dlSize);
155 }
156 
WriteCmdAddrWithBase(u8 cmd,u32 addr)157 static void WriteCmdAddrWithBase(u8 cmd, u32 addr) {
158 	WriteCmd(GE_CMD_BASE, (addr >> 8) & 0xFF0000);
159 	WriteCmd(cmd, addr & 0xFFFFFF);
160 }
161 
162 /*
163 static void WriteCmdFloat(u8 cmd, float f) {
164 	union {
165 		float fl;
166 		u32 u;
167 	} conv;
168 	conv.fl = f;
169 	WriteCmd(cmd, conv.u >> 8);
170 }*/
171 
BeginVertexData()172 static void BeginVertexData() {
173 	vertexCount = 0;
174 	vertexStart = dataWritePtr;
175 }
176 
Vertex(float x,float y,float u,float v,int tw,int th,u32 color=0xFFFFFFFF)177 static void Vertex(float x, float y, float u, float v, int tw, int th, u32 color = 0xFFFFFFFF) {
178 	if (g_RemasterMode) {
179 		PPGeRemasterVertex vtx;
180 		vtx.x = x; vtx.y = y; vtx.z = 0;
181 		vtx.u = u * tw; vtx.v = v * th;
182 		vtx.color = color;
183 		Memory::WriteStruct(dataWritePtr, &vtx);
184 		dataWritePtr += sizeof(vtx);
185 	} else {
186 		PPGeVertex vtx;
187 		vtx.x = x; vtx.y = y; vtx.z = 0;
188 		vtx.u = u * tw; vtx.v = v * th;
189 		vtx.color = color;
190 		Memory::WriteStruct(dataWritePtr, &vtx);
191 		dataWritePtr += sizeof(vtx);
192 	}
193 	_dbg_assert_(dataWritePtr <= dataPtr + dataSize);
194 	vertexCount++;
195 }
196 
EndVertexDataAndDraw(int prim)197 static void EndVertexDataAndDraw(int prim) {
198 	WriteCmdAddrWithBase(GE_CMD_VADDR, vertexStart);
199 	WriteCmd(GE_CMD_PRIM, (prim << 16) | vertexCount);
200 }
201 
PPGeIsFontTextureAddress(u32 addr)202 bool PPGeIsFontTextureAddress(u32 addr) {
203 	return addr == atlasPtr;
204 }
205 
__PPGeDoAlloc(u32 & size,bool fromTop,const char * name)206 static u32 __PPGeDoAlloc(u32 &size, bool fromTop, const char *name) {
207 	u32 ptr = kernelMemory.Alloc(size, fromTop, name);
208 	// Didn't get it, try again after decimating images.
209 	if (ptr == (u32)-1) {
210 		PPGeDecimateTextImages(4);
211 		PPGeImage::Decimate(4);
212 
213 		ptr = kernelMemory.Alloc(size, fromTop, name);
214 		if (ptr == (u32)-1) {
215 			return 0;
216 		}
217 	}
218 	return ptr;
219 }
220 
__PPGeSetupListArgs()221 void __PPGeSetupListArgs()
222 {
223 	if (listArgs.IsValid())
224 		return;
225 
226 	listArgs = __PPGeDoAlloc(listArgsSize, false, "PPGe List Args");
227 	if (listArgs.IsValid()) {
228 		listArgs->size = 8;
229 		if (savedContextPtr == 0)
230 			savedContextPtr = __PPGeDoAlloc(savedContextSize, false, "PPGe Saved Context");
231 		listArgs->context = savedContextPtr;
232 	}
233 }
234 
__PPGeInit()235 void __PPGeInit() {
236 	// PPGe isn't really important for headless, and LoadZIM takes a long time.
237 	bool skipZIM = host->ShouldSkipUI();
238 
239 	u8 *imageData[12]{};
240 	int width[12]{};
241 	int height[12]{};
242 	int flags = 0;
243 
244 	bool loadedZIM = !skipZIM && LoadZIM("ppge_atlas.zim", width, height, &flags, imageData);
245 	if (!skipZIM && !loadedZIM) {
246 		ERROR_LOG(SCEGE, "Failed to load ppge_atlas.zim.\n\nPlace it in the directory \"assets\" under your PPSSPP directory.\n\nPPGe stuff will not be drawn.");
247 	}
248 
249 	if (loadedZIM) {
250 		size_t atlas_data_size;
251 		if (!g_ppge_atlas.IsMetadataLoaded()) {
252 			uint8_t *atlas_data = VFSReadFile("ppge_atlas.meta", &atlas_data_size);
253 			if (atlas_data)
254 				g_ppge_atlas.Load(atlas_data, atlas_data_size);
255 			delete[] atlas_data;
256 		}
257 	}
258 
259 	u32 atlasSize = height[0] * width[0] / 2;  // it's a 4-bit paletted texture in ram
260 	atlasWidth = width[0];
261 	atlasHeight = height[0];
262 	dlPtr = __PPGeDoAlloc(dlSize, false, "PPGe Display List");
263 	dataPtr = __PPGeDoAlloc(dataSize, false, "PPGe Vertex Data");
264 	__PPGeSetupListArgs();
265 	atlasPtr = atlasSize == 0 ? 0 : __PPGeDoAlloc(atlasSize, false, "PPGe Atlas Texture");
266 	palette = __PPGeDoAlloc(paletteSize, false, "PPGe Texture Palette");
267 
268 	// Generate 16-greyscale palette. All PPGe graphics are greyscale so we can use a tiny paletted texture.
269 	for (int i = 0; i < 16; i++) {
270 		int val = i;
271 		palette[i] = (val << 12) | 0xFFF;
272 	}
273 
274 	const u32_le *imagePtr = (u32_le *)imageData[0];
275 	u8 *ramPtr = atlasPtr == 0 ? nullptr : (u8 *)Memory::GetPointer(atlasPtr);
276 
277 	// Palettize to 4-bit, the easy way.
278 	for (int i = 0; i < width[0] * height[0] / 2; i++) {
279 		// Each pixel is 16 bits, so this loads two pixels.
280 		u32 c = imagePtr[i];
281 		// It's white anyway, so we only look at one channel of each pixel.
282 		int a1 = (c & 0x0000000F) >> 0;
283 		int a2 = (c & 0x000F0000) >> 16;
284 		u8 cval = (a2 << 4) | a1;
285 		ramPtr[i] = cval;
286 	}
287 	atlasHash = XXH3_64bits(ramPtr, atlasWidth * atlasHeight / 2);
288 
289 	free(imageData[0]);
290 
291 	// We can't create it here, because Android needs it on the right thread.
292 	// Avoid creating ever on headless just to be safe.
293 	textDrawerInited = PSP_CoreParameter().headLess;
294 	textDrawer = nullptr;
295 	textDrawerImages.clear();
296 
297 	atlasRequiresReset = false;
298 
299 	INFO_LOG(SCEGE, "PPGe drawing library initialized. DL: %08x Data: %08x Atlas: %08x (%i) Args: %08x",
300 		dlPtr, dataPtr, atlasPtr, atlasSize, listArgs.ptr);
301 }
302 
__PPGeDoState(PointerWrap & p)303 void __PPGeDoState(PointerWrap &p)
304 {
305 	auto s = p.Section("PPGeDraw", 1, 4);
306 	if (!s)
307 		return;
308 
309 	Do(p, atlasPtr);
310 	Do(p, atlasWidth);
311 	Do(p, atlasHeight);
312 	Do(p, palette);
313 
314 	// If the atlas the save state was created with differs from the current one, reload.
315 	uint64_t savedHash = atlasHash;
316 	if (s >= 4) {
317 		Do(p, savedHash);
318 	} else {
319 		// Memory was already updated by this point, so check directly.
320 		if (atlasPtr != 0) {
321 			savedHash = XXH3_64bits(Memory::GetPointer(atlasPtr), atlasWidth * atlasHeight / 2);
322 		} else {
323 			savedHash ^= 1;
324 		}
325 	}
326 	atlasRequiresReset = savedHash != atlasHash;
327 
328 	Do(p, savedContextPtr);
329 	Do(p, savedContextSize);
330 
331 	if (s == 1) {
332 		listArgs = 0;
333 	} else {
334 		Do(p, listArgs);
335 	}
336 
337 	if (s >= 3) {
338 		uint32_t sz = (uint32_t)textDrawerImages.size();
339 		Do(p, sz);
340 
341 		switch (p.mode) {
342 		case PointerWrap::MODE_READ:
343 			textDrawerImages.clear();
344 			for (uint32_t i = 0; i < sz; ++i) {
345 				// We only care about the pointers, so we can free them.  We'll decimate right away.
346 				PPGeTextDrawerCacheKey key{ StringFromFormat("__savestate__%d", i), -1, -1 };
347 				textDrawerImages[key] = PPGeTextDrawerImage{};
348 				Do(p, textDrawerImages[key].ptr);
349 			}
350 			break;
351 		default:
352 			for (const auto &im : textDrawerImages) {
353 				Do(p, im.second.ptr);
354 			}
355 			break;
356 		}
357 	} else {
358 		textDrawerImages.clear();
359 	}
360 
361 	Do(p, dlPtr);
362 	Do(p, dlWritePtr);
363 	Do(p, dlSize);
364 
365 	Do(p, dataPtr);
366 	Do(p, dataWritePtr);
367 	Do(p, dataSize);
368 
369 	Do(p, vertexStart);
370 	Do(p, vertexCount);
371 
372 	Do(p, char_lines);
373 	Do(p, char_lines_metrics);
374 }
375 
__PPGeShutdown()376 void __PPGeShutdown()
377 {
378 	if (atlasPtr)
379 		kernelMemory.Free(atlasPtr);
380 	if (dataPtr)
381 		kernelMemory.Free(dataPtr);
382 	if (dlPtr)
383 		kernelMemory.Free(dlPtr);
384 	if (listArgs.IsValid())
385 		kernelMemory.Free(listArgs.ptr);
386 	if (savedContextPtr)
387 		kernelMemory.Free(savedContextPtr);
388 	if (palette)
389 		kernelMemory.Free(palette.ptr);
390 
391 	atlasPtr = 0;
392 	dataPtr = 0;
393 	dlPtr = 0;
394 	savedContextPtr = 0;
395 	listArgs = 0;
396 
397 	delete textDrawer;
398 	textDrawer = nullptr;
399 
400 	for (auto im : textDrawerImages)
401 		kernelMemory.Free(im.second.ptr);
402 	textDrawerImages.clear();
403 }
404 
PPGeBegin()405 void PPGeBegin()
406 {
407 	if (!dlPtr)
408 		return;
409 
410 	// Reset write pointers to start of command and data buffers.
411 	dlWritePtr = dlPtr;
412 	dataWritePtr = dataPtr;
413 
414 	// Set up the correct states for UI drawing
415 	WriteCmd(GE_CMD_OFFSETADDR, 0);
416 	WriteCmd(GE_CMD_ALPHABLENDENABLE, 1);
417 	WriteCmd(GE_CMD_BLENDMODE, 2 | (3 << 4));
418 	WriteCmd(GE_CMD_ALPHATESTENABLE, 0);
419 	WriteCmd(GE_CMD_COLORTESTENABLE, 0);
420 	WriteCmd(GE_CMD_ZTESTENABLE, 0);
421 	WriteCmd(GE_CMD_LIGHTINGENABLE, 0);
422 	WriteCmd(GE_CMD_FOGENABLE, 0);
423 	WriteCmd(GE_CMD_STENCILTESTENABLE, 0);
424 	WriteCmd(GE_CMD_CULLFACEENABLE, 0);
425 	WriteCmd(GE_CMD_CLEARMODE, 0);  // Normal mode
426 	WriteCmd(GE_CMD_MASKRGB, 0);
427 	WriteCmd(GE_CMD_MASKALPHA, 0);
428 
429 	PPGeSetDefaultTexture();
430 
431 	PPGeScissor(0, 0, 480, 272);
432 	WriteCmd(GE_CMD_MINZ, 0);
433 	WriteCmd(GE_CMD_MAXZ, 0xFFFF);
434 
435 	// Through mode, so we don't have to bother with matrices
436 	if (g_RemasterMode) {
437 		WriteCmd(GE_CMD_VERTEXTYPE, GE_VTYPE_TC_FLOAT | GE_VTYPE_COL_8888 | GE_VTYPE_POS_FLOAT | GE_VTYPE_THROUGH);
438 	} else {
439 		WriteCmd(GE_CMD_VERTEXTYPE, GE_VTYPE_TC_16BIT | GE_VTYPE_COL_8888 | GE_VTYPE_POS_FLOAT | GE_VTYPE_THROUGH);
440 	}
441 }
442 
PPGeEnd()443 void PPGeEnd()
444 {
445 	if (!dlPtr)
446 		return;
447 
448 	WriteCmd(GE_CMD_FINISH, 0);
449 	WriteCmd(GE_CMD_END, 0);
450 
451 	// Might've come from an old savestate.
452 	__PPGeSetupListArgs();
453 
454 	if (dataWritePtr > dataPtr) {
455 		// We actually drew something
456 		gpu->EnableInterrupts(false);
457 		u32 list = sceGeListEnQueue(dlPtr, dlWritePtr, -1, listArgs.ptr);
458 		DEBUG_LOG(SCEGE, "PPGe enqueued display list %i", list);
459 		gpu->EnableInterrupts(true);
460 	}
461 }
462 
PPGeScissor(int x1,int y1,int x2,int y2)463 void PPGeScissor(int x1, int y1, int x2, int y2) {
464 	_dbg_assert_(x1 >= 0 && x1 <= 480 && x2 >= 0 && x2 <= 480);
465 	_dbg_assert_(y1 >= 0 && y1 <= 272 && y2 >= 0 && y2 <= 272);
466 	WriteCmd(GE_CMD_SCISSOR1, (y1 << 10) | x1);
467 	WriteCmd(GE_CMD_SCISSOR2, ((y2 - 1) << 10) | (x2 - 1));
468 }
469 
PPGeScissorReset()470 void PPGeScissorReset() {
471 	PPGeScissor(0, 0, 480, 272);
472 }
473 
PPGeGetChar(const AtlasFont & atlasfont,unsigned int cval)474 static const AtlasChar *PPGeGetChar(const AtlasFont &atlasfont, unsigned int cval)
475 {
476 	const AtlasChar *c = atlasfont.getChar(cval);
477 	if (c == NULL) {
478 		// Try to use a replacement character, these come from the below table.
479 		// http://unicode.org/cldr/charts/supplemental/character_fallback_substitutions.html
480 		switch (cval) {
481 		case 0x00A0: // NO-BREAK SPACE
482 		case 0x2000: // EN QUAD
483 		case 0x2001: // EM QUAD
484 		case 0x2002: // EN SPACE
485 		case 0x2003: // EM SPACE
486 		case 0x2004: // THREE-PER-EM SPACE
487 		case 0x2005: // FOUR-PER-EM SPACE
488 		case 0x2006: // SIX-PER-EM SPACE
489 		case 0x2007: // FIGURE SPACE
490 		case 0x2008: // PUNCTUATION SPACE
491 		case 0x2009: // THIN SPACE
492 		case 0x200A: // HAIR SPACE
493 		case 0x202F: // NARROW NO-BREAK SPACE
494 		case 0x205F: // MEDIUM MATHEMATICAL
495 		case 0x3000: // IDEOGRAPHIC SPACE
496 			c = atlasfont.getChar(0x0020);
497 			break;
498 
499 		default:
500 			c = atlasfont.getChar(0xFFFD);
501 			break;
502 		}
503 		if (c == NULL)
504 			c = atlasfont.getChar('?');
505 	}
506 	return c;
507 }
508 
509 // Break a single text string into mutiple lines.
BreakLines(const char * text,const AtlasFont & atlasfont,float x,float y,PPGeAlign align,float scale,float lineHeightScale,int wrapType,float wrapWidth,bool dryRun)510 static AtlasTextMetrics BreakLines(const char *text, const AtlasFont &atlasfont, float x, float y,
511 									PPGeAlign align, float scale, float lineHeightScale, int wrapType, float wrapWidth, bool dryRun)
512 {
513 	y += atlasfont.ascend * scale;
514 	float sx = x, sy = y;
515 
516 	// TODO: Can we wrap it smartly on the screen edge?
517 	if (wrapWidth <= 0) {
518 		wrapWidth = 480.f;
519 	}
520 
521 	// used for replacing with ellipses
522 	float wrapCutoff = 8.0f;
523 	const AtlasChar *dot = PPGeGetChar(atlasfont, '.');
524 	if (dot) {
525 		wrapCutoff = dot->wx * scale * 3.0f;
526 	}
527 	float threshold = sx + wrapWidth - wrapCutoff;
528 
529 	//const float wrapGreyZone = 2.0f; // Grey zone for punctuations at line ends
530 
531 	int numLines = 1;
532 	float maxw = 0;
533 	float lineHeight = atlasfont.height * lineHeightScale;
534 	for (UTF8 utf(text); !utf.end(); )
535 	{
536 		float lineWidth = 0;
537 		bool skipRest = false;
538 		while (!utf.end())
539 		{
540 			UTF8 utfWord(utf);
541 			float nextWidth = 0;
542 			float spaceWidth = 0;
543 			int numChars = 0;
544 			bool finished = false;
545 			while (!utfWord.end() && !finished)
546 			{
547 				UTF8 utfPrev = utfWord;
548 				u32 cval = utfWord.next();
549 				const AtlasChar *ch = PPGeGetChar(atlasfont, cval);
550 				if (!ch) {
551 					continue;
552 				}
553 
554 				switch (cval) {
555 				// TODO: This list of punctuation is very incomplete.
556 				case ',':
557 				case '.':
558 				case ':':
559 				case '!':
560 				case ')':
561 				case '?':
562 				case 0x3001: // IDEOGRAPHIC COMMA
563 				case 0x3002: // IDEOGRAPHIC FULL STOP
564 				case 0x06D4: // ARABIC FULL STOP
565 				case 0xFF01: // FULLWIDTH EXCLAMATION MARK
566 				case 0xFF09: // FULLWIDTH RIGHT PARENTHESIS
567 				case 0xFF1F: // FULLWIDTH QUESTION MARK
568 					// Count this character (punctuation is so clingy), but then we're done.
569 					++numChars;
570 					nextWidth += ch->wx * scale;
571 					finished = true;
572 					break;
573 
574 				case ' ':
575 				case 0x3000: // IDEOGRAPHIC SPACE
576 					spaceWidth += ch->wx * scale;
577 					finished = true;
578 					break;
579 
580 				case '\t':
581 				case '\r':
582 				case '\n':
583 					// Ignore this character and we're done.
584 					finished = true;
585 					break;
586 
587 				default:
588 					{
589 						// CJK characters can be wrapped more freely.
590 						bool isCJK = (cval >= 0x1100 && cval <= 0x11FF); // Hangul Jamo.
591 						isCJK = isCJK || (cval >= 0x2E80 && cval <= 0x2FFF); // Kangxi Radicals etc.
592 #if 0
593 						isCJK = isCJK || (cval >= 0x3040 && cval <= 0x31FF); // Hiragana, Katakana, Hangul Compatibility Jamo etc.
594 						isCJK = isCJK || (cval >= 0x3200 && cval <= 0x32FF); // CJK Enclosed
595 						isCJK = isCJK || (cval >= 0x3300 && cval <= 0x33FF); // CJK Compatibility
596 						isCJK = isCJK || (cval >= 0x3400 && cval <= 0x4DB5); // CJK Unified Ideographs Extension A
597 #else
598 						isCJK = isCJK || (cval >= 0x3040 && cval <= 0x4DB5); // Above collapsed
599 #endif
600 						isCJK = isCJK || (cval >= 0x4E00 && cval <= 0x9FBB); // CJK Unified Ideographs
601 						isCJK = isCJK || (cval >= 0xAC00 && cval <= 0xD7AF); // Hangul Syllables
602 						isCJK = isCJK || (cval >= 0xF900 && cval <= 0xFAD9); // CJK Compatibility Ideographs
603 						isCJK = isCJK || (cval >= 0x20000 && cval <= 0x2A6D6); // CJK Unified Ideographs Extension B
604 						isCJK = isCJK || (cval >= 0x2F800 && cval <= 0x2FA1D); // CJK Compatibility Supplement
605 						if (isCJK) {
606 							if (numChars > 0) {
607 								utfWord = utfPrev;
608 								finished = true;
609 								break;
610 							}
611 						}
612 					}
613 					++numChars;
614 					nextWidth += ch->wx * scale;
615 					break;
616 				}
617 			}
618 
619 			bool useEllipsis = false;
620 			if (wrapType > 0)
621 			{
622 				if (lineWidth + nextWidth > wrapWidth || skipRest)
623 				{
624 					if (wrapType & PPGE_LINE_WRAP_WORD) {
625 						// TODO: Should check if we have had at least one other word instead.
626 						if (lineWidth > 0) {
627 							++numLines;
628 							break;
629 						}
630 					}
631 					if (wrapType & PPGE_LINE_USE_ELLIPSIS) {
632 						useEllipsis = true;
633 						if (skipRest) {
634 							numChars = 0;
635 						} else if (nextWidth < wrapCutoff) {
636 							// The word is too short, so just backspace!
637 							x = threshold;
638 						}
639 						nextWidth = 0;
640 						spaceWidth = 0;
641 						lineWidth = wrapWidth;
642 					}
643 				}
644 			}
645 			for (int i = 0; i < numChars; ++i)
646 			{
647 				u32 cval = utf.next();
648 				const AtlasChar *c = PPGeGetChar(atlasfont, cval);
649 				if (c)
650 				{
651 					if (useEllipsis && x >= threshold && dot)
652 					{
653 						if (!dryRun)
654 						{
655 							AtlasCharVertex cv;
656 							// Replace the following part with an ellipsis.
657 							cv.x = x + dot->ox * scale;
658 							cv.y = y + dot->oy * scale;
659 							cv.c = dot;
660 							char_one_line.push_back(cv);
661 							cv.x += dot->wx * scale;
662 							char_one_line.push_back(cv);
663 							cv.x += dot->wx * scale;
664 							char_one_line.push_back(cv);
665 						}
666 						skipRest = true;
667 						break;
668 					}
669 					if (!dryRun)
670 					{
671 						AtlasCharVertex cv;
672 						cv.x = x + c->ox * scale;
673 						cv.y = y + c->oy * scale;
674 						cv.c = c;
675 						char_one_line.push_back(cv);
676 					}
677 					x += c->wx * scale;
678 				}
679 			}
680 			lineWidth += nextWidth;
681 
682 			u32 cval = utf.end() ? 0 : utf.next();
683 			if (spaceWidth > 0)
684 			{
685 				if (!dryRun)
686 				{
687 					// No need to check c.
688 					const AtlasChar *c = PPGeGetChar(atlasfont, cval);
689 					AtlasCharVertex cv;
690 					cv.x = x + c->ox * scale;
691 					cv.y = y + c->oy * scale;
692 					cv.c = c;
693 					char_one_line.push_back(cv);
694 				}
695 				x += spaceWidth;
696 				lineWidth += spaceWidth;
697 				if (wrapType > 0 && lineWidth > wrapWidth) {
698 					lineWidth = wrapWidth;
699 				}
700 			}
701 			else if (cval == '\n') {
702 				++numLines;
703 				break;
704 			}
705 			utf = utfWord;
706 		}
707 		y += lineHeight;
708 		x = sx;
709 		if (lineWidth > maxw) {
710 			maxw = lineWidth;
711 		}
712 		if (!dryRun)
713 		{
714 			char_lines.push_back(char_one_line);
715 			char_one_line.clear();
716 		}
717 	}
718 
719 	const float w = maxw;
720 	const float h = (float)numLines * lineHeight;
721 	if (align & PPGeAlign::ANY) {
722 		if (!dryRun) {
723 			for (auto i = char_lines.begin(); i != char_lines.end(); ++i) {
724 				for (auto j = i->begin(); j != i->end(); ++j) {
725 					if (align & PPGeAlign::BOX_HCENTER) j->x -= w / 2.0f;
726 					else if (align & PPGeAlign::BOX_RIGHT) j->x -= w;
727 
728 					if (align & PPGeAlign::BOX_VCENTER) j->y -= h / 2.0f;
729 					else if (align & PPGeAlign::BOX_BOTTOM) j->y -= h;
730 				}
731 			}
732 		}
733 		if (align & PPGeAlign::BOX_HCENTER) sx -= w / 2.0f;
734 		else if (align & PPGeAlign::BOX_RIGHT) sx -= w;
735 		if (align & PPGeAlign::BOX_VCENTER) sy -= h / 2.0f;
736 		else if (align & PPGeAlign::BOX_BOTTOM) sy -= h;
737 	}
738 
739 	AtlasTextMetrics metrics = { sx, sy, w, lineHeight, scale, numLines };
740 	return metrics;
741 }
742 
HasTextDrawer()743 static bool HasTextDrawer() {
744 	// We create this on first use so it's on the correct thread.
745 	if (textDrawerInited) {
746 		return textDrawer != nullptr;
747 	}
748 
749 	// Should we pass a draw_? Yes! UWP requires it.
750 	textDrawer = TextDrawer::Create(g_draw);
751 	if (textDrawer) {
752 		textDrawer->SetFontScale(1.0f, 1.0f);
753 		textDrawer->SetForcedDPIScale(1.0f);
754 		textDrawer->SetFont(g_Config.sFont.c_str(), 18, 0);
755 	}
756 	textDrawerInited = true;
757 	return textDrawer != nullptr;
758 }
759 
PPGeMeasureText(float * w,float * h,const char * text,float scale,int WrapType,int wrapWidth)760 void PPGeMeasureText(float *w, float *h, const char *text, float scale, int WrapType, int wrapWidth) {
761 	if (HasTextDrawer()) {
762 		float mw, mh;
763 		textDrawer->SetFontScale(scale, scale);
764 		int dtalign = (WrapType & PPGE_LINE_WRAP_WORD) ? FLAG_WRAP_TEXT : 0;
765 		if (WrapType & PPGE_LINE_USE_ELLIPSIS)
766 			dtalign |= FLAG_ELLIPSIZE_TEXT;
767 		Bounds b(0, 0, wrapWidth <= 0 ? 480.0f : wrapWidth, 272.0f);
768 		textDrawer->MeasureStringRect(text, strlen(text), b, &mw, &mh, dtalign);
769 
770 		if (w)
771 			*w = mw;
772 		if (h)
773 			*h = mh;
774 		return;
775 	}
776 
777 	if (!g_ppge_atlas.IsMetadataLoaded() || g_ppge_atlas.num_fonts < 1) {
778 		if (w)
779 			*w = 0;
780 		if (h)
781 			*h = 0;
782 		return;
783 	}
784 
785 	const AtlasFont &atlasfont = g_ppge_atlas.fonts[0];
786 	AtlasTextMetrics metrics = BreakLines(text, atlasfont, 0, 0, PPGeAlign::BOX_TOP, scale, scale, WrapType, wrapWidth, true);
787 	if (w) *w = metrics.maxWidth;
788 	if (h) *h = metrics.lineHeight * metrics.numLines;
789 }
790 
PPGePrepareText(const char * text,float x,float y,PPGeAlign align,float scale,float lineHeightScale,int WrapType,int wrapWidth)791 void PPGePrepareText(const char *text, float x, float y, PPGeAlign align, float scale, float lineHeightScale, int WrapType, int wrapWidth)
792 {
793 	const AtlasFont &atlasfont = g_ppge_atlas.fonts[0];
794 	if (!g_ppge_atlas.IsMetadataLoaded() || g_ppge_atlas.num_fonts < 1) {
795 		return;
796 	}
797 	char_lines_metrics = BreakLines(text, atlasfont, x, y, align, scale, lineHeightScale, WrapType, wrapWidth, false);
798 }
799 
PPGeResetCurrentText()800 static void PPGeResetCurrentText() {
801 	char_one_line.clear();
802 	char_lines.clear();
803 	AtlasTextMetrics zeroBox = { 0 };
804 	char_lines_metrics = zeroBox;
805 }
806 
807 // Draws some text using the one font we have.
808 // Mostly rewritten.
PPGeDrawCurrentText(u32 color)809 void PPGeDrawCurrentText(u32 color)
810 {
811 	if (dlPtr)
812 	{
813 		float scale = char_lines_metrics.scale;
814 		BeginVertexData();
815 		for (auto i = char_lines.begin(); i != char_lines.end(); ++i)
816 		{
817 			for (auto j = i->begin(); j != i->end(); ++j)
818 			{
819 				float cx1 = j->x;
820 				float cy1 = j->y;
821 				const AtlasChar &c = *j->c;
822 				float cx2 = cx1 + c.pw * scale;
823 				float cy2 = cy1 + c.ph * scale;
824 				Vertex(cx1, cy1, c.sx, c.sy, atlasWidth, atlasHeight, color);
825 				Vertex(cx2, cy2, c.ex, c.ey, atlasWidth, atlasHeight, color);
826 			}
827 		}
828 		EndVertexDataAndDraw(GE_PRIM_RECTANGLES);
829 	}
830 	PPGeResetCurrentText();
831 }
832 
833 // Return a value such that (1 << value) >= x
GetPow2(int x)834 int GetPow2(int x) {
835 #ifdef __GNUC__
836 	int ret = 31 - __builtin_clz(x | 1);
837 	if ((1 << ret) < x)
838 #else
839 	int ret = 0;
840 	while ((1 << ret) < x)
841 #endif
842 		ret++;
843 	return ret;
844 }
845 
PPGeGetTextImage(const char * text,const PPGeStyle & style,float maxWidth,bool wrap)846 static PPGeTextDrawerImage PPGeGetTextImage(const char *text, const PPGeStyle &style, float maxWidth, bool wrap) {
847 	int tdalign = 0;
848 	tdalign |= FLAG_ELLIPSIZE_TEXT;
849 	if (wrap) {
850 		tdalign |= FLAG_WRAP_TEXT;
851 	}
852 
853 	PPGeTextDrawerCacheKey key{ text, tdalign, maxWidth / style.scale };
854 	PPGeTextDrawerImage im{};
855 
856 	auto cacheItem = textDrawerImages.find(key);
857 	if (cacheItem != textDrawerImages.end()) {
858 		im = cacheItem->second;
859 		cacheItem->second.entry.lastUsedFrame = gpuStats.numFlips;
860 	} else {
861 		std::vector<uint8_t> bitmapData;
862 		textDrawer->SetFontScale(style.scale, style.scale);
863 		Bounds b(0, 0, maxWidth, 272.0f);
864 		std::string cleaned = ReplaceAll(text, "\r", "");
865 		textDrawer->DrawStringBitmapRect(bitmapData, im.entry, Draw::DataFormat::R8_UNORM, cleaned.c_str(), b, tdalign);
866 
867 		int bufwBytes = ((im.entry.bmWidth + 31) / 32) * 16;
868 		u32 sz = bufwBytes * (im.entry.bmHeight + 1);
869 		u32 origSz = sz;
870 		im.ptr = __PPGeDoAlloc(sz, true, "PPGeText");
871 
872 		if (bitmapData.size() & 1)
873 			bitmapData.resize(bitmapData.size() + 1);
874 
875 		if (im.ptr) {
876 			int wBytes = (im.entry.bmWidth + 1) / 2;
877 			u8 *ramPtr = (u8 *)Memory::GetPointer(im.ptr);
878 			for (int y = 0; y < im.entry.bmHeight; ++y) {
879 				for (int x = 0; x < wBytes; ++x) {
880 					uint8_t c1 = bitmapData[y * im.entry.bmWidth + x * 2];
881 					uint8_t c2 = bitmapData[y * im.entry.bmWidth + x * 2 + 1];
882 					// Convert this to 4-bit palette values.
883 					ramPtr[y * bufwBytes + x] = (c2 & 0xF0) | (c1 >> 4);
884 				}
885 				if (bufwBytes != wBytes) {
886 					memset(ramPtr + y * bufwBytes + wBytes, 0, bufwBytes - wBytes);
887 				}
888 			}
889 			memset(ramPtr + im.entry.bmHeight * bufwBytes, 0, bufwBytes + sz - origSz);
890 		}
891 
892 		im.entry.lastUsedFrame = gpuStats.numFlips;
893 		textDrawerImages[key] = im;
894 	}
895 
896 	return im;
897 }
898 
PPGeDrawTextImage(PPGeTextDrawerImage im,float x,float y,const PPGeStyle & style)899 static void PPGeDrawTextImage(PPGeTextDrawerImage im, float x, float y, const PPGeStyle &style) {
900 	if (!im.ptr) {
901 		return;
902 	}
903 
904 	int bufw = ((im.entry.bmWidth + 31) / 32) * 32;
905 	int wp2 = GetPow2(im.entry.bmWidth);
906 	int hp2 = GetPow2(im.entry.bmHeight);
907 	WriteCmd(GE_CMD_TEXADDR0, im.ptr & 0xFFFFF0);
908 	WriteCmd(GE_CMD_TEXBUFWIDTH0, bufw | ((im.ptr & 0xFF000000) >> 8));
909 	WriteCmd(GE_CMD_TEXSIZE0, wp2 | (hp2 << 8));
910 	WriteCmd(GE_CMD_TEXFLUSH, 0);
911 
912 	float w = im.entry.width * style.scale;
913 	float h = im.entry.height * style.scale;
914 
915 	if (style.align & PPGeAlign::BOX_HCENTER)
916 		x -= w / 2.0f;
917 	else if (style.align & PPGeAlign::BOX_RIGHT)
918 		x -= w;
919 	if (style.align & PPGeAlign::BOX_VCENTER)
920 		y -= h / 2.0f;
921 	else if (style.align & PPGeAlign::BOX_BOTTOM)
922 		y -= h;
923 
924 	BeginVertexData();
925 	float u1 = (float)im.entry.width / (1 << wp2);
926 	float v1 = (float)im.entry.height / (1 << hp2);
927 	if (style.hasShadow) {
928 		// Draw more shadows for a blurrier shadow.
929 		for (float dy = 0.0f; dy <= 2.0f; dy += 1.0f) {
930 			for (float dx = 0.0f; dx <= 1.0f; dx += 0.5f) {
931 				if (dx == 0.0f && dy == 0.0f)
932 					continue;
933 				Vertex(x + dx, y + dy, 0, 0, 1 << wp2, 1 << hp2, alphaMul(style.shadowColor, 0.35f));
934 				Vertex(x + dx + w, y + dy + h, u1, v1, 1 << wp2, 1 << hp2, alphaMul(style.shadowColor, 0.35f));
935 			}
936 		}
937 	}
938 	Vertex(x, y, 0, 0, 1 << wp2, 1 << hp2, style.color);
939 	Vertex(x + w, y + h, u1, v1, 1 << wp2, 1 << hp2, style.color);
940 	EndVertexDataAndDraw(GE_PRIM_RECTANGLES);
941 
942 	PPGeSetDefaultTexture();
943 }
944 
PPGeDecimateTextImages(int age)945 static void PPGeDecimateTextImages(int age) {
946 	// Do this always, in case the platform has no TextDrawer but save state did.
947 	for (auto it = textDrawerImages.begin(); it != textDrawerImages.end(); ) {
948 		if (gpuStats.numFlips - it->second.entry.lastUsedFrame >= age) {
949 			kernelMemory.Free(it->second.ptr);
950 			it = textDrawerImages.erase(it);
951 		} else {
952 			++it;
953 		}
954 	}
955 }
956 
PPGeSanitizeText(const std::string & text)957 static std::string PPGeSanitizeText(const std::string &text) {
958 	return SanitizeUTF8(text);
959 }
960 
PPGeDrawText(const char * text,float x,float y,const PPGeStyle & style)961 void PPGeDrawText(const char *text, float x, float y, const PPGeStyle &style) {
962 	if (!text) {
963 		return;
964 	}
965 	std::string str = PPGeSanitizeText(text);
966 	if (str.empty()) {
967 		return;
968 	}
969 
970 	if (HasTextDrawer()) {
971 		PPGeTextDrawerImage im = PPGeGetTextImage(str.c_str(), style, 480.0f - x, false);
972 		if (im.ptr) {
973 			PPGeDrawTextImage(im, x, y, style);
974 			return;
975 		}
976 	}
977 
978 	if (style.hasShadow) {
979 		// This doesn't have the nicer shadow because it's so many verts.
980 		PPGePrepareText(text, x + 1, y + 2, style.align, style.scale, style.scale, PPGE_LINE_USE_ELLIPSIS);
981 		PPGeDrawCurrentText(style.shadowColor);
982 	}
983 
984 	PPGePrepareText(text, x, y, style.align, style.scale, style.scale, PPGE_LINE_USE_ELLIPSIS);
985 	PPGeDrawCurrentText(style.color);
986 }
987 
StripTrailingWhite(const std::string & s)988 static std::string StripTrailingWhite(const std::string &s) {
989 	size_t lastChar = s.find_last_not_of(" \t\r\n");
990 	if (lastChar != s.npos) {
991 		return s.substr(0, lastChar + 1);
992 	}
993 	return s;
994 }
995 
CropLinesToCount(const std::string & s,int numLines)996 static std::string CropLinesToCount(const std::string &s, int numLines) {
997 	std::vector<std::string> lines;
998 	SplitString(s, '\n', lines);
999 	if ((int)lines.size() <= numLines) {
1000 		return s;
1001 	}
1002 
1003 	size_t len = 0;
1004 	for (int i = 0; i < numLines; ++i) {
1005 		len += lines[i].length() + 1;
1006 	}
1007 
1008 	return s.substr(0, len);
1009 }
1010 
PPGeDrawTextWrapped(const char * text,float x,float y,float wrapWidth,float wrapHeight,const PPGeStyle & style)1011 void PPGeDrawTextWrapped(const char *text, float x, float y, float wrapWidth, float wrapHeight, const PPGeStyle &style) {
1012 	std::string s = PPGeSanitizeText(text);
1013 	if (wrapHeight != 0.0f) {
1014 		s = StripTrailingWhite(s);
1015 	}
1016 
1017 	int zoom = (PSP_CoreParameter().pixelHeight + 479) / 480;
1018 	zoom = std::min(zoom, PSP_CoreParameter().renderScaleFactor);
1019 	float maxScaleDown = zoom == 1 ? 1.3f : 2.0f;
1020 
1021 	if (HasTextDrawer()) {
1022 		float actualWidth, actualHeight;
1023 		Bounds b(0, 0, wrapWidth <= 0 ? 480.0f - x : wrapWidth, wrapHeight);
1024 		int tdalign = 0;
1025 		textDrawer->SetFontScale(style.scale, style.scale);
1026 		textDrawer->MeasureStringRect(s.c_str(), s.size(), b, &actualWidth, &actualHeight, tdalign | FLAG_WRAP_TEXT);
1027 
1028 		// Check if we need to scale the text down to fit better.
1029 		PPGeStyle adjustedStyle = style;
1030 		if (wrapHeight != 0.0f && actualHeight > wrapHeight) {
1031 			// Cheap way to get the line height.
1032 			float oneLine, twoLines;
1033 			textDrawer->MeasureString("|", 1, &actualWidth, &oneLine);
1034 			textDrawer->MeasureStringRect("|\n|", 3, Bounds(0, 0, 480, 272), &actualWidth, &twoLines);
1035 
1036 			float lineHeight = twoLines - oneLine;
1037 			if (actualHeight > wrapHeight * maxScaleDown) {
1038 				float maxLines = floor(wrapHeight * maxScaleDown / lineHeight);
1039 				actualHeight = (maxLines + 1) * lineHeight;
1040 				// Add an ellipsis if it's just too long to be readable.
1041 				// On a PSP, it does this without scaling it down.
1042 				s = StripTrailingWhite(CropLinesToCount(s, (int)maxLines)) + "\n...";
1043 			}
1044 
1045 			adjustedStyle.scale *= wrapHeight / actualHeight;
1046 		}
1047 
1048 		PPGeTextDrawerImage im = PPGeGetTextImage(s.c_str(), adjustedStyle, wrapWidth <= 0 ? 480.0f - x : wrapWidth, true);
1049 		if (im.ptr) {
1050 			PPGeDrawTextImage(im, x, y, adjustedStyle);
1051 			return;
1052 		}
1053 	}
1054 
1055 	int sx = style.hasShadow ? 1 : 0;
1056 	int sy = style.hasShadow ? 2 : 0;
1057 	PPGePrepareText(s.c_str(), x + sx, y + sy, style.align, style.scale, style.scale, PPGE_LINE_USE_ELLIPSIS | PPGE_LINE_WRAP_WORD, wrapWidth);
1058 
1059 	float scale = style.scale;
1060 	float lineHeightScale = style.scale;
1061 	float actualHeight = char_lines_metrics.lineHeight * char_lines_metrics.numLines;
1062 	if (wrapHeight != 0.0f && actualHeight > wrapHeight) {
1063 		if (actualHeight > wrapHeight * maxScaleDown) {
1064 			float maxLines = floor(wrapHeight * maxScaleDown / char_lines_metrics.lineHeight);
1065 			actualHeight = (maxLines + 1) * char_lines_metrics.lineHeight;
1066 			// Add an ellipsis if it's just too long to be readable.
1067 			// On a PSP, it does this without scaling it down.
1068 			s = StripTrailingWhite(CropLinesToCount(s, (int)maxLines)) + "\n...";
1069 		}
1070 
1071 		// Measure the text again after scaling down.
1072 		PPGeResetCurrentText();
1073 		float reduced = style.scale * wrapHeight / actualHeight;
1074 		// Try to keep the font as large as possible, so reduce the line height some.
1075 		scale = reduced * 1.15f;
1076 		lineHeightScale = reduced;
1077 		PPGePrepareText(s.c_str(), x + sx, y + sy, style.align, scale, lineHeightScale, PPGE_LINE_USE_ELLIPSIS | PPGE_LINE_WRAP_WORD, wrapWidth);
1078 	}
1079 	if (style.hasShadow) {
1080 		// This doesn't have the nicer shadow because it's so many verts.
1081 		PPGeDrawCurrentText(style.shadowColor);
1082 		PPGePrepareText(s.c_str(), x, y, style.align, scale, lineHeightScale, PPGE_LINE_USE_ELLIPSIS | PPGE_LINE_WRAP_WORD, wrapWidth);
1083 	}
1084 	PPGeDrawCurrentText(style.color);
1085 }
1086 
1087 // Draws a "4-patch" for button-like things that can be resized
PPGeDraw4Patch(ImageID atlasImage,float x,float y,float w,float h,u32 color)1088 void PPGeDraw4Patch(ImageID atlasImage, float x, float y, float w, float h, u32 color) {
1089 	if (!dlPtr)
1090 		return;
1091 	const AtlasImage *img = g_ppge_atlas.getImage(atlasImage);
1092 	if (!img)
1093 		return;
1094 	float borderx = img->w / 20;
1095 	float bordery = img->h / 20;
1096 	float u1 = img->u1, uhalf = (img->u1 + img->u2) / 2, u2 = img->u2;
1097 	float v1 = img->v1, vhalf = (img->v1 + img->v2) / 2, v2 = img->v2;
1098 	float xmid1 = x + borderx;
1099 	float xmid2 = x + w - borderx;
1100 	float ymid1 = y + bordery;
1101 	float ymid2 = y + h - bordery;
1102 	float x2 = x + w;
1103 	float y2 = y + h;
1104 	BeginVertexData();
1105 	// Top row
1106 	Vertex(x, y, u1, v1, atlasWidth, atlasHeight, color);
1107 	Vertex(xmid1, ymid1, uhalf, vhalf, atlasWidth, atlasHeight, color);
1108 	Vertex(xmid1, y, uhalf, v1, atlasWidth, atlasHeight, color);
1109 	Vertex(xmid2, ymid1, uhalf, vhalf, atlasWidth, atlasHeight, color);
1110 	Vertex(xmid2, y, uhalf, v1, atlasWidth, atlasHeight, color);
1111 	Vertex(x2, ymid1, u2, vhalf, atlasWidth, atlasHeight, color);
1112 	// Middle row
1113 	Vertex(x, ymid1, u1, vhalf, atlasWidth, atlasHeight, color);
1114 	Vertex(xmid1, ymid2, uhalf, vhalf, atlasWidth, atlasHeight, color);
1115 	Vertex(xmid1, ymid1, uhalf, vhalf, atlasWidth, atlasHeight, color);
1116 	Vertex(xmid2, ymid2, uhalf, vhalf, atlasWidth, atlasHeight, color);
1117 	Vertex(xmid2, ymid1, uhalf, vhalf, atlasWidth, atlasHeight, color);
1118 	Vertex(x2, ymid2, u2, v2, atlasWidth, atlasHeight, color);
1119 	// Bottom row
1120 	Vertex(x, ymid2, u1, vhalf, atlasWidth, atlasHeight, color);
1121 	Vertex(xmid1, y2, uhalf, v2, atlasWidth, atlasHeight, color);
1122 	Vertex(xmid1, ymid2, uhalf, vhalf, atlasWidth, atlasHeight, color);
1123 	Vertex(xmid2, y2, uhalf, v2, atlasWidth, atlasHeight, color);
1124 	Vertex(xmid2, ymid2, uhalf, vhalf, atlasWidth, atlasHeight, color);
1125 	Vertex(x2, y2, u2, v2, atlasWidth, atlasHeight, color);
1126 	EndVertexDataAndDraw(GE_PRIM_RECTANGLES);
1127 }
1128 
PPGeDrawRect(float x1,float y1,float x2,float y2,u32 color)1129 void PPGeDrawRect(float x1, float y1, float x2, float y2, u32 color) {
1130 	if (!dlPtr)
1131 		return;
1132 
1133 	WriteCmd(GE_CMD_TEXTUREMAPENABLE, 0);
1134 
1135 	BeginVertexData();
1136 	Vertex(x1, y1, 0, 0, 0, 0, color);
1137 	Vertex(x2, y2, 0, 0, 0, 0, color);
1138 	EndVertexDataAndDraw(GE_PRIM_RECTANGLES);
1139 
1140 	WriteCmd(GE_CMD_TEXTUREMAPENABLE, 1);
1141 }
1142 
1143 // Just blits an image to the screen, multiplied with the color.
PPGeDrawImage(ImageID atlasImage,float x,float y,const PPGeStyle & style)1144 void PPGeDrawImage(ImageID atlasImage, float x, float y, const PPGeStyle &style) {
1145 	if (!dlPtr)
1146 		return;
1147 
1148 	const AtlasImage *img = g_ppge_atlas.getImage(atlasImage);
1149 	if (!img) {
1150 		return;
1151 	}
1152 	float w = img->w;
1153 	float h = img->h;
1154 	BeginVertexData();
1155 	if (style.hasShadow) {
1156 		for (float dy = 0.0f; dy <= 2.0f; dy += 1.0f) {
1157 			for (float dx = 0.0f; dx <= 1.0f; dx += 0.5f) {
1158 				if (dx == 0.0f && dy == 0.0f)
1159 					continue;
1160 				Vertex(x + dx, y + dy, img->u1, img->v1, atlasWidth, atlasHeight, alphaMul(style.shadowColor, 0.35f));
1161 				Vertex(x + dx + w, y + dy + h, img->u2, img->v2, atlasWidth, atlasHeight, alphaMul(style.shadowColor, 0.35f));
1162 			}
1163 		}
1164 	}
1165 	Vertex(x, y, img->u1, img->v1, atlasWidth, atlasHeight, style.color);
1166 	Vertex(x + w, y + h, img->u2, img->v2, atlasWidth, atlasHeight, style.color);
1167 	EndVertexDataAndDraw(GE_PRIM_RECTANGLES);
1168 }
1169 
PPGeDrawImage(ImageID atlasImage,float x,float y,float w,float h,const PPGeStyle & style)1170 void PPGeDrawImage(ImageID atlasImage, float x, float y, float w, float h, const PPGeStyle &style) {
1171 	if (!dlPtr)
1172 		return;
1173 
1174 	const AtlasImage *img = g_ppge_atlas.getImage(atlasImage);
1175 	if (!img) {
1176 		return;
1177 	}
1178 	BeginVertexData();
1179 	if (style.hasShadow) {
1180 		for (float dy = 0.0f; dy <= 2.0f; dy += 1.0f) {
1181 			for (float dx = 0.0f; dx <= 1.0f; dx += 0.5f) {
1182 				if (dx == 0.0f && dy == 0.0f)
1183 					continue;
1184 				Vertex(x + dx, y + dy, img->u1, img->v1, atlasWidth, atlasHeight, alphaMul(style.shadowColor, 0.35f));
1185 				Vertex(x + dx + w, y + dy + h, img->u2, img->v2, atlasWidth, atlasHeight, alphaMul(style.shadowColor, 0.35f));
1186 			}
1187 		}
1188 	}
1189 	Vertex(x, y, img->u1, img->v1, atlasWidth, atlasHeight, style.color);
1190 	Vertex(x + w, y + h, img->u2, img->v2, atlasWidth, atlasHeight, style.color);
1191 	EndVertexDataAndDraw(GE_PRIM_RECTANGLES);
1192 }
1193 
PPGeDrawImage(float x,float y,float w,float h,float u1,float v1,float u2,float v2,int tw,int th,const PPGeImageStyle & style)1194 void PPGeDrawImage(float x, float y, float w, float h, float u1, float v1, float u2, float v2, int tw, int th, const PPGeImageStyle &style) {
1195 	if (!dlPtr)
1196 		return;
1197 	BeginVertexData();
1198 	Vertex(x, y, u1, v1, tw, th, style.color);
1199 	Vertex(x + w, y + h, u2, v2, tw, th, style.color);
1200 	EndVertexDataAndDraw(GE_PRIM_RECTANGLES);
1201 }
1202 
PPGeSetDefaultTexture()1203 void PPGeSetDefaultTexture()
1204 {
1205 	WriteCmd(GE_CMD_TEXTUREMAPENABLE, 1);
1206 	int wp2 = GetPow2(atlasWidth);
1207 	int hp2 = GetPow2(atlasHeight);
1208 	WriteCmd(GE_CMD_CLUTADDR, palette.ptr & 0xFFFFF0);
1209 	WriteCmd(GE_CMD_CLUTADDRUPPER, (palette.ptr & 0xFF000000) >> 8);
1210 	WriteCmd(GE_CMD_CLUTFORMAT, 0x00FF02);
1211 	WriteCmd(GE_CMD_LOADCLUT, 2);
1212 	WriteCmd(GE_CMD_TEXSIZE0, wp2 | (hp2 << 8));
1213 	WriteCmd(GE_CMD_TEXMAPMODE, 0 | (1 << 8));
1214 	WriteCmd(GE_CMD_TEXMODE, 0);
1215 	WriteCmd(GE_CMD_TEXFORMAT, GE_TFMT_CLUT4);  // 4-bit CLUT
1216 	WriteCmd(GE_CMD_TEXFILTER, (1 << 8) | 1);   // mag = LINEAR min = LINEAR
1217 	WriteCmd(GE_CMD_TEXWRAP, (1 << 8) | 1);  // clamp texture wrapping
1218 	WriteCmd(GE_CMD_TEXFUNC, (0 << 16) | (1 << 8) | 0);  // RGBA texture reads, modulate, no color doubling
1219 	WriteCmd(GE_CMD_TEXADDR0, atlasPtr & 0xFFFFF0);
1220 	WriteCmd(GE_CMD_TEXBUFWIDTH0, atlasWidth | ((atlasPtr & 0xFF000000) >> 8));
1221 	WriteCmd(GE_CMD_TEXFLUSH, 0);
1222 }
1223 
PPGeSetTexture(u32 dataAddr,int width,int height)1224 void PPGeSetTexture(u32 dataAddr, int width, int height)
1225 {
1226 	WriteCmd(GE_CMD_TEXTUREMAPENABLE, 1);
1227 	int wp2 = GetPow2(width);
1228 	int hp2 = GetPow2(height);
1229 	WriteCmd(GE_CMD_TEXSIZE0, wp2 | (hp2 << 8));
1230 	WriteCmd(GE_CMD_TEXMAPMODE, 0 | (1 << 8));
1231 	WriteCmd(GE_CMD_TEXMODE, 0);
1232 	WriteCmd(GE_CMD_TEXFORMAT, GE_TFMT_8888);  // 4444
1233 	WriteCmd(GE_CMD_TEXFILTER, (1 << 8) | 1);   // mag = LINEAR min = LINEAR
1234 	WriteCmd(GE_CMD_TEXWRAP, (1 << 8) | 1);  // clamp texture wrapping
1235 	WriteCmd(GE_CMD_TEXFUNC, (0 << 16) | (1 << 8) | 0);  // RGBA texture reads, modulate, no color doubling
1236 	WriteCmd(GE_CMD_TEXADDR0, dataAddr & 0xFFFFF0);
1237 	WriteCmd(GE_CMD_TEXBUFWIDTH0, width | ((dataAddr & 0xFF000000) >> 8));
1238 	WriteCmd(GE_CMD_TEXFLUSH, 0);
1239 }
1240 
PPGeDisableTexture()1241 void PPGeDisableTexture()
1242 {
1243 	WriteCmd(GE_CMD_TEXTUREMAPENABLE, 0);
1244 }
1245 
1246 std::vector<PPGeImage *> PPGeImage::loadedTextures_;
1247 
PPGeImage(const std::string & pspFilename)1248 PPGeImage::PPGeImage(const std::string &pspFilename)
1249 	: filename_(pspFilename), texture_(0) {
1250 }
1251 
PPGeImage(u32 pngPointer,size_t pngSize)1252 PPGeImage::PPGeImage(u32 pngPointer, size_t pngSize)
1253 	: filename_(""), png_(pngPointer), size_(pngSize), texture_(0) {
1254 }
1255 
~PPGeImage()1256 PPGeImage::~PPGeImage() {
1257 	Free();
1258 }
1259 
Load()1260 bool PPGeImage::Load() {
1261 	Free();
1262 
1263 	// In case it fails to load.
1264 	width_ = 0;
1265 	height_ = 0;
1266 
1267 	unsigned char *textureData;
1268 	int success;
1269 	if (filename_.empty()) {
1270 		success = pngLoadPtr(Memory::GetPointer(png_), size_, &width_, &height_, &textureData);
1271 	} else {
1272 		std::vector<u8> pngData;
1273 		if (pspFileSystem.ReadEntireFile(filename_, pngData) < 0) {
1274 			WARN_LOG(SCEGE, "Bad PPGeImage - cannot load file");
1275 			return false;
1276 		}
1277 
1278 		success = pngLoadPtr((const unsigned char *)&pngData[0], pngData.size(), &width_, &height_, &textureData);
1279 	}
1280 	if (!success) {
1281 		WARN_LOG(SCEGE, "Bad PPGeImage - not a valid png");
1282 		return false;
1283 	}
1284 
1285 	u32 dataSize = width_ * height_ * 4;
1286 	u32 texSize = dataSize + width_ * 4;
1287 	texture_ = __PPGeDoAlloc(texSize, true, "Savedata Icon");
1288 	if (texture_ == 0) {
1289 		free(textureData);
1290 		WARN_LOG(SCEGE, "Bad PPGeImage - unable to allocate space for texture");
1291 		return false;
1292 	}
1293 
1294 	Memory::Memcpy(texture_, textureData, dataSize, "PPGeTex");
1295 	Memory::Memset(texture_ + dataSize, 0, texSize - dataSize, "PPGeTexClear");
1296 	free(textureData);
1297 
1298 	lastFrame_ = gpuStats.numFlips;
1299 	loadedTextures_.push_back(this);
1300 	return true;
1301 }
1302 
Free()1303 void PPGeImage::Free() {
1304 	if (texture_ != 0) {
1305 		kernelMemory.Free(texture_);
1306 		texture_ = 0;
1307 		loadedTextures_.erase(std::remove(loadedTextures_.begin(), loadedTextures_.end(), this), loadedTextures_.end());
1308 	}
1309 }
1310 
DoState(PointerWrap & p)1311 void PPGeImage::DoState(PointerWrap &p) {
1312 	auto s = p.Section("PPGeImage", 1);
1313 	if (!s)
1314 		return;
1315 
1316 	Do(p, filename_);
1317 	Do(p, png_);
1318 	Do(p, size_);
1319 	Do(p, texture_);
1320 	Do(p, width_);
1321 	Do(p, height_);
1322 	Do(p, lastFrame_);
1323 }
1324 
CompatLoad(u32 texture,int width,int height)1325 void PPGeImage::CompatLoad(u32 texture, int width, int height) {
1326 	// Won't be reloadable, so don't add to loadedTextures_.
1327 	texture_ = texture;
1328 	width_ = width;
1329 	height_ = height;
1330 }
1331 
Decimate(int age)1332 void PPGeImage::Decimate(int age) {
1333 	int tooOldFrame = gpuStats.numFlips - age;
1334 	for (size_t i = 0; i < loadedTextures_.size(); ++i) {
1335 		if (loadedTextures_[i]->lastFrame_ < tooOldFrame) {
1336 			loadedTextures_[i]->Free();
1337 			// That altered loadedTextures_.
1338 			--i;
1339 		}
1340 	}
1341 }
1342 
SetTexture()1343 void PPGeImage::SetTexture() {
1344 	if (texture_ == 0) {
1345 		Decimate();
1346 		Load();
1347 	}
1348 
1349 	if (texture_ != 0) {
1350 		lastFrame_ = gpuStats.numFlips;
1351 		PPGeSetTexture(texture_, width_, height_);
1352 	} else {
1353 		PPGeDisableTexture();
1354 	}
1355 }
1356 
PPGeNotifyFrame()1357 void PPGeNotifyFrame() {
1358 	if (textDrawer) {
1359 		textDrawer->OncePerFrame();
1360 	}
1361 
1362 	PPGeDecimateTextImages();
1363 	PPGeImage::Decimate();
1364 
1365 	if (atlasRequiresReset) {
1366 		__PPGeShutdown();
1367 		__PPGeInit();
1368 	}
1369 }
1370