1 /*
2  *  ppui/sdl/DisplayDeviceFB_SDL.cpp
3  *
4  *  Copyright 2009 Peter Barth, Christopher O'Neill, Dale Whinham
5  *
6  *  This file is part of Milkytracker.
7  *
8  *  Milkytracker is free software: you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation, either version 3 of the License, or
11  *  (at your option) any later version.
12  *
13  *  Milkytracker is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with Milkytracker.  If not, see <http://www.gnu.org/licenses/>.
20  *
21  *  12/5/14 - Dale Whinham
22  *    - Port to SDL2
23  *    - Resizable window which renders to a scaled texture
24  *    - Experimental, buggy Retina support (potential problems with mouse coordinates if letterboxing happens)
25  *
26  *    TODO: - Test under Linux (only tested under OSX)
27  *          - Test/fix/remove scale factor and orientation code
28  *          - Look at the OpenGL stuff
29  */
30 
31 #include "DisplayDeviceFB_SDL.h"
32 #include "Graphics.h"
33 
PPDisplayDeviceFB(pp_int32 width,pp_int32 height,pp_int32 scaleFactor,pp_int32 bpp,bool fullScreen,Orientations theOrientation,bool swapRedBlue)34 PPDisplayDeviceFB::PPDisplayDeviceFB(pp_int32 width,
35 									 pp_int32 height,
36 									 pp_int32 scaleFactor,
37 									 pp_int32 bpp,
38 									 bool fullScreen,
39 									 Orientations theOrientation/* = ORIENTATION_NORMAL*/,
40 									 bool swapRedBlue/* = false*/) :
41 	PPDisplayDevice(width, height, scaleFactor, bpp, fullScreen, theOrientation),
42 	needsTemporaryBuffer((orientation != ORIENTATION_NORMAL) || (scaleFactor != 1)),
43 	temporaryBuffer(NULL)
44 {
45 	// Create an SDL window and surface
46 	theWindow = CreateWindow(realWidth, realHeight, bpp,
47 #ifdef HIDPI_SUPPORT
48 							  SDL_WINDOW_ALLOW_HIGHDPI |							// Support for 'Retina'/Hi-DPI displays
49 #endif
50 							  SDL_WINDOW_RESIZABLE	 |								// MilkyTracker's window is resizable
51 							  (bFullScreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0));	// Use 'fake fullscreen' because we can scale
52 
53 	if (theWindow == NULL)
54 	{
55 		fprintf(stderr, "SDL: Could not create window.\n");
56 		exit(EXIT_FAILURE);
57 	}
58 
59 	// Create renderer for the window
60 	theRenderer = SDL_CreateRenderer(theWindow, drv_index, 0);
61 	if (theRenderer == NULL)
62 	{
63 		fprintf(stderr, "SDL: SDL_CreateRenderer failed: %s\n", SDL_GetError());
64 		exit(EXIT_FAILURE);
65 	}
66 
67 #ifdef HIDPI_SUPPORT
68 	// Feed SDL_RenderSetLogicalSize() with output size, not GUI surface size, otherwise mouse coordinates will be wrong for Hi-DPI
69 	int rendererW, rendererH;
70 	SDL_GetRendererOutputSize(theRenderer, &rendererW, &rendererH);
71 #endif
72 
73 	// Log renderer capabilities
74 	SDL_RendererInfo theRendererInfo;
75 	if (!SDL_GetRendererInfo(theRenderer, &theRendererInfo))
76 	{
77 		if (theRendererInfo.flags & SDL_RENDERER_SOFTWARE) printf("SDL: Using software renderer.\n");
78 		if (theRendererInfo.flags & SDL_RENDERER_ACCELERATED) printf("SDL: Using accelerated renderer.\n");
79 		if (theRendererInfo.flags & SDL_RENDERER_PRESENTVSYNC) printf("SDL: Vsync enabled.\n");
80 		if (theRendererInfo.flags & SDL_RENDERER_TARGETTEXTURE) printf("SDL: Renderer supports rendering to texture.\n");
81 	}
82 
83 	// Lock aspect ratio and scale the UI up to fit the window
84 #ifdef HIDPI_SUPPORT
85 	SDL_RenderSetLogicalSize(theRenderer, rendererW, rendererH);
86 #else
87 	SDL_RenderSetLogicalSize(theRenderer, realWidth, realHeight);
88 #endif
89 
90 	// Use linear filtering for the scaling (make this optional eventually)
91 	SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
92 
93 	// Create surface for rendering graphics
94 	theSurface = SDL_CreateRGBSurface(0, realWidth, realHeight, bpp == -1 ? 32 : bpp, 0, 0, 0, 0);
95 	if (theSurface == NULL)
96 	{
97 		fprintf(stderr, "SDL: SDL_CreateSurface failed: %s\n", SDL_GetError());
98 		exit(EXIT_FAILURE);
99 	}
100 
101 	// Streaming texture for rendering the UI
102 	theTexture = SDL_CreateTexture(theRenderer, theSurface->format->format, SDL_TEXTUREACCESS_STREAMING, realWidth, realHeight);
103 	if (theTexture == NULL)
104 	{
105 		fprintf(stderr, "SDL: SDL_CreateTexture failed: %s\n", SDL_GetError());
106 		exit(EXIT_FAILURE);
107 	}
108 
109 	// We got a surface: update bpp value
110 	bpp = theSurface->format->BitsPerPixel;
111 
112 	// Create a PPGraphics context based on bpp
113 	switch (bpp)
114 	{
115 		case 16:
116 			currentGraphics = new PPGraphics_16BIT(width, height, 0, NULL);
117 			break;
118 
119 		case 24:
120 		{
121 			PPGraphics_24bpp_generic* g = new PPGraphics_24bpp_generic(width, height, 0, NULL);
122 			if (swapRedBlue)
123 			{
124 				g->setComponentBitpositions(theSurface->format->Bshift,
125 											theSurface->format->Gshift,
126 											theSurface->format->Rshift);
127 			}
128 			else
129 			{
130 				g->setComponentBitpositions(theSurface->format->Rshift,
131 											theSurface->format->Gshift,
132 											theSurface->format->Bshift);
133 			}
134 			currentGraphics = static_cast<PPGraphicsAbstract*>(g);
135 			break;
136 		}
137 
138 		case 32:
139 		{
140 			PPGraphics_32bpp_generic* g = new PPGraphics_32bpp_generic(width, height, 0, NULL);
141 			if (swapRedBlue)
142 			{
143 				g->setComponentBitpositions(theSurface->format->Bshift,
144 											theSurface->format->Gshift,
145 											theSurface->format->Rshift);
146 			}
147 			else
148 			{
149 				g->setComponentBitpositions(theSurface->format->Rshift,
150 											theSurface->format->Gshift,
151 											theSurface->format->Bshift);
152 			}
153 			currentGraphics = static_cast<PPGraphicsAbstract*>(g);
154 			break;
155 		}
156 
157 		default:
158 			fprintf(stderr, "SDL: Unsupported color depth (%i), try either 16, 24 or 32", bpp);
159 			exit(EXIT_FAILURE);
160 	}
161 
162 	if (needsTemporaryBuffer)
163 	{
164 		temporaryBufferPitch = (width*bpp)/8;
165 		temporaryBufferBPP = bpp;
166 		temporaryBuffer = new pp_uint8[getSize().width*getSize().height*(bpp/8)];
167 	}
168 
169 	currentGraphics->lock = true;
170 }
171 
~PPDisplayDeviceFB()172 PPDisplayDeviceFB::~PPDisplayDeviceFB()
173 {
174 	SDL_FreeSurface(theSurface);
175 	SDL_DestroyRenderer(theRenderer);
176 	SDL_DestroyWindow(theWindow);
177 
178 	delete[] temporaryBuffer;
179 	// base class is responsible for deleting currentGraphics
180 }
181 
open()182 PPGraphicsAbstract* PPDisplayDeviceFB::open()
183 {
184 	if (!isEnabled())
185 		return NULL;
186 
187 	if (currentGraphics->lock)
188 	{
189 		if (SDL_LockSurface(theSurface) < 0)
190 			return NULL;
191 
192 		currentGraphics->lock = false;
193 
194 		if (needsTemporaryBuffer)
195 			static_cast<PPGraphicsFrameBuffer*>(currentGraphics)->setBufferProperties(temporaryBufferPitch, (pp_uint8*)temporaryBuffer);
196 		else
197 			static_cast<PPGraphicsFrameBuffer*>(currentGraphics)->setBufferProperties(theSurface->pitch, (pp_uint8*)theSurface->pixels);
198 
199 		return currentGraphics;
200 	}
201 
202 	return NULL;
203 }
204 
close()205 void PPDisplayDeviceFB::close()
206 {
207 	SDL_UnlockSurface(theSurface);
208 
209 	currentGraphics->lock = true;
210 }
211 
update()212 void PPDisplayDeviceFB::update()
213 {
214 	if (!isUpdateAllowed() || !isEnabled())
215 		return;
216 
217 	if (theSurface->locked)
218 	{
219 		return;
220 	}
221 
222 	PPRect r(0, 0, getSize().width, getSize().height);
223 	swap(r);
224 
225 	// Update entire texture and copy to renderer
226 	SDL_UpdateTexture(theTexture, NULL, theSurface->pixels, theSurface->pitch);
227 	SDL_RenderClear(theRenderer);
228 	SDL_RenderCopy(theRenderer, theTexture, NULL, NULL);
229 	SDL_RenderPresent(theRenderer);
230 }
231 
update(const PPRect & r)232 void PPDisplayDeviceFB::update(const PPRect& r)
233 {
234 	if (!isUpdateAllowed() || !isEnabled())
235 		return;
236 
237 	if (theSurface->locked)
238 	{
239 		return;
240 	}
241 
242 	swap(r);
243 
244 	PPRect r2(r);
245 	r2.scale(scaleFactor);
246 
247 	transformInverse(r2);
248 
249 	SDL_Rect r3 = { r2.x1, r2.y1, r2.width(), r2.height() };
250 
251 	// Calculate destination pixel data offset based on row pitch and x coordinate
252 	void* surfaceOffset = (char*) theSurface->pixels + r2.y1 * theSurface->pitch + r2.x1 * theSurface->format->BytesPerPixel;
253 
254 	// Update dirty area of texture and copy to renderer
255 	SDL_UpdateTexture(theTexture, &r3, surfaceOffset, theSurface->pitch);
256 	SDL_RenderClear(theRenderer);
257 	SDL_RenderCopy(theRenderer, theTexture, NULL, NULL);
258 	SDL_RenderPresent(theRenderer);
259 }
260 
swap(const PPRect & r2)261 void PPDisplayDeviceFB::swap(const PPRect& r2)
262 {
263 	PPRect r(r2);
264 	pp_int32 h;
265 	if (r.x2 < r.x1)
266 	{
267 		h = r.x1; r.x1 = r.x2; r.x2 = h;
268 	}
269 	if (r.y2 < r.y1)
270 	{
271 		h = r.y1; r.y1 = r.y2; r.y2 = h;
272 	}
273 
274 	switch (orientation)
275 	{
276 		case ORIENTATION_NORMAL:
277 		{
278 			if (!needsTemporaryBuffer)
279 				return;
280 
281 			if (SDL_LockSurface(theSurface) < 0)
282 				return;
283 
284 			const pp_uint32 srcBPP = temporaryBufferBPP/8;
285 			const pp_uint32 dstBPP = theSurface->format->BytesPerPixel;
286 
287 			PPRect destRect(r);
288 			destRect.scale(scaleFactor);
289 
290 			const pp_uint32 stepU = (r.x2 - r.x1) * 65536 / (destRect.x2 - destRect.x1);
291 			const pp_uint32 stepV = (r.y2 - r.y1) * 65536 / (destRect.y2 - destRect.y1);
292 
293 			switch (temporaryBufferBPP)
294 			{
295 				case 16:
296 				{
297 					pp_uint32 srcPitch = temporaryBufferPitch;
298 					pp_uint32 dstPitch = theSurface->pitch;
299 
300 					pp_uint8* src = (pp_uint8*)temporaryBuffer;
301 					pp_uint8* dst = (pp_uint8*)theSurface->pixels;
302 
303 					pp_uint32 v = r.y1 * 65536;
304 					for (pp_uint32 y = destRect.y1; y < destRect.y2; y++)
305 					{
306 						pp_uint32 u = r.x1 * 65536;
307 						pp_uint16* dstPtr = (pp_uint16*)(dst + y*dstPitch + destRect.x1*dstBPP);
308 						pp_uint8* srcPtr = src + (v>>16)*srcPitch;
309 						for (pp_uint32 x = destRect.x1; x < destRect.x2; x++)
310 						{
311 							*dstPtr++ = *(pp_uint16*)(srcPtr + (u>>16) * srcBPP);
312 							u += stepU;
313 						}
314 						v += stepV;
315 					}
316 
317 
318 					break;
319 				}
320 
321 				case 24:
322 				{
323 					pp_uint32 srcPitch = temporaryBufferPitch;
324 					pp_uint32 dstPitch = theSurface->pitch;
325 
326 					pp_uint8* src = (pp_uint8*)temporaryBuffer;
327 					pp_uint8* dst = (pp_uint8*)theSurface->pixels;
328 
329 					const pp_uint32 srcBPP = temporaryBufferBPP/8;
330 					const pp_uint32 dstBPP = theSurface->format->BytesPerPixel;
331 
332 					pp_uint32 v = r.y1 * 65536;
333 					for (pp_uint32 y = destRect.y1; y < destRect.y2; y++)
334 					{
335 						pp_uint32 u = r.x1 * 65536;
336 						pp_uint8* dstPtr = (pp_uint8*)(dst + y*dstPitch + destRect.x1*dstBPP);
337 						pp_uint8* srcPtr = src + (v>>16)*srcPitch;
338 						for (pp_uint32 x = destRect.x1; x < destRect.x2; x++)
339 						{
340 							*dstPtr = *(pp_uint8*)(srcPtr + (u>>16) * srcBPP);
341 							*(dstPtr+1) = *(pp_uint8*)(srcPtr + (u>>16) * srcBPP + 1);
342 							*(dstPtr+2) = *(pp_uint8*)(srcPtr + (u>>16) * srcBPP + 2);
343 							dstPtr+=3;
344 							u += stepU;
345 						}
346 						v += stepV;
347 					}
348 
349 					break;
350 				}
351 
352 				case 32:
353 				{
354 					pp_uint32 srcPitch = temporaryBufferPitch;
355 					pp_uint32 dstPitch = theSurface->pitch;
356 
357 					pp_uint8* src = (pp_uint8*)temporaryBuffer;
358 					pp_uint8* dst = (pp_uint8*)theSurface->pixels;
359 
360 					const pp_uint32 srcBPP = temporaryBufferBPP/8;
361 					const pp_uint32 dstBPP = theSurface->format->BytesPerPixel;
362 
363 					pp_uint32 v = r.y1 * 65536;
364 					for (pp_uint32 y = destRect.y1; y < destRect.y2; y++)
365 					{
366 						pp_uint32 u = r.x1 * 65536;
367 						pp_uint32* dstPtr = (pp_uint32*)(dst + y*dstPitch + destRect.x1*dstBPP);
368 						pp_uint8* srcPtr = src + (v>>16)*srcPitch;
369 						for (pp_uint32 x = destRect.x1; x < destRect.x2; x++)
370 						{
371 							*dstPtr++ = *(pp_uint32*)(srcPtr + (u>>16) * srcBPP);
372 							u += stepU;
373 						}
374 						v += stepV;
375 					}
376 
377 					break;
378 				}
379 
380 				default:
381 					fprintf(stderr, "SDL: Unsupported color depth for requested orientation");
382 					exit(2);
383 			}
384 
385 			SDL_UnlockSurface(theSurface);
386 
387 			break;
388 		}
389 
390 		case ORIENTATION_ROTATE90CCW:
391 		{
392 			if (SDL_LockSurface(theSurface) < 0)
393 				return;
394 
395 			switch (temporaryBufferBPP)
396 			{
397 				case 16:
398 				{
399 					pp_uint32 srcPitch = temporaryBufferPitch >> 1;
400 					pp_uint32 dstPitch = theSurface->pitch >> 1;
401 
402 					pp_uint16* src = (pp_uint16*)temporaryBuffer;
403 					pp_uint16* dst = (pp_uint16*)theSurface->pixels;
404 
405 					if (scaleFactor != 1)
406 					{
407 						PPRect destRect(r);
408 						destRect.scale(scaleFactor);
409 
410 						const pp_uint32 stepU = (r.x2 - r.x1) * 65536 / (destRect.x2 - destRect.x1);
411 						const pp_uint32 stepV = (r.y2 - r.y1) * 65536 / (destRect.y2 - destRect.y1);
412 
413 						pp_uint32 v = r.y1 * 65536;
414 						for (pp_uint32 y = destRect.y1; y < destRect.y2; y++)
415 						{
416 							pp_uint32 u = r.x1 * 65536;
417 							pp_uint16* srcPtr = src + (v>>16)*srcPitch;
418 							pp_uint16* dstPtr = dst + y + (realHeight-destRect.x1)*dstPitch;
419 							for (pp_uint32 x = destRect.x1; x < destRect.x2; x++)
420 							{
421 								*(dstPtr-=dstPitch) = *(srcPtr+(u>>16));
422 
423 								u += stepU;
424 							}
425 							v += stepV;
426 						}
427 					}
428 					else
429 					{
430 						for (pp_uint32 y = r.y1; y < r.y2; y++)
431 						{
432 							pp_uint16* srcPtr = src + y*srcPitch + r.x1;
433 							pp_uint16* dstPtr = dst + y + (realHeight-r.x1)*dstPitch;
434 							for (pp_uint32 x = r.x1; x < r.x2; x++)
435 								*(dstPtr-=dstPitch) = *srcPtr++;
436 						}
437 					}
438 
439 					break;
440 				}
441 
442 				case 24:
443 				{
444 					pp_uint32 srcPitch = temporaryBufferPitch;
445 					pp_uint32 dstPitch = theSurface->pitch;
446 
447 					pp_uint8* src = (pp_uint8*)temporaryBuffer;
448 					pp_uint8* dst = (pp_uint8*)theSurface->pixels;
449 
450 					const pp_uint32 srcBPP = temporaryBufferBPP/8;
451 					const pp_uint32 dstBPP = theSurface->format->BytesPerPixel;
452 
453 					if (scaleFactor != 1)
454 					{
455 						PPRect destRect(r);
456 						destRect.scale(scaleFactor);
457 
458 						const pp_uint32 stepU = (r.x2 - r.x1) * 65536 / (destRect.x2 - destRect.x1);
459 						const pp_uint32 stepV = (r.y2 - r.y1) * 65536 / (destRect.y2 - destRect.y1);
460 
461 						pp_uint32 v = r.y1 * 65536;
462 						for (pp_uint32 y = destRect.y1; y < destRect.y2; y++)
463 						{
464 							pp_uint32 u = r.x1 * 65536;
465 							pp_uint8* srcPtr = src + (v>>16)*srcPitch;
466 							pp_uint8* dstPtr = dst + y*dstBPP + dstPitch*(realHeight-1-destRect.x1);
467 							for (pp_uint32 x = destRect.x1; x < destRect.x2; x++)
468 							{
469 								dstPtr[0] = *(srcPtr+(u>>16) * srcBPP);
470 								dstPtr[1] = *(srcPtr+(u>>16) * srcBPP + 1);
471 								dstPtr[2] = *(srcPtr+(u>>16) * srcBPP + 2);
472 								dstPtr-=dstPitch;
473 
474 								u += stepU;
475 							}
476 							v += stepV;
477 						}
478 					}
479 					else
480 					{
481 						for (pp_uint32 y = r.y1; y < r.y2; y++)
482 						{
483 							pp_uint8* srcPtr = src + y*srcPitch + r.x1*srcBPP;
484 							pp_uint8* dstPtr = dst + y*dstBPP + dstPitch*(realHeight-1-r.x1);
485 							for (pp_uint32 x = r.x1; x < r.x2; x++)
486 							{
487 								dstPtr[0] = srcPtr[0];
488 								dstPtr[1] = srcPtr[1];
489 								dstPtr[2] = srcPtr[2];
490 								srcPtr+=srcBPP;
491 								dstPtr-=dstPitch;
492 							}
493 						}
494 					}
495 
496 					break;
497 				}
498 
499 				case 32:
500 				{
501 					pp_uint32 srcPitch = temporaryBufferPitch;
502 					pp_uint32 dstPitch = theSurface->pitch;
503 
504 					pp_uint8* src = (pp_uint8*)temporaryBuffer;
505 					pp_uint8* dst = (pp_uint8*)theSurface->pixels;
506 
507 					const pp_uint32 srcBPP = temporaryBufferBPP/8;
508 					const pp_uint32 dstBPP = theSurface->format->BytesPerPixel;
509 
510 					if (scaleFactor != 1)
511 					{
512 						PPRect destRect(r);
513 						destRect.scale(scaleFactor);
514 
515 						const pp_uint32 stepU = (r.x2 - r.x1) * 65536 / (destRect.x2 - destRect.x1);
516 						const pp_uint32 stepV = (r.y2 - r.y1) * 65536 / (destRect.y2 - destRect.y1);
517 
518 						pp_uint32 v = r.y1 * 65536;
519 						for (pp_uint32 y = destRect.y1; y < destRect.y2; y++)
520 						{
521 							pp_uint32 u = r.x1 * 65536;
522 							pp_uint8* srcPtr = src + (v>>16)*srcPitch;
523 							pp_uint32* dstPtr = (pp_uint32*)(dst + y*dstBPP + dstPitch*(realHeight-1-destRect.x1));
524 							for (pp_uint32 x = destRect.x1; x < destRect.x2; x++)
525 							{
526 								*(dstPtr-=(dstPitch>>2)) = *(pp_uint32*)(srcPtr + (u>>16) * srcBPP);
527 								u += stepU;
528 							}
529 							v += stepV;
530 						}
531 					}
532 					else
533 					{
534 						for (pp_uint32 y = r.y1; y < r.y2; y++)
535 						{
536 							pp_uint32* srcPtr = (pp_uint32*)(src + y*srcPitch + r.x1*srcBPP);
537 							pp_uint32* dstPtr = (pp_uint32*)(dst + y*dstBPP + dstPitch*(realHeight-1-r.x1));
538 							for (pp_uint32 x = r.x1; x < r.x2; x++)
539 								*(dstPtr-=(dstPitch>>2)) = *srcPtr++;
540 						}
541 					}
542 
543 					break;
544 				}
545 
546 				default:
547 					fprintf(stderr, "SDL: Unsupported color depth for requested orientation");
548 					exit(2);
549 			}
550 
551 			SDL_UnlockSurface(theSurface);
552 			break;
553 		}
554 
555 		case ORIENTATION_ROTATE90CW:
556 		{
557 			if (SDL_LockSurface(theSurface) < 0)
558 				return;
559 
560 			switch (temporaryBufferBPP)
561 			{
562 				case 16:
563 				{
564 					pp_uint32 srcPitch = temporaryBufferPitch >> 1;
565 					pp_uint32 dstPitch = theSurface->pitch >> 1;
566 
567 					pp_uint16* src = (pp_uint16*)temporaryBuffer;
568 					pp_uint16* dst = (pp_uint16*)theSurface->pixels;
569 
570 					if (scaleFactor != 1)
571 					{
572 						PPRect destRect(r);
573 						destRect.scale(scaleFactor);
574 
575 						const pp_uint32 stepU = (r.x2 - r.x1) * 65536 / (destRect.x2 - destRect.x1);
576 						const pp_uint32 stepV = (r.y2 - r.y1) * 65536 / (destRect.y2 - destRect.y1);
577 
578 						pp_uint32 v = r.y1 * 65536;
579 						for (pp_uint32 y = destRect.y1; y < destRect.y2; y++)
580 						{
581 							pp_uint32 u = r.x1 * 65536;
582 							pp_uint16* srcPtr = src + (v>>16)*srcPitch;
583 							pp_uint16* dstPtr = dst + (realWidth-1-y) + (dstPitch*(destRect.x1));
584 							for (pp_uint32 x = destRect.x1; x < destRect.x2; x++)
585 							{
586 								*(dstPtr+=dstPitch) = *(srcPtr+(u>>16));
587 
588 								u += stepU;
589 							}
590 							v += stepV;
591 						}
592 					}
593 					else
594 					{
595 						for (pp_uint32 y = r.y1; y < r.y2; y++)
596 						{
597 							pp_uint16* srcPtr = src + y*srcPitch + r.x1;
598 							pp_uint16* dstPtr = dst + (realWidth-1-y) + (dstPitch*r.x1);
599 							for (pp_uint32 x = r.x1; x < r.x2; x++)
600 								*(dstPtr+=dstPitch) = *srcPtr++;
601 						}
602 					}
603 
604 					break;
605 				}
606 
607 				case 24:
608 				{
609 					pp_uint32 srcPitch = temporaryBufferPitch;
610 					pp_uint32 dstPitch = theSurface->pitch;
611 
612 					pp_uint8* src = (pp_uint8*)temporaryBuffer;
613 					pp_uint8* dst = (pp_uint8*)theSurface->pixels;
614 
615 					const pp_uint32 srcBPP = temporaryBufferBPP/8;
616 					const pp_uint32 dstBPP = theSurface->format->BytesPerPixel;
617 
618 					if (scaleFactor != 1)
619 					{
620 						PPRect destRect(r);
621 						destRect.scale(scaleFactor);
622 
623 						const pp_uint32 stepU = (r.x2 - r.x1) * 65536 / (destRect.x2 - destRect.x1);
624 						const pp_uint32 stepV = (r.y2 - r.y1) * 65536 / (destRect.y2 - destRect.y1);
625 
626 						pp_uint32 v = r.y1 * 65536;
627 						for (pp_uint32 y = destRect.y1; y < destRect.y2; y++)
628 						{
629 							pp_uint32 u = r.x1 * 65536;
630 							pp_uint8* srcPtr = src + (v>>16)*srcPitch;
631 							pp_uint8* dstPtr = dst + (realWidth-1-y)*dstBPP + (dstPitch*(destRect.x1));
632 							for (pp_uint32 x = destRect.x1; x < destRect.x2; x++)
633 							{
634 								dstPtr[0] = *(srcPtr+(u>>16) * srcBPP);
635 								dstPtr[1] = *(srcPtr+(u>>16) * srcBPP + 1);
636 								dstPtr[2] = *(srcPtr+(u>>16) * srcBPP + 2);
637 								dstPtr+=dstPitch;
638 
639 								u += stepU;
640 							}
641 							v += stepV;
642 						}
643 					}
644 					else
645 					{
646 						for (pp_uint32 y = r.y1; y < r.y2; y++)
647 						{
648 							pp_uint8* srcPtr = src + y*srcPitch + r.x1*srcBPP;
649 							pp_uint8* dstPtr = dst + (realWidth-1-y)*dstBPP + (dstPitch*r.x1);
650 							for (pp_uint32 x = r.x1; x < r.x2; x++)
651 							{
652 								dstPtr[0] = srcPtr[0];
653 								dstPtr[1] = srcPtr[1];
654 								dstPtr[2] = srcPtr[2];
655 								srcPtr+=srcBPP;
656 								dstPtr+=dstPitch;
657 							}
658 						}
659 					}
660 
661 					break;
662 				}
663 
664 				case 32:
665 				{
666 					pp_uint32 srcPitch = temporaryBufferPitch;
667 					pp_uint32 dstPitch = theSurface->pitch;
668 
669 					pp_uint8* src = (pp_uint8*)temporaryBuffer;
670 					pp_uint8* dst = (pp_uint8*)theSurface->pixels;
671 
672 					const pp_uint32 srcBPP = temporaryBufferBPP/8;
673 					const pp_uint32 dstBPP = theSurface->format->BytesPerPixel;
674 
675 					if (scaleFactor != 1)
676 					{
677 						PPRect destRect(r);
678 						destRect.scale(scaleFactor);
679 
680 						const pp_uint32 stepU = (r.x2 - r.x1) * 65536 / (destRect.x2 - destRect.x1);
681 						const pp_uint32 stepV = (r.y2 - r.y1) * 65536 / (destRect.y2 - destRect.y1);
682 
683 						pp_uint32 v = r.y1 * 65536;
684 						for (pp_uint32 y = destRect.y1; y < destRect.y2; y++)
685 						{
686 							pp_uint32 u = r.x1 * 65536;
687 							pp_uint8* srcPtr = src + (v>>16)*srcPitch;
688 							pp_uint32* dstPtr = (pp_uint32*)(dst + (realWidth-1-y)*dstBPP + (dstPitch*(destRect.x1)));
689 							for (pp_uint32 x = destRect.x1; x < destRect.x2; x++)
690 							{
691 								*(dstPtr+=(dstPitch>>2)) = *(pp_uint32*)(srcPtr + (u>>16) * srcBPP);
692 								u += stepU;
693 							}
694 							v += stepV;
695 						}
696 					}
697 					else
698 					{
699 						for (pp_uint32 y = r.y1; y < r.y2; y++)
700 						{
701 							pp_uint32* srcPtr = (pp_uint32*)(src + y*srcPitch + r.x1*srcBPP);
702 							pp_uint32* dstPtr = (pp_uint32*)(dst + (realWidth-1-y)*dstBPP + (dstPitch*r.x1));
703 							for (pp_uint32 x = r.x1; x < r.x2; x++)
704 								*(dstPtr+=(dstPitch>>2)) = *srcPtr++;
705 						}
706 					}
707 
708 					break;
709 				}
710 
711 				default:
712 					fprintf(stderr, "SDL: Unsupported color depth for requested orientation");
713 					exit(EXIT_FAILURE);
714 			}
715 
716 			SDL_UnlockSurface(theSurface);
717 			break;
718 		}
719 	}
720 
721 }
722 
723 // This is unused at the moment, could be useful if we manage to get the GUI resizable in the future.
setSize(const PPSize & size)724 void PPDisplayDeviceFB::setSize(const PPSize& size)
725 {
726 	this->size = size;
727 	theSurface = SDL_CreateRGBSurface(0, size.width, size.height, theSurface->format->BitsPerPixel, 0, 0, 0, 0);
728 	theTexture = SDL_CreateTextureFromSurface(theRenderer, theSurface);
729 	theRenderer = SDL_GetRenderer(theWindow);
730 }
731