1 #include <stdexcept>
2 
3 #include "Font.h"
4 #include "Local.h"
5 #include "WorldDef.h"
6 #include "RenderWorld.h"
7 #include "VSurface.h"
8 #include "Render_Dirty.h"
9 #include "SysUtil.h"
10 #include "Video.h"
11 #include "VObject_Blitters.h"
12 #include <stdarg.h>
13 #include "MemMan.h"
14 #include "Debug.h"
15 #include "UILayout.h"
16 
17 #include <vector>
18 
19 #include "Logger.h"
20 
21 
22 #define BACKGROUND_BUFFERS 500
23 
24 
25 // Struct for backgrounds
26 struct BACKGROUND_SAVE
27 {
28 	BOOLEAN         fAllocated;
29 	BOOLEAN         fFilled;
30 	BOOLEAN         fFreeMemory;
31 	BackgroundFlags uiFlags;
32 	UINT16*         pSaveArea;
33 	UINT16*         pZSaveArea;
34 	INT16           sLeft;
35 	INT16           sTop;
36 	INT16           sRight;
37 	INT16           sBottom;
38 	INT16           sWidth;
39 	INT16           sHeight;
40 	BOOLEAN         fPendingDelete;
41 	BOOLEAN         fDisabled;
42 };
43 
44 
45 static std::vector<BACKGROUND_SAVE*> gBackSaves;
46 static UINT32 guiNumBackSaves=0;
47 
48 static VIDEO_OVERLAY* gVideoOverlays;
49 
50 
51 #define FOR_EACH_VIDEO_OVERLAY(iter)                                  \
52 	for (VIDEO_OVERLAY* iter = gVideoOverlays; iter; iter = iter->next) \
53 		if (iter->fDisabled) continue; else
54 
55 #define FOR_EACH_VIDEO_OVERLAY_SAFE(iter)                             \
56 	for (VIDEO_OVERLAY* iter = gVideoOverlays, * iter##__next; iter; iter = iter##__next) \
57 		if (iter##__next = iter->next, iter->fDisabled) continue; else
58 
59 
60 SGPRect gDirtyClipRect;
61 
62 
63 static BOOLEAN gfViewportDirty = FALSE;
64 
65 
AddBaseDirtyRect(INT32 iLeft,INT32 iTop,INT32 iRight,INT32 iBottom)66 void AddBaseDirtyRect(INT32 iLeft, INT32 iTop, INT32 iRight, INT32 iBottom)
67 {
68 	if (iLeft < 0)            iLeft = 0;
69 	if (iLeft > SCREEN_WIDTH) iLeft = SCREEN_WIDTH;
70 
71 	if (iTop < 0)             iTop = 0;
72 	if (iTop > SCREEN_HEIGHT) iTop = SCREEN_HEIGHT;
73 
74 	if (iRight < 0)            iRight = 0;
75 	if (iRight > SCREEN_WIDTH) iRight = SCREEN_WIDTH;
76 
77 	if (iBottom < 0)             iBottom = 0;
78 	if (iBottom > SCREEN_HEIGHT) iBottom = SCREEN_HEIGHT;
79 
80 	if (iLeft == iRight || iTop == iBottom) return;
81 
82 	if (iLeft   == gsVIEWPORT_START_X        &&
83 			iRight  == gsVIEWPORT_END_X          &&
84 			iTop    == gsVIEWPORT_WINDOW_START_Y &&
85 			iBottom == gsVIEWPORT_WINDOW_END_Y)
86 	{
87 		gfViewportDirty = TRUE;
88 		return;
89 	}
90 
91 	InvalidateRegionEx(iLeft, iTop, iRight, iBottom);
92 }
93 
94 
ExecuteBaseDirtyRectQueue(void)95 void ExecuteBaseDirtyRectQueue(void)
96 {
97 	if (!gfViewportDirty) return;
98 	gfViewportDirty = FALSE;
99 
100 	InvalidateScreen();
101 }
102 
103 
GetFreeBackgroundBuffer(void)104 static BACKGROUND_SAVE* GetFreeBackgroundBuffer(void)
105 {
106 	for (UINT32 i = 0; i < guiNumBackSaves; ++i)
107 	{
108 		BACKGROUND_SAVE* const b = gBackSaves[i];
109 		if (!b->fAllocated && !b->fFilled) return b;
110 	}
111 
112 	if (guiNumBackSaves == gBackSaves.size())
113 	{
114 		// out of back saves capacity
115 		// let's add some more
116 		const int increment = 100;
117 		SLOGD("Increasing background slots to %d", gBackSaves.size() + increment);
118 		for(int i = 0; i < increment; i++) {
119 			gBackSaves.push_back(new BACKGROUND_SAVE());
120 		}
121 	}
122 
123 	return gBackSaves[guiNumBackSaves++];
124 }
125 
126 
RegisterBackgroundRect(BackgroundFlags const uiFlags,INT16 sLeft,INT16 sTop,INT16 const usWidth,INT16 const usHeight)127 BACKGROUND_SAVE* RegisterBackgroundRect(BackgroundFlags const uiFlags, INT16 sLeft, INT16 sTop, INT16 const usWidth, INT16 const usHeight)
128 {
129 	const INT32 ClipX1 = gDirtyClipRect.iLeft;
130 	const INT32 ClipY1 = gDirtyClipRect.iTop;
131 	const INT32 ClipX2 = gDirtyClipRect.iRight;
132 	const INT32 ClipY2 = gDirtyClipRect.iBottom;
133 
134 	INT16 sRight  = sLeft + usWidth;
135 	INT16 sBottom = sTop  + usHeight;
136 
137 	const INT32 iTempX = sLeft;
138 	const INT32 iTempY = sTop;
139 
140 	// Clip to rect
141 	const INT32 uiLeftSkip   = __min(ClipX1 -   MIN(ClipX1, iTempX),                   (INT32)usWidth);
142 	const INT32 uiTopSkip    = __min(ClipY1 - __min(ClipY1, iTempY),                   (INT32)usHeight);
143 	const INT32 uiRightSkip  = __min(  MAX(ClipX2, iTempX + (INT32)usWidth)  - ClipX2, (INT32)usWidth);
144 	const INT32 uiBottomSkip = __min(__max(ClipY2, iTempY + (INT32)usHeight) - ClipY2, (INT32)usHeight);
145 
146 	// check if whole thing is clipped
147 	if (uiLeftSkip >= (INT32)usWidth  || uiRightSkip  >= (INT32)usWidth)  return NO_BGND_RECT;
148 	if (uiTopSkip  >= (INT32)usHeight || uiBottomSkip >= (INT32)usHeight) return NO_BGND_RECT;
149 
150 	// Set re-set values given based on clipping
151 	sLeft   += uiLeftSkip;
152 	sRight  -= uiRightSkip;
153 	sTop    += uiTopSkip;
154 	sBottom -= uiBottomSkip;
155 
156 	BACKGROUND_SAVE* const b = GetFreeBackgroundBuffer();
157 	*b = BACKGROUND_SAVE{};
158 
159 	const UINT32 uiBufSize = (sRight - sLeft) * (sBottom - sTop);
160 	if (uiBufSize == 0) return NO_BGND_RECT;
161 
162 	if (uiFlags & BGND_FLAG_SAVERECT) b->pSaveArea  = new UINT16[uiBufSize]{};
163 	if (uiFlags & BGND_FLAG_SAVE_Z)   b->pZSaveArea = new UINT16[uiBufSize]{};
164 
165 	b->fFreeMemory = TRUE;
166 	b->fAllocated  = TRUE;
167 	b->uiFlags     = uiFlags;
168 	b->sLeft       = sLeft;
169 	b->sTop        = sTop;
170 	b->sRight      = sRight;
171 	b->sBottom     = sBottom;
172 	b->sWidth      = sRight  - sLeft;
173 	b->sHeight     = sBottom - sTop;
174 	b->fFilled     = FALSE;
175 
176 	return b;
177 }
178 
179 
RegisterBackgroundRectSingleFilled(INT16 const x,INT16 const y,INT16 const w,INT16 const h)180 void RegisterBackgroundRectSingleFilled(INT16 const x, INT16 const y, INT16 const w, INT16 const h)
181 {
182 	BACKGROUND_SAVE* const b = RegisterBackgroundRect(BGND_FLAG_SINGLE, x, y, w, h);
183 	if (b == NO_BGND_RECT) return;
184 
185 	b->fFilled = TRUE;
186 	AddBaseDirtyRect(b->sLeft, b->sTop, b->sRight, b->sBottom);
187 }
188 
189 
RestoreBackgroundRects(void)190 void RestoreBackgroundRects(void)
191 {
192 	{ SGPVSurface::Lock lsrc(guiSAVEBUFFER);
193 		SGPVSurface::Lock ldst(FRAME_BUFFER);
194 		UINT16* const pSrcBuf          = lsrc.Buffer<UINT16>();
195 		UINT32        uiSrcPitchBYTES  = lsrc.Pitch();
196 		UINT16* const pDestBuf         = ldst.Buffer<UINT16>();
197 		UINT32        uiDestPitchBYTES = ldst.Pitch();
198 
199 		for (UINT32 i = 0; i < guiNumBackSaves; ++i)
200 		{
201 			const BACKGROUND_SAVE* const b = gBackSaves[i];
202 			if (!b->fFilled || b->fDisabled) continue;
203 
204 			if (b->pSaveArea != NULL)
205 			{
206 				Blt16BPPTo16BPP(pDestBuf, uiDestPitchBYTES, b->pSaveArea, b->sWidth * 2, b->sLeft, b->sTop, 0, 0, b->sWidth, b->sHeight);
207 				AddBaseDirtyRect(b->sLeft, b->sTop, b->sRight, b->sBottom);
208 			}
209 			else if (b->pZSaveArea != NULL)
210 			{
211 				Blt16BPPTo16BPP(gpZBuffer, gZBufferPitch, b->pZSaveArea, b->sWidth * sizeof(*b->pZSaveArea), b->sLeft, b->sTop, 0, 0, b->sWidth, b->sHeight);
212 			}
213 			else
214 			{
215 				Blt16BPPTo16BPP(pDestBuf, uiDestPitchBYTES, pSrcBuf, uiSrcPitchBYTES, b->sLeft, b->sTop, b->sLeft, b->sTop, b->sWidth, b->sHeight);
216 				AddBaseDirtyRect(b->sLeft, b->sTop, b->sRight, b->sBottom);
217 			}
218 		}
219 	}
220 
221 	EmptyBackgroundRects();
222 }
223 
224 
EmptyBackgroundRects(void)225 void EmptyBackgroundRects(void)
226 {
227 	for (UINT32 i = 0; i < guiNumBackSaves; ++i)
228 	{
229 		BACKGROUND_SAVE* const b = gBackSaves[i];
230 		if (b->fFilled)
231 		{
232 			b->fFilled = FALSE;
233 
234 			if (!b->fAllocated && b->fFreeMemory)
235 			{
236 				if (b->pSaveArea  != NULL) delete[] b->pSaveArea;
237 				if (b->pZSaveArea != NULL) delete[] b->pZSaveArea;
238 
239 				b->fAllocated  = FALSE;
240 				b->fFreeMemory = FALSE;
241 				b->fFilled     = FALSE;
242 				b->pSaveArea   = NULL;
243 			}
244 		}
245 
246 		if (b->uiFlags & BGND_FLAG_SINGLE || b->fPendingDelete)
247 		{
248 			if (b->fFreeMemory)
249 			{
250 				if (b->pSaveArea != NULL)  delete[] b->pSaveArea;
251 				if (b->pZSaveArea != NULL) delete[] b->pZSaveArea;
252 			}
253 
254 			b->fAllocated     = FALSE;
255 			b->fFreeMemory    = FALSE;
256 			b->fFilled        = FALSE;
257 			b->pSaveArea      = NULL;
258 			b->fPendingDelete = FALSE;
259 		}
260 	}
261 }
262 
263 
SaveBackgroundRects(void)264 void SaveBackgroundRects(void)
265 {
266 	SGPVSurface::Lock l(FRAME_BUFFER);
267 	UINT16* const pSrcBuf          = l.Buffer<UINT16>();
268 	UINT32  const uiDestPitchBYTES = l.Pitch();
269 
270 	for (UINT32 i = 0; i < guiNumBackSaves; ++i)
271 	{
272 		BACKGROUND_SAVE* const b = gBackSaves[i];
273 		if (!b->fAllocated || b->fDisabled) continue;
274 
275 		if (b->pSaveArea != NULL)
276 		{
277 			Blt16BPPTo16BPP(b->pSaveArea, b->sWidth * 2, pSrcBuf, uiDestPitchBYTES, 0, 0, b->sLeft, b->sTop, b->sWidth, b->sHeight);
278 		}
279 		else if (b->pZSaveArea != NULL)
280 		{
281 			Blt16BPPTo16BPP(b->pZSaveArea, b->sWidth * sizeof(*b->pZSaveArea), gpZBuffer, gZBufferPitch, 0, 0, b->sLeft, b->sTop, b->sWidth, b->sHeight);
282 		}
283 		else
284 		{
285 			AddBaseDirtyRect(b->sLeft, b->sTop, b->sRight, b->sBottom);
286 		}
287 
288 		b->fFilled = TRUE;
289 	}
290 }
291 
292 
FreeBackgroundRect(BACKGROUND_SAVE * const b)293 void FreeBackgroundRect(BACKGROUND_SAVE* const b)
294 {
295 	if (b == NULL) return;
296 
297 	b->fAllocated = FALSE;
298 }
299 
300 
FreeBackgroundRectPending(BACKGROUND_SAVE * const b)301 void FreeBackgroundRectPending(BACKGROUND_SAVE* const b)
302 {
303 	if(b)
304 	{
305 		b->fPendingDelete = TRUE;
306 	}
307 }
308 
309 
FreeBackgroundRectNow(BACKGROUND_SAVE * const b)310 static void FreeBackgroundRectNow(BACKGROUND_SAVE* const b)
311 {
312 	if (b->fFreeMemory)
313 	{
314 		if (b->pSaveArea)  delete[] b->pSaveArea;
315 		if (b->pZSaveArea) delete[] b->pZSaveArea;
316 	}
317 
318 	b->fAllocated  = FALSE;
319 	b->fFreeMemory = FALSE;
320 	b->fFilled     = FALSE;
321 	b->pSaveArea   = NULL;
322 }
323 
324 
FreeBackgroundRectType(BackgroundFlags const uiFlags)325 void FreeBackgroundRectType(BackgroundFlags const uiFlags)
326 {
327 	for (UINT32 i = 0; i < guiNumBackSaves; ++i)
328 	{
329 		BACKGROUND_SAVE* const b = gBackSaves[i];
330 		if (b->uiFlags & uiFlags) FreeBackgroundRectNow(b);
331 	}
332 }
333 
334 
InitializeBackgroundRects(void)335 void InitializeBackgroundRects(void)
336 {
337 	guiNumBackSaves = 0;
338 	gDirtyClipRect.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
339 }
340 
341 
InvalidateBackgroundRects(void)342 void InvalidateBackgroundRects(void)
343 {
344 	for (UINT32 i = 0; i < guiNumBackSaves; ++i)
345 	{
346 		gBackSaves[i]->fFilled = FALSE;
347 	}
348 }
349 
350 
ShutdownBackgroundRects(void)351 void ShutdownBackgroundRects(void)
352 {
353 	for (auto backgroundSave : gBackSaves)
354 	{
355 		if (backgroundSave->fAllocated) FreeBackgroundRectNow(backgroundSave);
356 		delete backgroundSave;
357 	}
358 	gBackSaves.clear();
359 	guiNumBackSaves = 0;
360 }
361 
362 
UpdateSaveBuffer(void)363 void UpdateSaveBuffer(void)
364 {
365 	// Update saved buffer - do for the viewport size ony!
366 	BlitBufferToBuffer(FRAME_BUFFER, guiSAVEBUFFER, 0, gsVIEWPORT_WINDOW_START_Y, SCREEN_WIDTH, gsVIEWPORT_WINDOW_END_Y - gsVIEWPORT_WINDOW_START_Y);
367 }
368 
369 
RestoreExternBackgroundRect(const INT16 sLeft,const INT16 sTop,const INT16 sWidth,const INT16 sHeight)370 void RestoreExternBackgroundRect(const INT16 sLeft, const INT16 sTop, const INT16 sWidth, const INT16 sHeight)
371 {
372 	Assert(0 <= sLeft && sLeft + sWidth <= SCREEN_WIDTH && 0 <= sTop && sTop + sHeight <= SCREEN_HEIGHT);
373 
374 	BlitBufferToBuffer(guiSAVEBUFFER, FRAME_BUFFER, sLeft, sTop, sWidth, sHeight);
375 
376 	// Add rect to frame buffer queue
377 	InvalidateRegionEx(sLeft, sTop, sLeft + sWidth, sTop + sHeight);
378 }
379 
380 
RestoreExternBackgroundRectGivenID(const BACKGROUND_SAVE * const b)381 void RestoreExternBackgroundRectGivenID(const BACKGROUND_SAVE* const b)
382 {
383 	if (!b->fAllocated) return;
384 	RestoreExternBackgroundRect(b->sLeft, b->sTop, b->sWidth, b->sHeight);
385 }
386 
387 
388 /* Dirties a single-frame rect exactly the size needed to save the background
389 	* for a given call to gprintf. Note that this must be called before the
390 	* backgrounds are saved, and before the actual call to gprintf that writes to
391 	* the video buffer. */
GDirty(INT16 x,INT16 y,const ST::utf32_buffer & codepoints)392 static void GDirty(INT16 x, INT16 y, const ST::utf32_buffer& codepoints)
393 {
394 	UINT16 const length = StringPixLength(codepoints, FontDefault);
395 	if (length > 0)
396 	{
397 		UINT16 const height = GetFontHeight(FontDefault);
398 		RegisterBackgroundRectSingleFilled(x, y, length, height);
399 	}
400 }
401 
402 
GDirtyPrint(INT16 x,INT16 y,const ST::utf32_buffer & codepoints)403 void GDirtyPrint(INT16 x, INT16 y, const ST::utf32_buffer& codepoints)
404 {
405 	GDirty(x, y, codepoints);
406 	MPrint(x, y, codepoints);
407 }
408 
409 
GPrintInvalidate(INT16 x,INT16 y,const ST::utf32_buffer & codepoints)410 void GPrintInvalidate(INT16 x, INT16 y, const ST::utf32_buffer& codepoints)
411 {
412 	MPrint(x, y, codepoints);
413 
414 	UINT16 const length = StringPixLength(codepoints, FontDefault);
415 	if (length > 0)
416 	{
417 		UINT16 const height = GetFontHeight(FontDefault);
418 		InvalidateRegionEx(x, y, x + length, y + height);
419 	}
420 }
421 
422 
RegisterVideoOverlay(OVERLAY_CALLBACK const callback,INT16 const x,INT16 const y,INT16 const w,INT16 const h)423 VIDEO_OVERLAY* RegisterVideoOverlay(OVERLAY_CALLBACK const callback, INT16 const x, INT16 const y, INT16 const w, INT16 const h)
424 try
425 {
426 	BACKGROUND_SAVE* const bgs = RegisterBackgroundRect(BGND_FLAG_PERMANENT, x, y, w, h);
427 	if (!bgs) return 0;
428 
429 	VIDEO_OVERLAY* const v    = new VIDEO_OVERLAY{};
430 	VIDEO_OVERLAY* const head = gVideoOverlays;
431 	v->prev        = 0;
432 	v->next        = head;
433 	v->fAllocated  = 2;
434 	v->background  = bgs;
435 	v->sX          = x;
436 	v->sY          = y;
437 	v->uiDestBuff  = FRAME_BUFFER;
438 	v->BltCallback = callback;
439 
440 	if (head) head->prev = v;
441 	gVideoOverlays = v;
442 	return v;
443 }
444 catch (...) { return 0; }
445 
446 
RegisterVideoOverlay(OVERLAY_CALLBACK callback,INT16 x,INT16 y,SGPFont font,UINT8 foreground,UINT8 background,const ST::utf32_buffer & codepoints)447 VIDEO_OVERLAY* RegisterVideoOverlay(OVERLAY_CALLBACK callback, INT16 x, INT16 y, SGPFont font, UINT8 foreground, UINT8 background, const ST::utf32_buffer& codepoints)
448 {
449 	INT16          const w = StringPixLength(codepoints, font);
450 	INT16          const h = GetFontHeight(font);
451 	VIDEO_OVERLAY* const v = RegisterVideoOverlay(callback, x, y, w, h);
452 	if (v)
453 	{
454 		v->uiFontID   = font;
455 		v->ubFontFore = foreground;
456 		v->ubFontBack = background;
457 		v->codepoints = codepoints;
458 	}
459 	return v;
460 }
461 
462 
RemoveVideoOverlay(VIDEO_OVERLAY * const v)463 void RemoveVideoOverlay(VIDEO_OVERLAY* const v)
464 {
465 	if (!v) return;
466 
467 	// Check if we are actively scrolling
468 	if (v->fActivelySaving)
469 	{
470 		v->fDeletionPending = TRUE;
471 	}
472 	else
473 	{
474 		//RestoreExternBackgroundRectGivenID(v->background);
475 
476 		FreeBackgroundRect(v->background);
477 
478 		if (v->pSaveArea != NULL) delete v->pSaveArea;
479 		v->pSaveArea = NULL;
480 
481 		VIDEO_OVERLAY* const prev = v->prev;
482 		VIDEO_OVERLAY* const next = v->next;
483 		*(prev ? &prev->next : &gVideoOverlays) = next;
484 		if (next) next->prev = prev;
485 		delete v;
486 	}
487 }
488 
489 
490 // FUnctions for entrie array of blitters
ExecuteVideoOverlays(void)491 void ExecuteVideoOverlays(void)
492 {
493 	FOR_EACH_VIDEO_OVERLAY(v)
494 	{
495 		// If we are scrolling but haven't saved yet, don't!
496 		if (!v->fActivelySaving && g_scroll_inertia) continue;
497 
498 		// ATE: Wait a frame before executing!
499 		switch (v->fAllocated)
500 		{
501 			case 1: v->BltCallback(v); break;
502 			case 2: v->fAllocated = 1; break;
503 		}
504 	}
505 }
506 
507 
ExecuteVideoOverlaysToAlternateBuffer(SGPVSurface * const buffer)508 void ExecuteVideoOverlaysToAlternateBuffer(SGPVSurface* const buffer)
509 {
510 	FOR_EACH_VIDEO_OVERLAY(v)
511 	{
512 		if (!v->fActivelySaving) continue;
513 
514 		SGPVSurface* const old_dst = v->uiDestBuff;
515 		v->uiDestBuff = buffer;
516 		v->BltCallback(v);
517 		v->uiDestBuff = old_dst;
518 	}
519 }
520 
521 
AllocateVideoOverlayArea(VIDEO_OVERLAY * const v)522 static void AllocateVideoOverlayArea(VIDEO_OVERLAY* const v)
523 {
524 	Assert(!v->fDisabled);
525 
526 	// Get buffer size
527 	const BACKGROUND_SAVE* const bgs      = v->background;
528 	UINT32                 const buf_size = (bgs->sRight - bgs->sLeft) * (bgs->sBottom - bgs->sTop);
529 
530 	v->fActivelySaving = TRUE;
531 	v->pSaveArea       = new UINT16[buf_size]{};
532 }
533 
534 
AllocateVideoOverlaysArea(void)535 void AllocateVideoOverlaysArea(void)
536 {
537 	FOR_EACH_VIDEO_OVERLAY(v)
538 	{
539 		AllocateVideoOverlayArea(v);
540 	}
541 }
542 
543 
SaveVideoOverlaysArea(SGPVSurface * const src)544 void SaveVideoOverlaysArea(SGPVSurface* const src)
545 {
546 	SGPVSurface::Lock l(src);
547 	UINT16* const pSrcBuf         = l.Buffer<UINT16>();
548 	UINT32  const uiSrcPitchBYTES = l.Pitch();
549 
550 	FOR_EACH_VIDEO_OVERLAY(v)
551 	{
552 		// OK, if our saved area is null, allocate it here!
553 		if (v->pSaveArea == NULL)
554 		{
555 			AllocateVideoOverlayArea(v);
556 			if (v->pSaveArea == NULL) continue;
557 		}
558 
559 		// Save data from frame buffer!
560 		const BACKGROUND_SAVE* const b = v->background;
561 		Blt16BPPTo16BPP(v->pSaveArea, b->sWidth * 2, pSrcBuf, uiSrcPitchBYTES, 0, 0, b->sLeft, b->sTop, b->sWidth, b->sHeight);
562 	}
563 }
564 
565 
DeleteVideoOverlaysArea(void)566 void DeleteVideoOverlaysArea(void)
567 {
568 	FOR_EACH_VIDEO_OVERLAY_SAFE(v)
569 	{
570 		if (v->pSaveArea != NULL) delete[] v->pSaveArea;
571 		v->pSaveArea       = NULL;
572 		v->fActivelySaving = FALSE;
573 		if (v->fDeletionPending) RemoveVideoOverlay(v);
574 	}
575 }
576 
577 
RestoreShiftedVideoOverlays(const INT16 sShiftX,const INT16 sShiftY)578 void RestoreShiftedVideoOverlays(const INT16 sShiftX, const INT16 sShiftY)
579 {
580 	const INT32 ClipX1 = 0;
581 	const INT32 ClipY1 = gsVIEWPORT_WINDOW_START_Y;
582 	const INT32 ClipX2 = SCREEN_WIDTH;
583 	const INT32 ClipY2 = gsVIEWPORT_WINDOW_END_Y - 1;
584 
585 	SGPVSurface::Lock l(BACKBUFFER);
586 	UINT16* const pDestBuf         = l.Buffer<UINT16>();
587 	UINT32  const uiDestPitchBYTES = l.Pitch();
588 
589 	FOR_EACH_VIDEO_OVERLAY_SAFE(v)
590 	{
591 		if (v->pSaveArea == NULL) continue;
592 
593 		// Get restore background values
594 		const BACKGROUND_SAVE* const b = v->background;
595 		INT16  sLeft    = b->sLeft;
596 		INT16  sTop     = b->sTop;
597 		INT16  sRight   = b->sRight;
598 		INT16  sBottom  = b->sBottom;
599 		UINT32 usHeight = b->sHeight;
600 		UINT32 usWidth  = b->sWidth;
601 
602 		// Clip!!
603 		const INT32 iTempX = sLeft - sShiftX;
604 		const INT32 iTempY = sTop  - sShiftY;
605 
606 		// Clip to rect
607 		const INT32 uiLeftSkip   = __min(ClipX1 -   MIN(ClipX1, iTempX),                   (INT32)usWidth);
608 		const INT32 uiTopSkip    = __min(ClipY1 - __min(ClipY1, iTempY),                   (INT32)usHeight);
609 		const INT32 uiRightSkip  = __min(  MAX(ClipX2, iTempX + (INT32)usWidth)  - ClipX2, (INT32)usWidth);
610 		const INT32 uiBottomSkip = __min(__max(ClipY2, iTempY + (INT32)usHeight) - ClipY2, (INT32)usHeight);
611 
612 		// check if whole thing is clipped
613 		if (uiLeftSkip >= (INT32)usWidth  || uiRightSkip  >= (INT32)usWidth)  continue;
614 		if (uiTopSkip  >= (INT32)usHeight || uiBottomSkip >= (INT32)usHeight) continue;
615 
616 		// Set re-set values given based on clipping
617 		sLeft    = iTempX + (INT16)uiLeftSkip;
618 		sTop     = iTempY + (INT16)uiTopSkip;
619 		sRight   = sRight  - sShiftX - (INT16)uiRightSkip;
620 		sBottom  = sBottom - sShiftY - (INT16)uiBottomSkip;
621 
622 		usHeight = sBottom - sTop;
623 		usWidth  = sRight -  sLeft;
624 
625 		Blt16BPPTo16BPP(pDestBuf, uiDestPitchBYTES, v->pSaveArea, b->sWidth * 2, sLeft, sTop, uiLeftSkip, uiTopSkip, usWidth, usHeight);
626 
627 		// Once done, check for pending deletion
628 		if (v->fDeletionPending) RemoveVideoOverlay(v);
629 	}
630 }
631 
632 
BlitBufferToBuffer(SGPVSurface * const src,SGPVSurface * const dst,const UINT16 usSrcX,const UINT16 usSrcY,const UINT16 usWidth,const UINT16 usHeight)633 void BlitBufferToBuffer(SGPVSurface* const src, SGPVSurface* const dst, const UINT16 usSrcX, const UINT16 usSrcY, const UINT16 usWidth, const UINT16 usHeight)
634 {
635 	SGPBox const r = { usSrcX, usSrcY, usWidth, usHeight };
636 	BltVideoSurface(dst, src, usSrcX, usSrcY, &r);
637 }
638 
639 
EnableVideoOverlay(const BOOLEAN fEnable,VIDEO_OVERLAY * const v)640 void EnableVideoOverlay(const BOOLEAN fEnable, VIDEO_OVERLAY* const v)
641 {
642 	if (!v) return;
643 	v->fDisabled             = !fEnable;
644 	v->background->fDisabled = !fEnable;
645 }
646 
647 
SetVideoOverlayText(VIDEO_OVERLAY * v,const ST::utf32_buffer & codepoints)648 void SetVideoOverlayText(VIDEO_OVERLAY* v, const ST::utf32_buffer& codepoints)
649 {
650 	if (!v) return;
651 	v->codepoints = codepoints;
652 }
653 
654 
SetVideoOverlayPos(VIDEO_OVERLAY * const v,const INT16 X,const INT16 Y)655 void SetVideoOverlayPos(VIDEO_OVERLAY* const v, const INT16 X, const INT16 Y)
656 {
657 	if (!v) return;
658 
659 	// If position has changed and there is text, adjust
660 	if (v->codepoints.size() > 0)
661 	{
662 		UINT16 uiStringLength = StringPixLength(v->codepoints, v->uiFontID);
663 		UINT16 uiStringHeight = GetFontHeight(v->uiFontID);
664 
665 		// Delete old rect
666 		// Remove background
667 		FreeBackgroundRectPending(v->background);
668 
669 		v->background = RegisterBackgroundRect(BGND_FLAG_PERMANENT, X, Y, uiStringLength, uiStringHeight);
670 		v->sX         = X;
671 		v->sY         = Y;
672 	}
673 }
674