1 /*
2 Hatari - screen.c
3
4 This file is distributed under the GNU General Public License, version 2
5 or at your option any later version. Read the file gpl.txt for details.
6
7 This code converts a 1/2/4 plane ST format screen to either 8, 16 or 32-bit PC
8 format. An awful lot of processing is needed to do this conversion - we
9 cannot simply change palettes on interrupts as it is possible with DOS.
10 The main code processes the palette/resolution mask tables to find exactly
11 which lines need to updating and the conversion routines themselves only
12 update 16-pixel blocks which differ from the previous frame - this gives a
13 large performance increase.
14 Each conversion routine can convert any part of the source ST screen (which
15 includes the overscan border, usually set to colour zero) so they can be used
16 for both window and full-screen mode.
17 Note that in Hi-Resolution we have no overscan and just two colors so we can
18 optimise things further.
19 In color mode it seems possible to display 47 lines in the bottom border
20 with a second 60/50 Hz switch, but most programs consider there are 45
21 visible lines in the bottom border only, which gives a total of 274 lines
22 for a screen. So not displaying the last two lines fixes garbage that could
23 appear in the last two lines when displaying 47 lines (Digiworld 2 by ICE,
24 Tyranny by DHS).
25 */
26
27 const char Screen_fileid[] = "Hatari screen.c : " __DATE__ " " __TIME__;
28
29 #include <SDL.h>
30 #include <SDL_endian.h>
31 #include <assert.h>
32
33 #include "main.h"
34 #include "configuration.h"
35 #include "avi_record.h"
36 #include "log.h"
37 #include "paths.h"
38 #include "options.h"
39 #include "screen.h"
40 #include "screenConvert.h"
41 #include "control.h"
42 #include "convert/routines.h"
43 #include "resolution.h"
44 #include "spec512.h"
45 #include "statusbar.h"
46 #include "vdi.h"
47 #include "video.h"
48 #include "falcon/videl.h"
49
50 #define DEBUG 0
51
52 #if DEBUG
53 # define DEBUGPRINT(x) printf x
54 #else
55 # define DEBUGPRINT(x)
56 #endif
57
58 /* extern for several purposes */
59 SDL_Surface *sdlscrn = NULL; /* The SDL screen surface */
60 int nScreenZoomX, nScreenZoomY; /* Zooming factors, used for scaling mouse motions */
61 int nBorderPixelsLeft, nBorderPixelsRight; /* Pixels in left and right border */
62 static int nBorderPixelsTop, nBorderPixelsBottom; /* Lines in top and bottom border */
63
64 /* extern for shortcuts etc. */
65 bool bGrabMouse = false; /* Grab the mouse cursor in the window */
66 bool bInFullScreen = false; /* true if in full screen */
67
68 /* extern for spec512.c */
69 int STScreenLeftSkipBytes;
70 int STScreenStartHorizLine; /* Start lines to be converted */
71 Uint32 STRGBPalette[16]; /* Palette buffer used in conversion routines */
72 Uint32 ST2RGB[4096]; /* Table to convert ST 0x777 / STe 0xfff palette to PC format RGB551 (2 pixels each entry) */
73
74 /* extern for video.c */
75 Uint8 *pSTScreen;
76 FRAMEBUFFER *pFrameBuffer; /* Pointer into current 'FrameBuffer' */
77
78 static FRAMEBUFFER FrameBuffers[NUM_FRAMEBUFFERS]; /* Store frame buffer details to tell how to update */
79 static Uint8 *pSTScreenCopy; /* Keep track of current and previous ST screen data */
80 static Uint8 *pPCScreenDest; /* Destination PC buffer */
81 static int STScreenEndHorizLine; /* End lines to be converted */
82 static int PCScreenBytesPerLine;
83 static int STScreenWidthBytes;
84 static int PCScreenOffsetX; /* how many pixels to skip from left when drawing */
85 static int PCScreenOffsetY; /* how many pixels to skip from top when drawing */
86 static SDL_Rect STScreenRect; /* screen size without statusbar */
87
88 static int STScreenLineOffset[NUM_VISIBLE_LINES]; /* Offsets for ST screen lines eg, 0,160,320... */
89 static Uint16 HBLPalette[16], PrevHBLPalette[16]; /* Current palette for line, also copy of first line */
90
91 static void (*ScreenDrawFunctionsNormal[3])(void); /* Screen draw functions */
92
93 static bool bScreenContentsChanged; /* true if buffer changed and requires blitting */
94 static bool bScrDoubleY; /* true if double on Y */
95 static int ScrUpdateFlag; /* Bit mask of how to update screen */
96 static bool bRGBTableInSync; /* Is RGB table up to date? */
97
98 /* These are used for the generic screen conversion functions */
99 static int genconv_width_req, genconv_height_req, genconv_bpp;
100 static bool genconv_do_update; /* HW surface is available -> the SDL need not to update the surface after ->pixel access */
101
102
103 static bool Screen_DrawFrame(bool bForceFlip);
104
105 #if WITH_SDL2
106
107 SDL_Window *sdlWindow;
108 static SDL_Renderer *sdlRenderer;
109 static SDL_Texture *sdlTexture;
110 static bool bUseSdlRenderer; /* true when using SDL2 renderer */
111
SDL_UpdateRects(SDL_Surface * screen,int numrects,SDL_Rect * rects)112 void SDL_UpdateRects(SDL_Surface *screen, int numrects, SDL_Rect *rects)
113 {
114 if (bUseSdlRenderer)
115 {
116 SDL_UpdateTexture(sdlTexture, NULL, screen->pixels, screen->pitch);
117 SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);
118 SDL_RenderPresent(sdlRenderer);
119 }
120 else
121 {
122 SDL_UpdateWindowSurfaceRects(sdlWindow, rects, numrects);
123 }
124 }
125
SDL_UpdateRect(SDL_Surface * screen,Sint32 x,Sint32 y,Sint32 w,Sint32 h)126 void SDL_UpdateRect(SDL_Surface *screen, Sint32 x, Sint32 y, Sint32 w, Sint32 h)
127 {
128 SDL_Rect rect;
129
130 if (w == 0 && h == 0) {
131 x = y = 0;
132 w = screen->w;
133 h = screen->h;
134 }
135
136 rect.x = x; rect.y = y;
137 rect.w = w; rect.h = h;
138 SDL_UpdateRects(screen, 1, &rect);
139 }
140
141 #endif /* WITH_SDL2 */
142
143 /*-----------------------------------------------------------------------*/
144 /**
145 * Create ST 0x777 / STe 0xfff color format to 16 or 32 bits per pixel
146 * conversion table. Called each time when changed resolution or to/from
147 * fullscreen mode.
148 */
Screen_SetupRGBTable(void)149 static void Screen_SetupRGBTable(void)
150 {
151 Uint16 STColor;
152 Uint32 RGBColor;
153 int r, g, b;
154 int rr, gg, bb;
155
156 /* Do Red, Green and Blue for all 16*16*16 = 4096 STe colors */
157 for (r = 0; r < 16; r++)
158 {
159 for (g = 0; g < 16; g++)
160 {
161 for (b = 0; b < 16; b++)
162 {
163 /* STe 0xfff format */
164 STColor = (r<<8) | (g<<4) | (b);
165 rr = ((r & 0x7) << 1) | ((r & 0x8) >> 3);
166 rr |= rr << 4;
167 gg = ((g & 0x7) << 1) | ((g & 0x8) >> 3);
168 gg |= gg << 4;
169 bb = ((b & 0x7) << 1) | ((b & 0x8) >> 3);
170 bb |= bb << 4;
171 RGBColor = SDL_MapRGB(sdlscrn->format, rr, gg, bb);
172 if (sdlscrn->format->BitsPerPixel <= 16)
173 {
174 /* As longs, for speed (write two pixels at once) */
175 ST2RGB[STColor] = (RGBColor<<16) | RGBColor;
176 }
177 else
178 {
179 ST2RGB[STColor] = RGBColor;
180 }
181 }
182 }
183 }
184 }
185
186
187 /**
188 * Convert 640x400 monochrome screen
189 */
Screen_ConvertHighRes(void)190 static void Screen_ConvertHighRes(void)
191 {
192 int linewidth = 640 / 16;
193
194 Screen_GenConvert(pSTScreen, 640, 400, 1, linewidth, 0, 0, 0, 0, 0);
195 bScreenContentsChanged = true;
196 }
197
198 /**
199 * Set screen draw functions.
200 */
Screen_SetDrawFunctions(int nBitCount,bool bDoubleLowRes)201 static void Screen_SetDrawFunctions(int nBitCount, bool bDoubleLowRes)
202 {
203 if (nBitCount <= 16)
204 {
205 /* High color */
206 if (bDoubleLowRes)
207 ScreenDrawFunctionsNormal[ST_LOW_RES] = ConvertLowRes_640x16Bit;
208 else
209 ScreenDrawFunctionsNormal[ST_LOW_RES] = ConvertLowRes_320x16Bit;
210 ScreenDrawFunctionsNormal[ST_MEDIUM_RES] = ConvertMediumRes_640x16Bit;
211 }
212 else /* Assume 32 bit drawing functions */
213 {
214 /* True color */
215 if (bDoubleLowRes)
216 ScreenDrawFunctionsNormal[ST_LOW_RES] = ConvertLowRes_640x32Bit;
217 else
218 ScreenDrawFunctionsNormal[ST_LOW_RES] = ConvertLowRes_320x32Bit;
219 ScreenDrawFunctionsNormal[ST_MEDIUM_RES] = ConvertMediumRes_640x32Bit;
220 }
221 }
222
223
224 /*-----------------------------------------------------------------------*/
225 /**
226 * Set amount of border pixels
227 */
Screen_SetBorderPixels(int leftX,int leftY)228 static void Screen_SetBorderPixels(int leftX, int leftY)
229 {
230 /* All screen widths need to be aligned to 16-bits */
231 nBorderPixelsLeft = Opt_ValueAlignMinMax(leftX/2, 16, 0, 48);
232 nBorderPixelsRight = nBorderPixelsLeft;
233
234 /* assertain assumption of code below */
235 assert(OVERSCAN_TOP < MAX_OVERSCAN_BOTTOM);
236
237 if (leftY > 2*OVERSCAN_TOP)
238 {
239 nBorderPixelsTop = OVERSCAN_TOP;
240 if (leftY >= OVERSCAN_TOP + MAX_OVERSCAN_BOTTOM)
241 nBorderPixelsBottom = MAX_OVERSCAN_BOTTOM;
242 else
243 nBorderPixelsBottom = leftY - OVERSCAN_TOP;
244 }
245 else
246 {
247 if (leftY > 0)
248 nBorderPixelsTop = nBorderPixelsBottom = leftY/2;
249 else
250 nBorderPixelsTop = nBorderPixelsBottom = 0;
251 }
252 }
253
254 /*-----------------------------------------------------------------------*/
255 /**
256 * store Y offset for each horizontal line in our source ST screen for
257 * reference in the convert functions.
258 */
Screen_SetSTScreenOffsets(void)259 static void Screen_SetSTScreenOffsets(void)
260 {
261 int i;
262
263 /* Store offset to each horizontal line, uses
264 * nBorderPixels* variables.
265 */
266 for (i = 0; i < NUM_VISIBLE_LINES; i++)
267 {
268 STScreenLineOffset[i] = i * SCREENBYTES_LINE;
269 }
270 }
271
272 /**
273 * Return true if Falcon/TT/VDI generic screen convert functions
274 * need to be used instead of the ST/STE functions.
275 */
Screen_UseGenConvScreen(void)276 static bool Screen_UseGenConvScreen(void)
277 {
278 return Config_IsMachineFalcon() || Config_IsMachineTT()
279 || bUseHighRes || bUseVDIRes;
280 }
281
Screen_WantToKeepResolution(void)282 static bool Screen_WantToKeepResolution(void)
283 {
284 #if WITH_SDL2
285 return ConfigureParams.Screen.bKeepResolution;
286 #else
287 if (Screen_UseGenConvScreen())
288 return ConfigureParams.Screen.bKeepResolution;
289 else
290 return ConfigureParams.Screen.bKeepResolutionST;
291 #endif
292 }
293
294 #if WITH_SDL2
Screen_FreeSDL2Resources(void)295 static void Screen_FreeSDL2Resources(void)
296 {
297 if (sdlTexture)
298 {
299 SDL_DestroyTexture(sdlTexture);
300 sdlTexture = NULL;
301 }
302 if (sdlscrn)
303 {
304 if (bUseSdlRenderer)
305 SDL_FreeSurface(sdlscrn);
306 sdlscrn = NULL;
307 }
308 if (sdlRenderer)
309 {
310 SDL_DestroyRenderer(sdlRenderer);
311 sdlRenderer = NULL;
312 }
313 }
314 #endif
315
316 /**
317 * Change the SDL video mode.
318 * @return true if mode has been changed, false if change was not necessary
319 */
Screen_SetSDLVideoSize(int width,int height,int bitdepth,bool bForceChange)320 bool Screen_SetSDLVideoSize(int width, int height, int bitdepth, bool bForceChange)
321 {
322 Uint32 sdlVideoFlags;
323 char *psSdlVideoDriver;
324 bool bUseDummyMode;
325 #if WITH_SDL2
326 static int nPrevRenderScaleQuality = 0;
327 static bool bPrevUseVsync = false;
328 static bool bPrevInFullScreen;
329 int win_width, win_height;
330
331 if (bitdepth == 0 || bitdepth == 24)
332 bitdepth = 32;
333 #endif
334 /* Check if we really have to change the video mode: */
335 if (sdlscrn != NULL && sdlscrn->w == width && sdlscrn->h == height
336 && sdlscrn->format->BitsPerPixel == bitdepth && !bForceChange)
337 return false;
338
339 psSdlVideoDriver = SDL_getenv("SDL_VIDEODRIVER");
340 bUseDummyMode = psSdlVideoDriver && !strcmp(psSdlVideoDriver, "dummy");
341 if (bUseDummyMode)
342 {
343 ConfigureParams.Screen.DisableVideo = true;
344 }
345
346 #ifdef _MUDFLAP
347 if (sdlscrn)
348 {
349 __mf_unregister(sdlscrn->pixels, sdlscrn->pitch*sdlscrn->h, __MF_TYPE_GUESS);
350 }
351 #endif
352 if (bInFullScreen)
353 {
354 /* unhide the Hatari WM window for fullscreen */
355 Control_ReparentWindow(width, height, bInFullScreen);
356 }
357
358 #if WITH_SDL2
359 bUseSdlRenderer = ConfigureParams.Screen.bUseSdlRenderer && !bUseDummyMode;
360
361 /* SDL Video attributes: */
362 win_width = width;
363 win_height = height;
364 if (bInFullScreen)
365 {
366 sdlVideoFlags = SDL_WINDOW_BORDERLESS | SDL_WINDOW_INPUT_GRABBED;
367 if (ConfigureParams.Screen.bKeepResolution)
368 sdlVideoFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
369 else
370 sdlVideoFlags |= SDL_WINDOW_FULLSCREEN;
371 }
372 else
373 {
374 int deskw, deskh;
375 if (getenv("PARENT_WIN_ID") != NULL) /* Embedded window? */
376 sdlVideoFlags = SDL_WINDOW_BORDERLESS;
377 else if (ConfigureParams.Screen.bResizable && bUseSdlRenderer)
378 sdlVideoFlags = SDL_WINDOW_RESIZABLE;
379 else
380 sdlVideoFlags = 0;
381 /* Make sure that window is not bigger than current desktop */
382 Resolution_GetDesktopSize(&deskw, &deskh);
383 if (win_width > deskw)
384 win_width = deskw;
385 if (win_height > deskh)
386 win_height = deskh;
387 }
388
389 Screen_FreeSDL2Resources();
390 if (sdlWindow &&
391 ((bInFullScreen && !ConfigureParams.Screen.bKeepResolution) ||
392 (bPrevInFullScreen != bInFullScreen) ||
393 bForceChange
394 ))
395 {
396 SDL_DestroyWindow(sdlWindow);
397 sdlWindow = NULL;
398 }
399 bPrevInFullScreen = bInFullScreen;
400
401 /* Set SDL2 video hints */
402 if (nPrevRenderScaleQuality != ConfigureParams.Screen.nRenderScaleQuality)
403 {
404 char hint[2] = { '0' + ConfigureParams.Screen.nRenderScaleQuality, 0 };
405 SDL_SetHintWithPriority(SDL_HINT_RENDER_SCALE_QUALITY, hint, SDL_HINT_OVERRIDE);
406 nPrevRenderScaleQuality = ConfigureParams.Screen.nRenderScaleQuality;
407 }
408 if (bPrevUseVsync != ConfigureParams.Screen.bUseVsync)
409 {
410 char hint[2] = { '0' + ConfigureParams.Screen.bUseVsync, 0 };
411 SDL_SetHintWithPriority(SDL_HINT_RENDER_VSYNC, hint, SDL_HINT_OVERRIDE);
412 bPrevUseVsync = ConfigureParams.Screen.bUseVsync;
413 }
414
415 #ifdef SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4 /* requires sdl >= 2.0.4 */
416 /* Disable closing Hatari with alt+F4 under Windows as alt+F4 can be used by some emulated programs */
417 SDL_SetHintWithPriority(SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4, "1", SDL_HINT_OVERRIDE);
418 #endif
419
420 /* Set new video mode */
421 DEBUGPRINT(("SDL screen request: %d x %d @ %d (%s)\n", width, height,
422 bitdepth, bInFullScreen?"fullscreen":"windowed"));
423
424 if (sdlWindow)
425 {
426 if ((SDL_GetWindowFlags(sdlWindow) & SDL_WINDOW_MAXIMIZED) == 0)
427 SDL_SetWindowSize(sdlWindow, win_width, win_height);
428 }
429 else
430 {
431 sdlWindow = SDL_CreateWindow("Hatari", SDL_WINDOWPOS_UNDEFINED,
432 SDL_WINDOWPOS_UNDEFINED,
433 win_width, win_height, sdlVideoFlags);
434 }
435 if (!sdlWindow)
436 {
437 fprintf(stderr, "ERROR: Failed to create %dx%d window!\n",
438 win_width, win_height);
439 exit(-1);
440 }
441 if (bUseSdlRenderer)
442 {
443 int rm, bm, gm, pfmt;
444
445 sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, 0);
446 if (!sdlRenderer)
447 {
448 fprintf(stderr, "ERROR: Failed to create %dx%d renderer!\n",
449 win_width, win_height);
450 exit(1);
451 }
452
453 if (bInFullScreen)
454 SDL_RenderSetLogicalSize(sdlRenderer, width, height);
455
456 if (bitdepth == 16)
457 {
458 rm = 0xF800;
459 gm = 0x07E0;
460 bm = 0x001F;
461 pfmt = SDL_PIXELFORMAT_RGB565;
462 }
463 else
464 {
465 rm = 0x00FF0000;
466 gm = 0x0000FF00;
467 bm = 0x000000FF;
468 pfmt = SDL_PIXELFORMAT_RGB888;
469 }
470 sdlscrn = SDL_CreateRGBSurface(0, width, height, bitdepth,
471 rm, gm, bm, 0);
472 sdlTexture = SDL_CreateTexture(sdlRenderer, pfmt,
473 SDL_TEXTUREACCESS_STREAMING,
474 width, height);
475 if (!sdlTexture)
476 {
477 fprintf(stderr, "ERROR: Failed to create %dx%d@%d texture!\n",
478 width, height, bitdepth);
479 exit(-3);
480 }
481 }
482 else
483 {
484 sdlscrn = SDL_GetWindowSurface(sdlWindow);
485 }
486
487 #else /* !WITH_SDL2 */
488
489 /* SDL Video attributes: */
490 if (bInFullScreen)
491 {
492 sdlVideoFlags = SDL_HWSURFACE|SDL_FULLSCREEN/*|SDL_DOUBLEBUF*/;
493 /* SDL_DOUBLEBUF helps avoiding tearing and can be faster on suitable HW,
494 * but it doesn't work with partial screen updates done by the ST screen
495 * update code or the Hatari GUI, so double buffering is disabled. */
496 }
497 else
498 {
499 sdlVideoFlags = SDL_SWSURFACE;
500 }
501
502 /* Set new video mode */
503 DEBUGPRINT(("SDL screen request: %d x %d @ %d (%s)\n", width, height, bitdepth,
504 bInFullScreen?"fullscreen":"windowed"));
505 sdlscrn = SDL_SetVideoMode(width, height, bitdepth, sdlVideoFlags);
506
507 /* By default ConfigureParams.Screen.nForceBpp and therefore
508 * BitCount is zero which means "SDL color depth autodetection".
509 * In this case the SDL_SetVideoMode() call might return
510 * a 24 bpp resolution
511 */
512 if (sdlscrn && sdlscrn->format->BitsPerPixel == 24)
513 {
514 Log_Printf(LOG_WARN, "Unsupported color depth 24, trying 32 bpp instead...\n");
515 sdlscrn = SDL_SetVideoMode(width, height, 32, sdlVideoFlags);
516 }
517
518 #endif /* !WITH_SDL2 */
519
520 /* Exit if we can not open a screen */
521 if (!sdlscrn)
522 {
523 fprintf(stderr, "ERROR: Could not set video mode:\n %s\n", SDL_GetError() );
524 SDL_Quit();
525 exit(-2);
526 }
527
528 DEBUGPRINT(("SDL screen granted: %d x %d @ %d\n", sdlscrn->w, sdlscrn->h,
529 sdlscrn->format->BitsPerPixel));
530
531 #ifdef _MUDFLAP
532 __mf_register(sdlscrn->pixels, sdlscrn->pitch*sdlscrn->h, __MF_TYPE_GUESS, "SDL pixels");
533 #endif
534
535 if (!bInFullScreen)
536 {
537 /* re-embed the new Hatari SDL window */
538 Control_ReparentWindow(width, height, bInFullScreen);
539 }
540
541 Avi_SetSurface(sdlscrn);
542
543 bRGBTableInSync = false;
544
545 return true;
546 }
547
548
549 /**
550 * Initialize ST/STE screen resolution.
551 */
Screen_SetSTResolution(bool bForceChange)552 static void Screen_SetSTResolution(bool bForceChange)
553 {
554 int Width, Height, nZoom, SBarHeight, BitCount, maxW, maxH;
555 bool bDoubleLowRes = false;
556
557 /* Bits per pixel */
558 BitCount = ConfigureParams.Screen.nForceBpp;
559
560 nBorderPixelsTop = nBorderPixelsBottom = 0;
561 nBorderPixelsLeft = nBorderPixelsRight = 0;
562
563 nScreenZoomX = 1;
564 nScreenZoomY = 1;
565
566 if (STRes == ST_LOW_RES)
567 {
568 Width = 320;
569 Height = 200;
570 nZoom = 1;
571 }
572 else /* else use 640x400, also for med-rez */
573 {
574 Width = 640;
575 Height = 400;
576 nZoom = 2;
577 }
578
579 /* Statusbar height for doubled screen size */
580 SBarHeight = Statusbar_GetHeightForSize(640, 400);
581
582 Resolution_GetLimits(&maxW, &maxH, &BitCount, Screen_WantToKeepResolution());
583
584 /* Zoom if necessary, factors used for scaling mouse motions */
585 if (STRes == ST_LOW_RES &&
586 2*Width <= maxW && 2*Height+SBarHeight <= maxH)
587 {
588 nZoom = 2;
589 Width *= 2;
590 Height *= 2;
591 nScreenZoomX = 2;
592 nScreenZoomY = 2;
593 bDoubleLowRes = true;
594 }
595 else if (STRes == ST_MEDIUM_RES)
596 {
597 /* med-rez conversion functions want always
598 * to double vertically, they don't support
599 * skipping that (only leaving doubled lines
600 * black for the TV mode).
601 */
602 nScreenZoomX = 1;
603 nScreenZoomY = 2;
604 }
605
606 /* Adjust width/height for overscan borders, if mono or VDI we have no overscan */
607 if (ConfigureParams.Screen.bAllowOverscan && !bUseHighRes)
608 {
609 int leftX = maxW - Width;
610 int leftY = maxH - (Height + Statusbar_GetHeightForSize(Width, Height));
611
612 Screen_SetBorderPixels(leftX/nZoom, leftY/nZoom);
613 DEBUGPRINT(("resolution limit:\n\t%d x %d\nlimited resolution:\n\t", maxW, maxH));
614 DEBUGPRINT(("%d * (%d + %d + %d) x (%d + %d + %d)\n", nZoom,
615 nBorderPixelsLeft, Width/nZoom, nBorderPixelsRight,
616 nBorderPixelsTop, Height/nZoom, nBorderPixelsBottom));
617 Width += (nBorderPixelsRight + nBorderPixelsLeft)*nZoom;
618 Height += (nBorderPixelsTop + nBorderPixelsBottom)*nZoom;
619 DEBUGPRINT(("\t= %d x %d (+ statusbar)\n", Width, Height));
620 }
621
622 Screen_SetSTScreenOffsets();
623 Height += Statusbar_SetHeight(Width, Height);
624
625 PCScreenOffsetX = PCScreenOffsetY = 0;
626
627 /* Video attributes: */
628 #if !WITH_SDL2
629 if (bInFullScreen && ConfigureParams.Screen.bKeepResolutionST)
630 {
631 /* use desktop resolution */
632 Resolution_GetDesktopSize(&maxW, &maxH);
633 SBarHeight = Statusbar_GetHeightForSize(maxW, maxH);
634 /* re-calculate statusbar height for this resolution */
635 Statusbar_SetHeight(maxW, maxH-SBarHeight);
636 /* center Atari screen to resolution */
637 PCScreenOffsetY = (maxH - Height)/2;
638 PCScreenOffsetX = (maxW - Width)/2;
639 /* and select desktop resolution */
640 Height = maxH;
641 Width = maxW;
642 }
643 #endif
644
645 if (Screen_SetSDLVideoSize(Width, Height, BitCount, bForceChange))
646 {
647 Statusbar_Init(sdlscrn);
648
649 /* screen area without the statusbar */
650 STScreenRect.x = 0;
651 STScreenRect.y = 0;
652 STScreenRect.w = sdlscrn->w;
653 STScreenRect.h = sdlscrn->h - Statusbar_GetHeight();
654 }
655
656 if (!bRGBTableInSync)
657 {
658 Screen_SetupRGBTable(); /* Create color conversion table */
659 bRGBTableInSync = true;
660 }
661
662 /* Set drawing functions */
663 Screen_SetDrawFunctions(sdlscrn->format->BitsPerPixel, bDoubleLowRes);
664
665 Screen_SetFullUpdate(); /* Cause full update of screen */
666 }
667
668
669 /**
670 * Change resolution, according to the machine and display type
671 * that we're currently emulating.
672 */
Screen_ChangeResolution(bool bForceChange)673 static void Screen_ChangeResolution(bool bForceChange)
674 {
675 int hbpp = ConfigureParams.Screen.nForceBpp;
676
677 if (bUseVDIRes)
678 {
679 Screen_SetGenConvSize(VDIWidth, VDIHeight, hbpp, bForceChange);
680 }
681 else if (Config_IsMachineFalcon())
682 {
683 Videl_ScreenModeChanged(bForceChange);
684 }
685 else if (Config_IsMachineTT())
686 {
687 int width, height, bpp;
688 Video_GetTTRes(&width, &height, &bpp);
689 Screen_SetGenConvSize(width, height, hbpp, bForceChange);
690 }
691 else if (bUseHighRes)
692 {
693 Screen_SetGenConvSize(640, 400, hbpp, bForceChange);
694 }
695 else
696 {
697 Screen_SetSTResolution(bForceChange);
698 }
699
700 if (bInFullScreen || bGrabMouse)
701 SDL_WM_GrabInput(SDL_GRAB_ON);
702 else
703 SDL_WM_GrabInput(SDL_GRAB_OFF);
704 }
705
706
707 /**
708 * Change the resolution - but only if it was already initialized before
709 */
Screen_ModeChanged(bool bForceChange)710 void Screen_ModeChanged(bool bForceChange)
711 {
712 if (sdlscrn) /* Do it only if we're already up and running */
713 {
714 Screen_ChangeResolution(bForceChange);
715 }
716 }
717
718
719 /*-----------------------------------------------------------------------*/
720 /**
721 * Init Screen bitmap and buffers/tables needed for ST to PC screen conversion
722 */
Screen_Init(void)723 void Screen_Init(void)
724 {
725 int i;
726 SDL_Surface *pIconSurf;
727 char sIconFileName[FILENAME_MAX];
728
729 /* Clear frame buffer structures and set current pointer */
730 memset(FrameBuffers, 0, NUM_FRAMEBUFFERS * sizeof(FRAMEBUFFER));
731
732 /* Allocate previous screen check workspace. We are going to double-buffer a double-buffered screen. Oh. */
733 for (i = 0; i < NUM_FRAMEBUFFERS; i++)
734 {
735 FrameBuffers[i].pSTScreen = malloc(MAX_VDI_BYTES);
736 FrameBuffers[i].pSTScreenCopy = malloc(MAX_VDI_BYTES);
737 if (!FrameBuffers[i].pSTScreen || !FrameBuffers[i].pSTScreenCopy)
738 {
739 fprintf(stderr, "ERROR: Failed to allocate frame buffer memory.\n");
740 exit(-1);
741 }
742 }
743 pFrameBuffer = &FrameBuffers[0];
744
745 /* Set initial window resolution */
746 bInFullScreen = ConfigureParams.Screen.bFullScreen;
747 Screen_ChangeResolution(false);
748 ScreenDrawFunctionsNormal[ST_HIGH_RES] = Screen_ConvertHighRes;
749
750 Video_SetScreenRasters(); /* Set rasters ready for first screen */
751
752 /* Load and set icon */
753 snprintf(sIconFileName, sizeof(sIconFileName), "%s%chatari-icon.bmp",
754 Paths_GetDataDir(), PATHSEP);
755 pIconSurf = SDL_LoadBMP(sIconFileName);
756 if (pIconSurf)
757 {
758 #if WITH_SDL2
759 SDL_SetColorKey(pIconSurf, SDL_TRUE, SDL_MapRGB(pIconSurf->format, 255, 255, 255));
760 SDL_SetWindowIcon(sdlWindow, pIconSurf);
761 #else
762 SDL_SetColorKey(pIconSurf, SDL_SRCCOLORKEY, SDL_MapRGB(pIconSurf->format, 255, 255, 255));
763 SDL_WM_SetIcon(pIconSurf, NULL);
764 #endif
765 SDL_FreeSurface(pIconSurf);
766 }
767
768 /* Configure some SDL stuff: */
769 SDL_ShowCursor(SDL_DISABLE);
770 }
771
772
773 /*-----------------------------------------------------------------------*/
774 /**
775 * Free screen bitmap and allocated resources
776 */
Screen_UnInit(void)777 void Screen_UnInit(void)
778 {
779 int i;
780
781 /* Free memory used for copies */
782 for (i = 0; i < NUM_FRAMEBUFFERS; i++)
783 {
784 free(FrameBuffers[i].pSTScreen);
785 free(FrameBuffers[i].pSTScreenCopy);
786 }
787
788 #if WITH_SDL2
789 Screen_FreeSDL2Resources();
790 if (sdlWindow)
791 {
792 SDL_DestroyWindow(sdlWindow);
793 sdlWindow = NULL;
794 }
795 #endif
796 }
797
798
799 /*-----------------------------------------------------------------------*/
800 /**
801 * Reset screen
802 */
Screen_Reset(void)803 void Screen_Reset(void)
804 {
805 /* On re-boot, always correct ST resolution for monitor, eg Colour/Mono */
806 if (bUseVDIRes)
807 {
808 STRes = VDIRes;
809 }
810 else
811 {
812 if (bUseHighRes)
813 {
814 STRes = ST_HIGH_RES;
815 TTRes = TT_HIGH_RES;
816 }
817 else
818 {
819 STRes = ST_LOW_RES;
820 TTRes = TT_MEDIUM_RES;
821 }
822 }
823 /* Cause full update */
824 Screen_ModeChanged(false);
825 }
826
827
828 /*-----------------------------------------------------------------------*/
829 /**
830 * Set flags so screen will be TOTALLY re-drawn (clears whole of full-screen)
831 * next time around
832 */
Screen_SetFullUpdate(void)833 void Screen_SetFullUpdate(void)
834 {
835 int i;
836
837 /* Update frame buffers */
838 for (i = 0; i < NUM_FRAMEBUFFERS; i++)
839 FrameBuffers[i].bFullUpdate = true;
840 }
841
842
843 /*-----------------------------------------------------------------------*/
844 /**
845 * Clear Window display memory
846 */
Screen_ClearScreen(void)847 static void Screen_ClearScreen(void)
848 {
849 SDL_FillRect(sdlscrn, &STScreenRect, SDL_MapRGB(sdlscrn->format, 0, 0, 0));
850 }
851
852
853 /*-----------------------------------------------------------------------*/
854 /**
855 * Force screen redraw. Does the right thing regardless of whether
856 * we're in ST/STe, Falcon or TT mode. Needed when switching modes
857 * while emulation is paused.
858 */
Screen_Refresh(void)859 static void Screen_Refresh(void)
860 {
861 if (bUseVDIRes)
862 {
863 Screen_GenDraw(VideoBase, VDIWidth, VDIHeight, VDIPlanes,
864 VDIWidth * VDIPlanes / 16, 0, 0, 0, 0);
865 }
866 else if (Config_IsMachineFalcon())
867 {
868 VIDEL_renderScreen();
869 }
870 else if (Config_IsMachineTT())
871 {
872 Video_RenderTTScreen();
873 }
874 else
875 {
876 Screen_DrawFrame(true);
877 }
878 }
879
880
881 /*-----------------------------------------------------------------------*/
882 /**
883 * Enter Full screen mode
884 */
Screen_EnterFullScreen(void)885 void Screen_EnterFullScreen(void)
886 {
887 bool bWasRunning;
888
889 if (!bInFullScreen)
890 {
891 /* Hold things... */
892 bWasRunning = Main_PauseEmulation(false);
893 bInFullScreen = true;
894
895 if (Screen_UseGenConvScreen())
896 {
897 Screen_SetGenConvSize(genconv_width_req, genconv_height_req,
898 genconv_bpp, true);
899 /* force screen redraw */
900 Screen_GenConvUpdate(NULL, true);
901 }
902 else
903 {
904 Screen_SetSTResolution(true);
905 Screen_ClearScreen(); /* Black out screen bitmap as will be invalid when return */
906 }
907
908 if (!Screen_WantToKeepResolution())
909 {
910 /* Give monitor time to change to new resolution */
911 SDL_Delay(20);
912 }
913
914 if (bWasRunning)
915 {
916 /* And off we go... */
917 Main_UnPauseEmulation();
918 }
919 else
920 {
921 Screen_Refresh();
922 }
923 SDL_WM_GrabInput(SDL_GRAB_ON); /* Grab mouse pointer in fullscreen */
924 }
925 }
926
927
928 /*-----------------------------------------------------------------------*/
929 /**
930 * Return from Full screen mode back to a window
931 */
Screen_ReturnFromFullScreen(void)932 void Screen_ReturnFromFullScreen(void)
933 {
934 bool bWasRunning;
935
936 if (bInFullScreen)
937 {
938 /* Hold things... */
939 bWasRunning = Main_PauseEmulation(false);
940 bInFullScreen = false;
941
942 if (Screen_UseGenConvScreen())
943 {
944 Screen_SetGenConvSize(genconv_width_req, genconv_height_req,
945 genconv_bpp, true);
946 /* force screen redraw */
947 Screen_GenConvUpdate(NULL, true);
948 }
949 else
950 {
951 Screen_SetSTResolution(true);
952 }
953
954 if (!Screen_WantToKeepResolution())
955 {
956 /* Give monitor time to switch resolution */
957 SDL_Delay(20);
958 }
959
960 if (bWasRunning)
961 {
962 /* And off we go... */
963 Main_UnPauseEmulation();
964 }
965 else
966 {
967 Screen_Refresh();
968 }
969
970 if (!bGrabMouse)
971 {
972 /* Un-grab mouse pointer in windowed mode */
973 SDL_WM_GrabInput(SDL_GRAB_OFF);
974 }
975 }
976 }
977
978
979 /*-----------------------------------------------------------------------*/
980 /**
981 * Have we changed between low/med/high res?
982 */
Screen_DidResolutionChange(int new_res)983 static void Screen_DidResolutionChange(int new_res)
984 {
985 if (new_res != STRes)
986 {
987 STRes = new_res;
988 Screen_ModeChanged(false);
989 }
990 else
991 {
992 /* Did change overscan mode? Causes full update */
993 if (pFrameBuffer->VerticalOverscanCopy != VerticalOverscan)
994 pFrameBuffer->bFullUpdate = true;
995 }
996 }
997
998
999 /**
1000 * Compare current resolution on line with previous, and set 'UpdateLine' accordingly
1001 * Return if swap between low/medium resolution
1002 */
Screen_CompareResolution(int y,int * pUpdateLine,int oldres)1003 static bool Screen_CompareResolution(int y, int *pUpdateLine, int oldres)
1004 {
1005 /* Check if wrote to resolution register */
1006 if (HBLPaletteMasks[y]&PALETTEMASK_RESOLUTION) /* See 'Intercept_ShifterMode_WriteByte' */
1007 {
1008 int newres = (HBLPaletteMasks[y]>>16)&ST_MEDIUM_RES_BIT;
1009 /* Did resolution change? */
1010 if (newres != (int)((pFrameBuffer->HBLPaletteMasks[y]>>16)&ST_MEDIUM_RES_BIT))
1011 *pUpdateLine |= PALETTEMASK_UPDATERES;
1012 else
1013 *pUpdateLine &= ~PALETTEMASK_UPDATERES;
1014 /* Have used any low/medium res mix? */
1015 return (newres != (oldres&ST_MEDIUM_RES_BIT));
1016 }
1017 return false;
1018 }
1019
1020
1021 /*-----------------------------------------------------------------------*/
1022 /**
1023 * Check to see if palette changes cause screen update and keep 'HBLPalette[]' up-to-date
1024 */
Screen_ComparePalette(int y,int * pUpdateLine)1025 static void Screen_ComparePalette(int y, int *pUpdateLine)
1026 {
1027 bool bPaletteChanged = false;
1028 int i;
1029
1030 /* Did write to palette in this or previous frame? */
1031 if (((HBLPaletteMasks[y]|pFrameBuffer->HBLPaletteMasks[y])&PALETTEMASK_PALETTE)!=0)
1032 {
1033 /* Check and update ones which changed */
1034 for (i = 0; i < 16; i++)
1035 {
1036 if (HBLPaletteMasks[y]&(1<<i)) /* Update changes in ST palette */
1037 HBLPalette[i] = HBLPalettes[(y*16)+i];
1038 }
1039 /* Now check with same palette from previous frame for any differences(may be changing palette back) */
1040 for (i = 0; (i < 16) && (!bPaletteChanged); i++)
1041 {
1042 if (HBLPalette[i]!=pFrameBuffer->HBLPalettes[(y*16)+i])
1043 bPaletteChanged = true;
1044 }
1045 if (bPaletteChanged)
1046 *pUpdateLine |= PALETTEMASK_UPDATEPAL;
1047 else
1048 *pUpdateLine &= ~PALETTEMASK_UPDATEPAL;
1049 }
1050 }
1051
1052
1053 /*-----------------------------------------------------------------------*/
1054 /**
1055 * Check for differences in Palette and Resolution from Mask table and update
1056 * and store off which lines need updating and create full-screen palette.
1057 * (It is very important for these routines to check for colour changes with
1058 * the previous screen so only the very minimum parts are updated).
1059 * Return new STRes value.
1060 */
Screen_ComparePaletteMask(int res)1061 static int Screen_ComparePaletteMask(int res)
1062 {
1063 bool bLowMedMix = false;
1064 int LineUpdate = 0;
1065 int y;
1066
1067 /* Set for monochrome? */
1068 if (bUseHighRes)
1069 {
1070 VerticalOverscan = V_OVERSCAN_NONE;
1071
1072 /* Just copy mono colors */
1073 if (HBLPalettes[0] & 0x777)
1074 {
1075 HBLPalettes[0] = 0x777;
1076 HBLPalettes[1] = 0x000;
1077 }
1078 else
1079 {
1080 HBLPalettes[0] = 0x000;
1081 HBLPalettes[1] = 0x777;
1082 }
1083
1084 /* Colors changed? */
1085 if (HBLPalettes[0] != PrevHBLPalette[0])
1086 pFrameBuffer->bFullUpdate = true;
1087
1088 /* Set bit to flag 'full update' */
1089 if (pFrameBuffer->bFullUpdate)
1090 ScrUpdateFlag = PALETTEMASK_UPDATEFULL;
1091 else
1092 ScrUpdateFlag = 0x00000000;
1093
1094 /* Force to standard hi-resolution screen, without overscan */
1095 res = ST_HIGH_RES;
1096 }
1097 else /* Full colour */
1098 {
1099 /* Get resolution */
1100 //res = (HBLPaletteMasks[0]>>16)&ST_RES_MASK;
1101 /* [NP] keep only low/med bit (could be hires in case of overscan on the 1st line) */
1102 res = (HBLPaletteMasks[0]>>16)&ST_MEDIUM_RES_BIT;
1103
1104 /* Do all lines - first is tagged as full-update */
1105 for (y = 0; y < NUM_VISIBLE_LINES; y++)
1106 {
1107 /* Find any resolution/palette change and update palette/mask buffer */
1108 /* ( LineUpdate has top two bits set to say if line needs updating due to palette or resolution change ) */
1109 bLowMedMix |= Screen_CompareResolution(y, &LineUpdate, res);
1110 Screen_ComparePalette(y,&LineUpdate);
1111 HBLPaletteMasks[y] = (HBLPaletteMasks[y]&(~PALETTEMASK_UPDATEMASK)) | LineUpdate;
1112 /* Copy palette and mask for next frame */
1113 memcpy(&pFrameBuffer->HBLPalettes[y*16],HBLPalette,sizeof(short int)*16);
1114 pFrameBuffer->HBLPaletteMasks[y] = HBLPaletteMasks[y];
1115 }
1116 /* Did mix/have medium resolution? */
1117 if (bLowMedMix || (res & ST_MEDIUM_RES_BIT))
1118 res = ST_MEDIUM_RES;
1119 }
1120
1121 /* Copy old palette for compare */
1122 memcpy(PrevHBLPalette, HBLPalettes, sizeof(Uint16)*16);
1123
1124 return res;
1125 }
1126
1127
1128 /*-----------------------------------------------------------------------*/
1129 /**
1130 * Update Palette Mask to show 'full-update' required. This is usually done after a resolution change
1131 * or when going between a Window and full-screen display
1132 */
Screen_SetFullUpdateMask(void)1133 static void Screen_SetFullUpdateMask(void)
1134 {
1135 int y;
1136
1137 for (y = 0; y < NUM_VISIBLE_LINES; y++)
1138 HBLPaletteMasks[y] |= PALETTEMASK_UPDATEFULL;
1139 }
1140
1141
1142 /*-----------------------------------------------------------------------*/
1143 /**
1144 * Set details for ST screen conversion.
1145 */
Screen_SetConvertDetails(void)1146 static void Screen_SetConvertDetails(void)
1147 {
1148 pSTScreen = pFrameBuffer->pSTScreen; /* Source in ST memory */
1149 pSTScreenCopy = pFrameBuffer->pSTScreenCopy; /* Previous ST screen */
1150 pPCScreenDest = sdlscrn->pixels; /* Destination PC screen */
1151
1152 PCScreenBytesPerLine = sdlscrn->pitch; /* Bytes per line */
1153
1154 /* Center to available framebuffer */
1155 pPCScreenDest += PCScreenOffsetY * PCScreenBytesPerLine + PCScreenOffsetX * (sdlscrn->format->BitsPerPixel/8);
1156
1157 pHBLPalettes = pFrameBuffer->HBLPalettes; /* HBL palettes pointer */
1158 /* Not in TV-Mode? Then double up on Y: */
1159 bScrDoubleY = !(ConfigureParams.Screen.nMonitorType == MONITOR_TYPE_TV);
1160
1161 if (ConfigureParams.Screen.bAllowOverscan) /* Use borders? */
1162 {
1163 /* Always draw to WHOLE screen including ALL borders */
1164 STScreenLeftSkipBytes = 0; /* Number of bytes to skip on ST screen for left (border) */
1165
1166 if (bUseHighRes)
1167 {
1168 pFrameBuffer->VerticalOverscanCopy = VerticalOverscan = V_OVERSCAN_NONE;
1169 STScreenStartHorizLine = 0;
1170 STScreenEndHorizLine = 400;
1171 }
1172 else
1173 {
1174 STScreenWidthBytes = SCREENBYTES_LINE; /* Number of horizontal bytes in our ST screen */
1175 STScreenStartHorizLine = OVERSCAN_TOP - nBorderPixelsTop;
1176 STScreenEndHorizLine = OVERSCAN_TOP + 200 + nBorderPixelsBottom;
1177 }
1178 }
1179 else
1180 {
1181 /* Only draw main area and centre on Y */
1182 STScreenLeftSkipBytes = SCREENBYTES_LEFT;
1183 STScreenWidthBytes = SCREENBYTES_MIDDLE;
1184 STScreenStartHorizLine = OVERSCAN_TOP;
1185 STScreenEndHorizLine = OVERSCAN_TOP + (bUseHighRes ? 400 : 200);
1186 }
1187 }
1188
1189
1190 /*-----------------------------------------------------------------------*/
1191 /**
1192 * Lock full-screen for drawing
1193 */
Screen_Lock(void)1194 bool Screen_Lock(void)
1195 {
1196 if (SDL_MUSTLOCK(sdlscrn))
1197 {
1198 if (SDL_LockSurface(sdlscrn))
1199 {
1200 Screen_ReturnFromFullScreen(); /* All OK? If not need to jump back to a window */
1201 return false;
1202 }
1203 }
1204
1205 return true;
1206 }
1207
1208 /*-----------------------------------------------------------------------*/
1209 /**
1210 * UnLock full-screen
1211 */
Screen_UnLock(void)1212 void Screen_UnLock(void)
1213 {
1214 if ( SDL_MUSTLOCK(sdlscrn) )
1215 SDL_UnlockSurface(sdlscrn);
1216 }
1217
1218
1219 /*-----------------------------------------------------------------------*/
1220 /**
1221 * Blit our converted ST screen to window/full-screen
1222 */
Screen_Blit(SDL_Rect * sbar_rect)1223 static void Screen_Blit(SDL_Rect *sbar_rect)
1224 {
1225 unsigned char *pTmpScreen;
1226
1227 #if 0 /* double buffering cannot be used with partial screen updates */
1228 # if NUM_FRAMEBUFFERS > 1
1229 if (bInFullScreen && (sdlscrn->flags & SDL_DOUBLEBUF))
1230 {
1231 /* Swap screen */
1232 if (pFrameBuffer==&FrameBuffers[0])
1233 pFrameBuffer = &FrameBuffers[1];
1234 else
1235 pFrameBuffer = &FrameBuffers[0];
1236 SDL_Flip(sdlscrn);
1237 }
1238 else
1239 # endif
1240 #endif
1241 {
1242 int count = 1;
1243 SDL_Rect rects[2];
1244 rects[0] = STScreenRect;
1245 if (sbar_rect)
1246 {
1247 rects[1] = *sbar_rect;
1248 count = 2;
1249 }
1250 SDL_UpdateRects(sdlscrn, count, rects);
1251 }
1252
1253 /* Swap copy/raster buffers in screen. */
1254 pTmpScreen = pFrameBuffer->pSTScreenCopy;
1255 pFrameBuffer->pSTScreenCopy = pFrameBuffer->pSTScreen;
1256 pFrameBuffer->pSTScreen = pTmpScreen;
1257 }
1258
1259
1260 /*-----------------------------------------------------------------------*/
1261 /**
1262 * Draw ST screen to window/full-screen framebuffer
1263 * @param bForceFlip Force screen update, even if contents did not change
1264 * @return true if screen contents changed
1265 */
Screen_DrawFrame(bool bForceFlip)1266 static bool Screen_DrawFrame(bool bForceFlip)
1267 {
1268 int new_res;
1269 void (*pDrawFunction)(void);
1270 static bool bPrevFrameWasSpec512 = false;
1271 SDL_Rect *sbar_rect;
1272
1273 assert(!bUseVDIRes);
1274
1275 /* Scan palette/resolution masks for each line and build up palette/difference tables */
1276 new_res = Screen_ComparePaletteMask(STRes);
1277 /* Did we change resolution this frame - allocate new screen if did so */
1278 Screen_DidResolutionChange(new_res);
1279 /* Is need full-update, tag as such */
1280 if (pFrameBuffer->bFullUpdate)
1281 Screen_SetFullUpdateMask();
1282
1283 /* restore area potentially left under overlay led
1284 * and saved by Statusbar_OverlayBackup()
1285 */
1286 Statusbar_OverlayRestore(sdlscrn);
1287
1288 /* Lock screen for direct screen surface format writes */
1289 if (ConfigureParams.Screen.DisableVideo || !Screen_Lock())
1290 {
1291 return false;
1292 }
1293
1294 bScreenContentsChanged = false; /* Did change (ie needs blit?) */
1295
1296 /* Set details */
1297 Screen_SetConvertDetails();
1298
1299 /* Clear screen on full update to clear out borders and also interleaved lines */
1300 if (pFrameBuffer->bFullUpdate)
1301 Screen_ClearScreen();
1302
1303 /* Call drawing for full-screen */
1304 pDrawFunction = ScreenDrawFunctionsNormal[STRes];
1305 /* Check if is Spec512 image */
1306 if (Spec512_IsImage())
1307 {
1308 bPrevFrameWasSpec512 = true;
1309 /* What mode were we in? Keep to 320xH or 640xH */
1310 if (pDrawFunction==ConvertLowRes_320x16Bit)
1311 pDrawFunction = ConvertLowRes_320x16Bit_Spec;
1312 else if (pDrawFunction==ConvertLowRes_640x16Bit)
1313 pDrawFunction = ConvertLowRes_640x16Bit_Spec;
1314 else if (pDrawFunction==ConvertLowRes_320x32Bit)
1315 pDrawFunction = ConvertLowRes_320x32Bit_Spec;
1316 else if (pDrawFunction==ConvertLowRes_640x32Bit)
1317 pDrawFunction = ConvertLowRes_640x32Bit_Spec;
1318 else if (pDrawFunction==ConvertMediumRes_640x32Bit)
1319 pDrawFunction = ConvertMediumRes_640x32Bit_Spec;
1320 else if (pDrawFunction==ConvertMediumRes_640x16Bit)
1321 pDrawFunction = ConvertMediumRes_640x16Bit_Spec;
1322 }
1323 else if (bPrevFrameWasSpec512)
1324 {
1325 /* If we switch back from Spec512 mode to normal
1326 * screen rendering, we have to make sure to do
1327 * a full update of the screen. */
1328 Screen_SetFullUpdateMask();
1329 bPrevFrameWasSpec512 = false;
1330 }
1331
1332 if (pDrawFunction)
1333 CALL_VAR(pDrawFunction);
1334
1335 /* Unlock screen */
1336 Screen_UnLock();
1337
1338 /* draw overlay led(s) or statusbar after unlock */
1339 Statusbar_OverlayBackup(sdlscrn);
1340 sbar_rect = Statusbar_Update(sdlscrn, false);
1341
1342 /* Clear flags, remember type of overscan as if change need screen full update */
1343 pFrameBuffer->bFullUpdate = false;
1344 pFrameBuffer->VerticalOverscanCopy = VerticalOverscan;
1345
1346 /* And show to user */
1347 if (bScreenContentsChanged || bForceFlip || sbar_rect)
1348 {
1349 Screen_Blit(sbar_rect);
1350 }
1351
1352 return bScreenContentsChanged;
1353 }
1354
1355
1356 /*-----------------------------------------------------------------------*/
1357 /**
1358 * Draw ST screen to window/full-screen
1359 */
Screen_Draw(void)1360 bool Screen_Draw(void)
1361 {
1362 if (bQuitProgram)
1363 {
1364 return false;
1365 }
1366
1367 /* And draw (if screen contents changed) */
1368 return Screen_DrawFrame(false);
1369 }
1370
1371 /**
1372 * This is used to set the size of the SDL screen
1373 * when we're using the generic conversion functions.
1374 */
Screen_SetGenConvSize(int width,int height,int bpp,bool bForceChange)1375 void Screen_SetGenConvSize(int width, int height, int bpp, bool bForceChange)
1376 {
1377 const bool keep = ConfigureParams.Screen.bKeepResolution;
1378 int screenwidth, screenheight, maxw, maxh;
1379 int scalex, scaley, sbarheight;
1380
1381 if (bpp == 24)
1382 bpp = 32;
1383
1384 /* constrain size request to user's desktop size */
1385 Resolution_GetDesktopSize(&maxw, &maxh);
1386 #if !WITH_SDL2
1387 scalex = scaley = 1;
1388 while (width > maxw*scalex) {
1389 scalex *= 2;
1390 }
1391 while (height > maxh*scaley) {
1392 scaley *= 2;
1393 }
1394 if (scalex * scaley > 1) {
1395 Log_Printf(LOG_WARN, "Too large screen size %dx%d -> divided by %dx%d!\n",
1396 width, height, scalex, scaley);
1397 width /= scalex;
1398 height /= scaley;
1399 }
1400 #endif
1401
1402 Resolution_GetLimits(&maxw, &maxh, &bpp, keep);
1403 nScreenZoomX = nScreenZoomY = 1;
1404
1405 if (ConfigureParams.Screen.bAspectCorrect) {
1406 /* Falcon (and TT) pixel scaling factors seem to 2^x
1407 * (quarter/half pixel, interlace/double line), so
1408 * do aspect correction as 2's exponent.
1409 */
1410 while (nScreenZoomX*width < height &&
1411 2*nScreenZoomX*width < maxw) {
1412 nScreenZoomX *= 2;
1413 }
1414 while (2*nScreenZoomY*height < width &&
1415 2*nScreenZoomY*height < maxh) {
1416 nScreenZoomY *= 2;
1417 }
1418 if (nScreenZoomX*nScreenZoomY > 2) {
1419 Log_Printf(LOG_WARN, "Strange screen size %dx%d -> aspect corrected by %dx%d!\n",
1420 width, height, nScreenZoomX, nScreenZoomY);
1421 }
1422 }
1423
1424 /* then select scale as close to target size as possible
1425 * without having larger size than it
1426 */
1427 scalex = maxw/(nScreenZoomX*width);
1428 scaley = maxh/(nScreenZoomY*height);
1429 if (scalex > 1 && scaley > 1) {
1430 /* keep aspect ratio */
1431 if (scalex < scaley) {
1432 nScreenZoomX *= scalex;
1433 nScreenZoomY *= scalex;
1434 } else {
1435 nScreenZoomX *= scaley;
1436 nScreenZoomY *= scaley;
1437 }
1438 }
1439
1440 genconv_width_req = width;
1441 genconv_height_req = height;
1442 width *= nScreenZoomX;
1443 height *= nScreenZoomY;
1444
1445 /* get statusbar size for this screen size */
1446 sbarheight = Statusbar_GetHeightForSize(width, height);
1447 screenheight = height + sbarheight;
1448 screenwidth = width;
1449
1450 #if !WITH_SDL2
1451 /* get resolution corresponding to these */
1452 Resolution_Search(&screenwidth, &screenheight, &bpp, keep);
1453 #endif
1454 /* re-calculate statusbar height for this resolution */
1455 sbarheight = Statusbar_SetHeight(screenwidth, screenheight-sbarheight);
1456
1457 genconv_bpp = bpp;
1458 /* screen area without the statusbar */
1459 STScreenRect.x = STScreenRect.y = 0;
1460 STScreenRect.w = screenwidth;
1461 STScreenRect.h = screenheight - sbarheight;
1462
1463 if (!Screen_SetSDLVideoSize(screenwidth, screenheight, bpp, bForceChange))
1464 {
1465 /* same host screen size despite Atari resolution change,
1466 * -> no time consuming host video mode change needed
1467 */
1468 if (screenwidth > width || screenheight > height+sbarheight) {
1469 /* Atari screen smaller than host -> clear screen */
1470 Screen_ClearScreen();
1471 /* re-calculate variables in case height + statusbar height
1472 * don't anymore match SDL surface size (there's an assert
1473 * for that)
1474 */
1475 Statusbar_Init(sdlscrn);
1476 }
1477 #if WITH_SDL2
1478 genconv_do_update = true;
1479 #else
1480 genconv_do_update = ( sdlscrn->flags & SDL_HWSURFACE ) == 0;
1481 #endif
1482 return;
1483 }
1484
1485 // In case surface format changed, remap the native palette
1486 Screen_RemapPalette();
1487
1488 // redraw statusbar
1489 Statusbar_Init(sdlscrn);
1490
1491 DEBUGPRINT(("Surface Pitch = %d, width = %d, height = %d\n", sdlscrn->pitch, sdlscrn->w, sdlscrn->h));
1492 DEBUGPRINT(("Must Lock? %s\n", SDL_MUSTLOCK(sdlscrn) ? "YES" : "NO"));
1493
1494 #if WITH_SDL2
1495 genconv_do_update = true;
1496 #else
1497 // is the SDL_update needed?
1498 genconv_do_update = ( sdlscrn->flags & SDL_HWSURFACE ) == 0;
1499 #endif
1500
1501 DEBUGPRINT(("Pixel format:bitspp=%d, tmasks r=%04x g=%04x b=%04x"
1502 ", tshifts r=%d g=%d b=%d"
1503 ", tlosses r=%d g=%d b=%d\n",
1504 sdlscrn->format->BitsPerPixel,
1505 sdlscrn->format->Rmask, sdlscrn->format->Gmask, sdlscrn->format->Bmask,
1506 sdlscrn->format->Rshift, sdlscrn->format->Gshift, sdlscrn->format->Bshift,
1507 sdlscrn->format->Rloss, sdlscrn->format->Gloss, sdlscrn->format->Bloss));
1508
1509 Main_WarpMouse(sdlscrn->w/2,sdlscrn->h/2, false);
1510 }
1511
Screen_GenConvUpdate(SDL_Rect * extra,bool forced)1512 void Screen_GenConvUpdate(SDL_Rect *extra, bool forced)
1513 {
1514 SDL_Rect rects[2];
1515 int count = 1;
1516
1517 /* Don't update anything on screen if video output is disabled */
1518 if ( ConfigureParams.Screen.DisableVideo )
1519 return;
1520
1521 if (!forced && !genconv_do_update) // the HW surface is available
1522 return;
1523
1524 rects[0] = STScreenRect;
1525 if (extra) {
1526 rects[1] = *extra;
1527 count = 2;
1528 }
1529 SDL_UpdateRects(sdlscrn, count, rects);
1530 }
1531
Screen_GetGenConvWidth(void)1532 Uint32 Screen_GetGenConvWidth(void)
1533 {
1534 return STScreenRect.w;
1535 }
1536
Screen_GetGenConvHeight(void)1537 Uint32 Screen_GetGenConvHeight(void)
1538 {
1539 return STScreenRect.h;
1540 }
1541
1542
1543 /* -------------- screen conversion routines --------------------------------
1544 Screen conversion routines. We have a number of routines to convert ST screen
1545 to PC format. We split these into Low, Medium and High each with 8/16-bit
1546 versions. To gain extra speed, as almost half of the processing time can be
1547 spent in these routines, we check for any changes from the previously
1548 displayed frame. AdjustLinePaletteRemap() sets a flag to tell the routines
1549 if we need to totally update a line (ie full update, or palette/res change)
1550 or if we just can do a difference check.
1551 We convert each screen 16 pixels at a time by use of a couple of look-up
1552 tables. These tables convert from 2-plane format to bbp and then we can add
1553 two of these together to get 4-planes. This keeps the tables small and thus
1554 improves speed. We then look these bbp values up as an RGB/Index value to
1555 copy to the screen.
1556 */
1557
1558
1559 /*-----------------------------------------------------------------------*/
1560 /**
1561 * Update the STRGBPalette[] array with current colours for this raster line.
1562 *
1563 * Return 'ScrUpdateFlag', 0x80000000=Full update, 0x40000000=Update
1564 * as palette changed
1565 */
AdjustLinePaletteRemap(int y)1566 static int AdjustLinePaletteRemap(int y)
1567 {
1568 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
1569 static const int endiantable[16] = {0,2,1,3,8,10,9,11,4,6,5,7,12,14,13,15};
1570 #endif
1571 Uint16 *actHBLPal;
1572 int i;
1573
1574 /* Copy palette and convert to RGB in display format */
1575 actHBLPal = pHBLPalettes + (y<<4); /* offset in palette */
1576 for (i=0; i<16; i++)
1577 {
1578 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
1579 STRGBPalette[endiantable[i]] = ST2RGB[*actHBLPal++];
1580 #else
1581 STRGBPalette[i] = ST2RGB[*actHBLPal++];
1582 #endif
1583 }
1584 ScrUpdateFlag = HBLPaletteMasks[y];
1585 return ScrUpdateFlag;
1586 }
1587
1588
1589 /*-----------------------------------------------------------------------*/
1590 /**
1591 * Run updates to palette(STRGBPalette[]) until get to screen line
1592 * we are to convert from
1593 */
Convert_StartFrame(void)1594 static void Convert_StartFrame(void)
1595 {
1596 int y = 0;
1597 /* Get #lines before conversion starts */
1598 int lines = STScreenStartHorizLine;
1599 while (lines--)
1600 AdjustLinePaletteRemap(y++); /* Update palette */
1601 }
1602
1603 /* lookup tables and conversion macros */
1604 #include "convert/macros.h"
1605
1606 /* Conversion routines */
1607
1608 #include "convert/low320x16.c" /* LowRes To 320xH x 16-bit color */
1609 #include "convert/low640x16.c" /* LowRes To 640xH x 16-bit color */
1610 #include "convert/med640x16.c" /* MediumRes To 640xH x 16-bit color */
1611 #include "convert/low320x16_spec.c" /* LowRes Spectrum 512 To 320xH x 16-bit color */
1612 #include "convert/low640x16_spec.c" /* LowRes Spectrum 512 To 640xH x 16-bit color */
1613 #include "convert/med640x16_spec.c" /* MediumRes Spectrum 512 To 640xH x 16-bit color */
1614
1615 #include "convert/low320x32.c" /* LowRes To 320xH x 32-bit color */
1616 #include "convert/low640x32.c" /* LowRes To 640xH x 32-bit color */
1617 #include "convert/med640x32.c" /* MediumRes To 640xH x 32-bit color */
1618 #include "convert/low320x32_spec.c" /* LowRes Spectrum 512 To 320xH x 32-bit color */
1619 #include "convert/low640x32_spec.c" /* LowRes Spectrum 512 To 640xH x 32-bit color */
1620 #include "convert/med640x32_spec.c" /* MediumRes Spectrum 512 To 640xH x 32-bit color */
1621