1 #include "ContentManager.h"
2 #include "Debug.h"
3 #include "Fade_Screen.h"
4 #include "FileMan.h"
5 #include "GameInstance.h"
6 #include "HImage.h"
7 #include "Input.h"
8 #include "Local.h"
9 #include "Logger.h"
10 #include "RenderWorld.h"
11 #include "Render_Dirty.h"
12 #include "Timer.h"
13 #include "Timer_Control.h"
14 #include "Types.h"
15 #include "VObject_Blitters.h"
16 #include "VSurface.h"
17 #include "Video.h"
18 #include "UILayout.h"
19 #include "Font.h"
20 #include "Icon.h"
21 #include <algorithm>
22 #include <ctime>
23 #include <stdexcept>
24 
25 #define BUFFER_READY      0x00
26 #define BUFFER_DIRTY      0x02
27 
28 #define MAX_CURSOR_WIDTH  64
29 #define MAX_CURSOR_HEIGHT 64
30 
31 #define MAX_DIRTY_REGIONS 128
32 
33 #define VIDEO_OFF         0x00
34 #define VIDEO_ON          0x01
35 #define VIDEO_SUSPENDED   0x04
36 
37 #define RED_MASK 0xF800
38 #define GREEN_MASK 0x07E0
39 #define BLUE_MASK 0x001F
40 #define ALPHA_MASK 0
41 
42 #define OVERSAMPLING_SCALE 4
43 
44 
45 // Globals for mouse cursor
46 static UINT16 gusMouseCursorWidth;
47 static UINT16 gusMouseCursorHeight;
48 static INT16  gsMouseCursorXOffset;
49 static INT16  gsMouseCursorYOffset;
50 
51 static SDL_Rect MouseBackground = { 0, 0, 0, 0 };
52 
53 // Refresh thread based variables
54 static UINT32 guiFrameBufferState;  // BUFFER_READY, BUFFER_DIRTY
55 static UINT32 guiVideoManagerState; // VIDEO_ON, VIDEO_OFF, VIDEO_SUSPENDED
56 
57 // Dirty rectangle management variables
58 static SDL_Rect DirtyRegions[MAX_DIRTY_REGIONS];
59 static UINT32   guiDirtyRegionCount;
60 static BOOLEAN  gfForceFullScreenRefresh;
61 
62 
63 static SDL_Rect DirtyRegionsEx[MAX_DIRTY_REGIONS];
64 static UINT32   guiDirtyRegionExCount;
65 
66 
67 static SDL_Surface* MouseCursor;
68 static SDL_Surface* FrameBuffer;
69 static SDL_Renderer*  GameRenderer;
70 SDL_Window* g_game_window;
71 
72 static SDL_Surface* ScreenBuffer;
73 static SDL_Texture* ScreenTexture;
74 static SDL_Texture* ScaledScreenTexture;
75 static Uint32       g_window_flags = 0;
76 static VideoScaleQuality ScaleQuality = VideoScaleQuality::LINEAR;
77 
78 static void RecreateBackBuffer();
79 static void DeletePrimaryVideoSurfaces(void);
80 
81 // returns if desktop resolution larger game resolution
IsDesktopLargeEnough()82 BOOLEAN IsDesktopLargeEnough()
83 {
84 	SDL_DisplayMode dm;
85 	if (SDL_GetDesktopDisplayMode(0, &dm) == 0)
86 	{
87 		if (dm.w < SCREEN_WIDTH || dm.h < SCREEN_HEIGHT)
88 		{
89 			return false;
90 		}
91 	}
92 	return true;
93 }
94 
VideoSetFullScreen(const BOOLEAN enable)95 void VideoSetFullScreen(const BOOLEAN enable)
96 {
97 	if (enable)
98 	{
99 		g_window_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
100 	}
101 	else
102 	{
103 		g_window_flags &= ~SDL_WINDOW_FULLSCREEN_DESKTOP;
104 	}
105 }
106 
VideoToggleFullScreen(void)107 void VideoToggleFullScreen(void)
108 {
109 	if (SDL_GetWindowFlags(g_game_window) & SDL_WINDOW_FULLSCREEN_DESKTOP)
110 	{
111 		SDL_SetWindowFullscreen(g_game_window, 0);
112 	}
113 	else
114 	{
115 		SDL_SetWindowFullscreen(g_game_window, SDL_WINDOW_FULLSCREEN_DESKTOP);
116 	}
117 }
118 
VideoSetBrightness(float brightness)119 void VideoSetBrightness(float brightness)
120 {
121 	SDL_SetWindowBrightness(g_game_window, brightness);
122 }
123 
124 
125 static void GetRGBDistribution();
126 
127 
InitializeVideoManager(const VideoScaleQuality quality)128 void InitializeVideoManager(const VideoScaleQuality quality)
129 {
130 	SLOGD("Initializing the video manager");
131 	SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
132 
133 	ScaleQuality = quality;
134 	g_window_flags |= SDL_WINDOW_RESIZABLE;
135 
136 	g_game_window = SDL_CreateWindow(APPLICATION_NAME,
137 					SDL_WINDOWPOS_UNDEFINED,
138 					SDL_WINDOWPOS_UNDEFINED,
139 					SCREEN_WIDTH, SCREEN_HEIGHT,
140 					g_window_flags);
141 
142 	GameRenderer = SDL_CreateRenderer(g_game_window, -1, 0);
143 	SDL_RenderSetLogicalSize(GameRenderer, SCREEN_WIDTH, SCREEN_HEIGHT);
144 
145 	SDL_Surface* windowIcon = SDL_CreateRGBSurfaceFrom(
146 			(void*)gWindowIconData.pixel_data,
147 			gWindowIconData.width,
148 			gWindowIconData.height,
149 			gWindowIconData.bytes_per_pixel*8,
150 			gWindowIconData.bytes_per_pixel*gWindowIconData.width,
151 			0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
152 	SDL_SetWindowIcon(g_game_window, windowIcon);
153 	SDL_FreeSurface(windowIcon);
154 
155 
156 	ClippingRect.set(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
157 
158 	ScreenBuffer = SDL_CreateRGBSurface(
159 					0,
160 					SCREEN_WIDTH,
161 					SCREEN_HEIGHT,
162 					PIXEL_DEPTH,
163 					RED_MASK,
164 					GREEN_MASK,
165 					BLUE_MASK,
166 					ALPHA_MASK
167 	);
168 
169 	if (ScreenBuffer == NULL) {
170 		SLOGE("SDL_CreateRGBSurface for ScreenBuffer failed: %s\n", SDL_GetError());
171 	}
172 
173 
174 	if (ScaleQuality == VideoScaleQuality::PERFECT)
175 	{
176 		SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
177 #if SDL_VERSION_ATLEAST(2,0,5)
178 		if (!IsDesktopLargeEnough())
179 		{
180 			// Pixel-perfect mode cannot handle scaling down, and will
181 			// result in a empty black screen if the window size is
182 			// smaller than logical render resolution.
183 			throw std::runtime_error("Game resolution must not be larger than desktop size. "
184 				"Please reduce game resolution or choose another scaling mode.");
185 		}
186 		SDL_SetWindowMinimumSize(g_game_window, SCREEN_WIDTH, SCREEN_HEIGHT);
187 		SDL_RenderSetIntegerScale(GameRenderer, SDL_TRUE);
188 #else
189 		SLOGW("Pixel-perfect scaling is not available");
190 		ScaleQuality = VideoScaleQuality::NEAR_PERFECT;
191 #endif
192 	}
193 	else if (ScaleQuality == VideoScaleQuality::NEAR_PERFECT)
194 	{
195 		SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
196 		ScaledScreenTexture = SDL_CreateTexture(GameRenderer,
197 			SDL_PIXELFORMAT_RGB565,
198 			SDL_TEXTUREACCESS_TARGET,
199 			SCREEN_WIDTH * OVERSAMPLING_SCALE, SCREEN_HEIGHT * OVERSAMPLING_SCALE);
200 
201 		if (ScaledScreenTexture == NULL)
202 		{
203 			SLOGE("SDL_CreateTexture for ScaledScreenTexture failed: %s\n", SDL_GetError());
204 		}
205 
206 		SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
207 	}
208 	else
209 	{
210 		SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
211 	}
212 
213 	ScreenTexture = SDL_CreateTexture(GameRenderer,
214 					SDL_PIXELFORMAT_RGB565,
215 					SDL_TEXTUREACCESS_STREAMING,
216 					SCREEN_WIDTH, SCREEN_HEIGHT);
217 
218 	if (ScreenTexture == NULL) {
219 		SLOGE("SDL_CreateTexture for ScreenTexture failed: %s\n", SDL_GetError());
220 	}
221 
222 	FrameBuffer = SDL_CreateRGBSurface(
223 		SDL_SWSURFACE, SCREEN_WIDTH, SCREEN_HEIGHT, PIXEL_DEPTH,
224 		RED_MASK, GREEN_MASK, BLUE_MASK, ALPHA_MASK
225 	);
226 
227 	if (FrameBuffer == NULL)
228 	{
229 		SLOGE("SDL_CreateRGBSurface for FrameBuffer failed: %s\n", SDL_GetError());
230 	}
231 
232 	MouseCursor = SDL_CreateRGBSurface(
233 		0, MAX_CURSOR_WIDTH, MAX_CURSOR_HEIGHT, PIXEL_DEPTH,
234 		RED_MASK, GREEN_MASK, BLUE_MASK, ALPHA_MASK
235 	);
236 	SDL_SetColorKey(MouseCursor, SDL_TRUE, 0);
237 
238 	if (MouseCursor == NULL)
239 	{
240 		SLOGE("SDL_CreateRGBSurface for MouseCursor failed: %s\n", SDL_GetError());
241 	}
242 
243 	SDL_ShowCursor(SDL_DISABLE);
244 
245 	// Initialize state variables
246 	guiFrameBufferState      = BUFFER_DIRTY;
247 	guiVideoManagerState     = VIDEO_ON;
248 	guiDirtyRegionCount      = 0;
249 	gfForceFullScreenRefresh = TRUE;
250 
251 	// This function must be called to setup RGB information
252 	GetRGBDistribution();
253 }
254 
255 
ShutdownVideoManager(void)256 void ShutdownVideoManager(void)
257 {
258 	SLOGD("Shutting down the video manager");
259 	/* Toggle the state of the video manager to indicate to the refresh thread
260 	 * that it needs to shut itself down */
261 
262 	guiVideoManagerState = VIDEO_OFF;
263 
264 	if (ScreenBuffer != NULL) {
265 		SDL_FreeSurface(ScreenBuffer);
266 		ScreenBuffer = NULL;
267 	}
268 
269 	if (ScreenTexture != NULL) {
270 		SDL_DestroyTexture(ScreenTexture);
271 		ScreenTexture = NULL;
272 	}
273 
274 	if (ScaledScreenTexture != NULL) {
275 		SDL_DestroyTexture(ScaledScreenTexture);
276 		ScaledScreenTexture = NULL;
277 	}
278 
279 	if (GameRenderer != NULL) {
280 		SDL_DestroyRenderer(GameRenderer);
281 		GameRenderer = NULL;
282 	}
283 
284 	if (g_game_window != NULL) {
285 		SDL_DestroyWindow(g_game_window);
286 		g_game_window = NULL;
287 	}
288 
289 	SDL_QuitSubSystem(SDL_INIT_VIDEO);
290 
291 	// ATE: Release mouse cursor!
292 	FreeMouseCursor();
293 }
294 
295 
SuspendVideoManager(void)296 void SuspendVideoManager(void)
297 {
298 	guiVideoManagerState = VIDEO_SUSPENDED;
299 }
300 
InvalidateRegion(INT32 iLeft,INT32 iTop,INT32 iRight,INT32 iBottom)301 void InvalidateRegion(INT32 iLeft, INT32 iTop, INT32 iRight, INT32 iBottom)
302 {
303 	if (gfForceFullScreenRefresh)
304 	{
305 		// There's no point in going on since we are forcing a full screen refresh
306 		return;
307 	}
308 
309 	if (guiDirtyRegionCount < MAX_DIRTY_REGIONS)
310 	{
311 		// Well we haven't broken the MAX_DIRTY_REGIONS limit yet, so we register the new region
312 
313 		// DO SOME PREMIMARY CHECKS FOR VALID RECTS
314 		if (iLeft < 0) iLeft = 0;
315 		if (iTop  < 0) iTop  = 0;
316 
317 		if (iRight  > SCREEN_WIDTH)  iRight  = SCREEN_WIDTH;
318 		if (iBottom > SCREEN_HEIGHT) iBottom = SCREEN_HEIGHT;
319 
320 		if (iRight - iLeft <= 0) return;
321 		if (iBottom - iTop <= 0) return;
322 
323 		DirtyRegions[guiDirtyRegionCount].x = iLeft;
324 		DirtyRegions[guiDirtyRegionCount].y = iTop;
325 		DirtyRegions[guiDirtyRegionCount].w = iRight  - iLeft;
326 		DirtyRegions[guiDirtyRegionCount].h = iBottom - iTop;
327 		guiDirtyRegionCount++;
328 	}
329 	else
330 	{
331 		// The MAX_DIRTY_REGIONS limit has been exceeded. Therefore we arbitrarely invalidate the entire
332 		// screen and force a full screen refresh
333 		guiDirtyRegionExCount = 0;
334 		guiDirtyRegionCount = 0;
335 		gfForceFullScreenRefresh = TRUE;
336 	}
337 }
338 
339 
340 static void AddRegionEx(INT32 iLeft, INT32 iTop, INT32 iRight, INT32 iBottom);
341 
342 
InvalidateRegionEx(INT32 iLeft,INT32 iTop,INT32 iRight,INT32 iBottom)343 void InvalidateRegionEx(INT32 iLeft, INT32 iTop, INT32 iRight, INT32 iBottom)
344 {
345 	// Check if we are spanning the rectangle - if so slit it up!
346 	if (iTop <= gsVIEWPORT_WINDOW_END_Y && iBottom > gsVIEWPORT_WINDOW_END_Y)
347 	{
348 		// Add new top region
349 		AddRegionEx(iLeft, iTop, iRight, gsVIEWPORT_WINDOW_END_Y);
350 
351 		// Add new bottom region
352 		AddRegionEx(iLeft, gsVIEWPORT_WINDOW_END_Y, iRight, iBottom);
353 	}
354 	else
355 	{
356 		AddRegionEx(iLeft, iTop, iRight, iBottom);
357 	}
358 }
359 
360 
AddRegionEx(INT32 iLeft,INT32 iTop,INT32 iRight,INT32 iBottom)361 static void AddRegionEx(INT32 iLeft, INT32 iTop, INT32 iRight, INT32 iBottom)
362 {
363 	if (guiDirtyRegionExCount < MAX_DIRTY_REGIONS)
364 	{
365 		// DO SOME PRELIMINARY CHECKS FOR VALID RECTS
366 		if (iLeft < 0) iLeft = 0;
367 		if (iTop  < 0) iTop  = 0;
368 
369 		if (iRight  > SCREEN_WIDTH)  iRight  = SCREEN_WIDTH;
370 		if (iBottom > SCREEN_HEIGHT) iBottom = SCREEN_HEIGHT;
371 
372 		if (iRight - iLeft <= 0) return;
373 		if (iBottom - iTop <= 0) return;
374 
375 		DirtyRegionsEx[guiDirtyRegionExCount].x = iLeft;
376 		DirtyRegionsEx[guiDirtyRegionExCount].y = iTop;
377 		DirtyRegionsEx[guiDirtyRegionExCount].w = iRight  - iLeft;
378 		DirtyRegionsEx[guiDirtyRegionExCount].h = iBottom - iTop;
379 		guiDirtyRegionExCount++;
380 	}
381 	else
382 	{
383 		guiDirtyRegionExCount = 0;
384 		guiDirtyRegionCount = 0;
385 		gfForceFullScreenRefresh = TRUE;
386 	}
387 }
388 
389 
InvalidateScreen(void)390 void InvalidateScreen(void)
391 {
392 	// W A R N I N G ---- W A R N I N G ---- W A R N I N G ---- W A R N I N G ---- W A R N I N G ----
393 	//
394 	// This function is intended to be called by a thread which has already locked the
395 	// FRAME_BUFFER_MUTEX mutual exclusion section. Anything else will cause the application to
396 	// yack
397 
398 	guiDirtyRegionCount = 0;
399 	guiDirtyRegionExCount = 0;
400 	gfForceFullScreenRefresh = TRUE;
401 	guiFrameBufferState = BUFFER_DIRTY;
402 }
403 
404 
405 //#define SCROLL_TEST
406 
ScrollJA2Background(INT16 sScrollXIncrement,INT16 sScrollYIncrement)407 static void ScrollJA2Background(INT16 sScrollXIncrement, INT16 sScrollYIncrement)
408 {
409 	SDL_Surface* Frame  = FrameBuffer;
410 	SDL_Surface* Source = SDL_CreateRGBSurface(0, ScreenBuffer->w, ScreenBuffer->h, PIXEL_DEPTH, RED_MASK, GREEN_MASK, BLUE_MASK, ALPHA_MASK);
411 	SDL_Surface* Dest   = ScreenBuffer; // Back
412 	SDL_Rect     SrcRect;
413 	SDL_Rect     DstRect;
414 	SDL_Rect     StripRegions[2];
415 	UINT16       NumStrips = 0;
416 
417 	const UINT16 usWidth  = SCREEN_WIDTH;
418 	const UINT16 usHeight = gsVIEWPORT_WINDOW_END_Y - gsVIEWPORT_WINDOW_START_Y;
419 
420 	SDL_BlitSurface(ScreenBuffer, NULL, Source, NULL);
421 
422 	if (sScrollXIncrement < 0)
423 	{
424 		SrcRect.x = 0;
425 		SrcRect.w = usWidth + sScrollXIncrement;
426 		DstRect.x = -sScrollXIncrement;
427 		StripRegions[0].x = gsVIEWPORT_START_X;
428 		StripRegions[0].y = gsVIEWPORT_WINDOW_START_Y;
429 		StripRegions[0].w = -sScrollXIncrement;
430 		StripRegions[0].h = usHeight;
431 		++NumStrips;
432 	}
433 	else if (sScrollXIncrement > 0)
434 	{
435 		SrcRect.x = sScrollXIncrement;
436 		SrcRect.w = usWidth - sScrollXIncrement;
437 		DstRect.x = 0;
438 		StripRegions[0].x = gsVIEWPORT_END_X - sScrollXIncrement;
439 		StripRegions[0].y = gsVIEWPORT_WINDOW_START_Y;
440 		StripRegions[0].w = sScrollXIncrement;
441 		StripRegions[0].h = usHeight;
442 		++NumStrips;
443 	}
444 	else
445 	{
446 		SrcRect.x = 0;
447 		SrcRect.w = usWidth;
448 		DstRect.x = 0;
449 	}
450 
451 	if (sScrollYIncrement < 0)
452 	{
453 		SrcRect.y = gsVIEWPORT_WINDOW_START_Y;
454 		SrcRect.h = usHeight + sScrollYIncrement;
455 		DstRect.y = gsVIEWPORT_WINDOW_START_Y - sScrollYIncrement;
456 		StripRegions[NumStrips].x = DstRect.x;
457 		StripRegions[NumStrips].y = gsVIEWPORT_WINDOW_START_Y;
458 		StripRegions[NumStrips].w = SrcRect.w;
459 		StripRegions[NumStrips].h = -sScrollYIncrement;
460 		++NumStrips;
461 	}
462 	else if (sScrollYIncrement > 0)
463 	{
464 		SrcRect.y = gsVIEWPORT_WINDOW_START_Y + sScrollYIncrement;
465 		SrcRect.h = usHeight - sScrollYIncrement;
466 		DstRect.y = gsVIEWPORT_WINDOW_START_Y;
467 		StripRegions[NumStrips].x = DstRect.x;
468 		StripRegions[NumStrips].y = gsVIEWPORT_WINDOW_END_Y - sScrollYIncrement;
469 		StripRegions[NumStrips].w = SrcRect.w;
470 		StripRegions[NumStrips].h = sScrollYIncrement;
471 		++NumStrips;
472 	}
473 	else
474 	{
475 		SrcRect.y = gsVIEWPORT_WINDOW_START_Y;
476 		SrcRect.h = usHeight;
477 		DstRect.y = gsVIEWPORT_WINDOW_START_Y;
478 	}
479 
480 	SDL_BlitSurface(Source, &SrcRect, Dest, &DstRect);
481 
482 #ifdef SCROLL_TEST
483 	SDL_FillRect(Dest, NULL, 0);
484 #endif
485 
486 	for (UINT i = 0; i < NumStrips; i++)
487 	{
488 		UINT x = StripRegions[i].x;
489 		UINT y = StripRegions[i].y;
490 		UINT w = StripRegions[i].w;
491 		UINT h = StripRegions[i].h;
492 		for (UINT j = y; j < y + h; ++j)
493 		{
494 			std::fill_n(gpZBuffer + j * SCREEN_WIDTH + x, w, 0);
495 		}
496 
497 		RenderStaticWorldRect(x, y, x + w, y + h, TRUE);
498 		SDL_BlitSurface(Frame, &StripRegions[i], Dest, &StripRegions[i]);
499 	}
500 
501 	// RESTORE SHIFTED
502 	RestoreShiftedVideoOverlays(sScrollXIncrement, sScrollYIncrement);
503 
504 	// SAVE NEW
505 	SaveVideoOverlaysArea(BACKBUFFER);
506 
507 	// BLIT NEW
508 	ExecuteVideoOverlaysToAlternateBuffer(BACKBUFFER);
509 
510 	SDL_Texture* screenTexture = SDL_CreateTextureFromSurface(GameRenderer, ScreenBuffer);
511 
512 	SDL_Rect r;
513 	r.x = gsVIEWPORT_START_X;
514 	r.y = gsVIEWPORT_WINDOW_START_Y;
515 	r.w = gsVIEWPORT_END_X - gsVIEWPORT_START_X;
516 	r.h = gsVIEWPORT_WINDOW_END_Y - gsVIEWPORT_WINDOW_START_Y;
517 	SDL_RenderCopy(GameRenderer, screenTexture, &r, &r);
518 
519 	SDL_FreeSurface(Source);
520 	SDL_DestroyTexture(screenTexture);
521 }
522 
523 
RefreshScreen(void)524 void RefreshScreen(void)
525 {
526 	if (guiVideoManagerState != VIDEO_ON) return;
527 
528 #if DEBUG_PRINT_FPS
529 	{
530 		static int32_t prevSecond = 0;
531 		static int32_t fps = 0;
532 
533 		int32_t currentSecond = time(NULL);
534 		if(currentSecond != prevSecond)
535 		{
536 			printf("fps: %d\n", fps);
537 			fps = 0;
538 			prevSecond = currentSecond;
539 		}
540 		else
541 		{
542 			fps++;
543 		}
544 	}
545 #endif
546 
547 	SDL_BlitSurface(FrameBuffer, &MouseBackground, ScreenBuffer, &MouseBackground);
548 
549 	const BOOLEAN scrolling = (gsScrollXIncrement != 0 || gsScrollYIncrement != 0);
550 
551 	if (guiFrameBufferState == BUFFER_DIRTY)
552 	{
553 		if (gfFadeInitialized && gfFadeInVideo)
554 		{
555 			gFadeFunction();
556 		}
557 		else
558 		{
559 			if (gfForceFullScreenRefresh)
560 			{
561 				SDL_BlitSurface(FrameBuffer, NULL, ScreenBuffer, NULL);
562 			}
563 			else
564 			{
565 				for (UINT32 i = 0; i < guiDirtyRegionCount; i++)
566 				{
567 					SDL_BlitSurface(FrameBuffer, &DirtyRegions[i], ScreenBuffer, &DirtyRegions[i]);
568 				}
569 
570 				for (UINT32 i = 0; i < guiDirtyRegionExCount; i++)
571 				{
572 					SDL_Rect* r = &DirtyRegionsEx[i];
573 					if (scrolling)
574 					{
575 						// Check if we are completely out of bounds
576 						if (r->y <= gsVIEWPORT_WINDOW_END_Y && r->y + r->h <= gsVIEWPORT_WINDOW_END_Y)
577 						{
578 							continue;
579 						}
580 					}
581 					SDL_BlitSurface(FrameBuffer, r, ScreenBuffer, r);
582 				}
583 			}
584 		}
585 		if (scrolling)
586 		{
587 			ScrollJA2Background(gsScrollXIncrement, gsScrollYIncrement);
588 			gsScrollXIncrement = 0;
589 			gsScrollYIncrement = 0;
590 		}
591 		gfIgnoreScrollDueToCenterAdjust = FALSE;
592 		guiFrameBufferState = BUFFER_READY;
593 	}
594 
595 	SGPPoint MousePos;
596 	GetMousePos(&MousePos);
597 	SDL_Rect src;
598 	src.x = 0;
599 	src.y = 0;
600 	src.w = gusMouseCursorWidth;
601 	src.h = gusMouseCursorHeight;
602 	SDL_Rect dst;
603 	dst.x = MousePos.iX - gsMouseCursorXOffset;
604 	dst.y = MousePos.iY - gsMouseCursorYOffset;
605 	SDL_BlitSurface(MouseCursor, &src, ScreenBuffer, &dst);
606 	MouseBackground = dst;
607 
608 	SDL_UpdateTexture(ScreenTexture, NULL, ScreenBuffer->pixels, ScreenBuffer->pitch);
609 
610 	SDL_RenderClear(GameRenderer);
611 
612 	if (ScaleQuality == VideoScaleQuality::NEAR_PERFECT) {
613 		SDL_SetRenderTarget(GameRenderer, ScaledScreenTexture);
614 		SDL_RenderCopy(GameRenderer, ScreenTexture, nullptr, nullptr);
615 
616 		SDL_SetRenderTarget(GameRenderer, nullptr);
617 		SDL_RenderCopy(GameRenderer, ScaledScreenTexture, nullptr, nullptr);
618 	}
619 	else {
620 		SDL_RenderCopy(GameRenderer, ScreenTexture, NULL, NULL);
621 	}
622 
623 	SDL_RenderPresent(GameRenderer);
624 
625 	gfForceFullScreenRefresh = FALSE;
626 	guiDirtyRegionCount = 0;
627 	guiDirtyRegionExCount = 0;
628 }
629 
630 
GetRGBDistribution()631 static void GetRGBDistribution()
632 {
633 	SDL_PixelFormat const& f = *ScreenBuffer->format;
634 
635 	UINT32          const  r = f.Rmask;
636 	UINT32          const  g = f.Gmask;
637 	UINT32          const  b = f.Bmask;
638 
639 	/* Mask the highest bit of each component. This is used for alpha blending. */
640 	guiTranslucentMask = (r & r >> 1) | (g & g >> 1) | (b & b >> 1);
641 
642 	gusRedMask   = r;
643 	gusGreenMask = g;
644 	gusBlueMask  = b;
645 
646 	gusRedShift   = f.Rshift - f.Rloss;
647 	gusGreenShift = f.Gshift - f.Gloss;
648 	gusBlueShift  = f.Bshift - f.Bloss;
649 }
650 
651 
GetPrimaryRGBDistributionMasks(UINT32 * const RedBitMask,UINT32 * const GreenBitMask,UINT32 * const BlueBitMask)652 void GetPrimaryRGBDistributionMasks(UINT32* const  RedBitMask, UINT32* const GreenBitMask, UINT32* const BlueBitMask)
653 {
654 	*RedBitMask   = gusRedMask;
655 	*GreenBitMask = gusGreenMask;
656 	*BlueBitMask  = gusBlueMask;
657 }
658 
659 
SetMouseCursorProperties(INT16 sOffsetX,INT16 sOffsetY,UINT16 usCursorHeight,UINT16 usCursorWidth)660 void SetMouseCursorProperties(INT16 sOffsetX, INT16 sOffsetY, UINT16 usCursorHeight, UINT16 usCursorWidth)
661 {
662 	gsMouseCursorXOffset = sOffsetX;
663 	gsMouseCursorYOffset = sOffsetY;
664 	gusMouseCursorWidth  = usCursorWidth;
665 	gusMouseCursorHeight = usCursorHeight;
666 }
667 
668 
EndFrameBufferRender(void)669 void EndFrameBufferRender(void)
670 {
671 	guiFrameBufferState = BUFFER_DIRTY;
672 }
673 
674 
RecreateBackBuffer()675 static void RecreateBackBuffer()
676 {
677 	// ScreenBuffer should not be automatically removed because it was created
678 	// with SDL_SetVideoMode.  So, using SGPVSurface instead of SGPVSurfaceAuto
679 	SGPVSurface* newBackbuffer = new SGPVSurface(ScreenBuffer);
680 
681 	if(g_back_buffer != NULL)
682 	{
683 		ReplaceFontBackBuffer(g_back_buffer, newBackbuffer);
684 
685 		delete g_back_buffer;
686 		g_back_buffer = NULL;
687 	}
688 
689 	g_back_buffer  = newBackbuffer;
690 }
691 
SetPrimaryVideoSurfaces(void)692 static void SetPrimaryVideoSurfaces(void)
693 {
694 	// Delete surfaces if they exist
695 	DeletePrimaryVideoSurfaces();
696 
697 	RecreateBackBuffer();
698 
699 	g_mouse_buffer = new SGPVSurfaceAuto(MouseCursor);
700 	g_frame_buffer = new SGPVSurfaceAuto(FrameBuffer);
701 }
702 
DeletePrimaryVideoSurfaces(void)703 static void DeletePrimaryVideoSurfaces(void)
704 {
705 	delete g_back_buffer;
706 	g_back_buffer = NULL;
707 
708 	delete g_frame_buffer;
709 	g_frame_buffer = NULL;
710 
711 	delete g_mouse_buffer;
712 	g_mouse_buffer = NULL;
713 }
714 
715 SGPVSurface* gpVSurfaceHead = 0;
716 
InitializeVideoSurfaceManager(void)717 void InitializeVideoSurfaceManager(void)
718 {
719 	//Shouldn't be calling this if the video surface manager already exists.
720 	//Call shutdown first...
721 	Assert(gpVSurfaceHead == NULL);
722 	gpVSurfaceHead = NULL;
723 
724 	// Create primary and backbuffer from globals
725 	SetPrimaryVideoSurfaces();
726 }
727 
728 
ShutdownVideoSurfaceManager(void)729 void ShutdownVideoSurfaceManager(void)
730 {
731 	SLOGD("Shutting down the Video Surface manager");
732 
733 	// Delete primary viedeo surfaces
734 	DeletePrimaryVideoSurfaces();
735 
736 	while (gpVSurfaceHead)
737 	{
738 		delete gpVSurfaceHead;
739 	}
740 }
741