2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8 /** @file sdl2_default_v.cpp Implementation of the default backend for SDL2 video driver. */
10 #include "../stdafx.h"
11 #include "../openttd.h"
12 #include "../error_func.h"
13 #include "../gfx_func.h"
15 #include "../blitter/factory.hpp"
16 #include "../network/network.h"
17 #include "../thread.h"
18 #include "../progress.h"
19 #include "../core/random_func.hpp"
20 #include "../core/math_func.hpp"
21 #include "../core/mem_func.hpp"
22 #include "../core/geometry_func.hpp"
23 #include "../fileio_func.h"
24 #include "../framerate_type.h"
25 #include "../window_func.h"
26 #include "sdl2_default_v.h"
29 #include <condition_variable>
31 # include <emscripten.h>
32 # include <emscripten/html5.h>
35 #include "../safeguards.h"
37 static FVideoDriver_SDL_Default iFVideoDriver_SDL_Default
;
39 static SDL_Surface
*_sdl_surface
;
40 static SDL_Surface
*_sdl_rgb_surface
;
41 static SDL_Surface
*_sdl_real_surface
;
42 static SDL_Palette
*_sdl_palette
;
44 void VideoDriver_SDL_Default::UpdatePalette()
48 for (int i
= 0; i
!= this->local_palette
.count_dirty
; i
++) {
49 pal
[i
].r
= this->local_palette
.palette
[this->local_palette
.first_dirty
+ i
].r
;
50 pal
[i
].g
= this->local_palette
.palette
[this->local_palette
.first_dirty
+ i
].g
;
51 pal
[i
].b
= this->local_palette
.palette
[this->local_palette
.first_dirty
+ i
].b
;
55 SDL_SetPaletteColors(_sdl_palette
, pal
, this->local_palette
.first_dirty
, this->local_palette
.count_dirty
);
56 SDL_SetSurfacePalette(_sdl_surface
, _sdl_palette
);
59 void VideoDriver_SDL_Default::MakePalette()
61 if (_sdl_palette
== nullptr) {
62 _sdl_palette
= SDL_AllocPalette(256);
63 if (_sdl_palette
== nullptr) UserError("SDL2: Couldn't allocate palette: {}", SDL_GetError());
66 CopyPalette(this->local_palette
, true);
67 this->UpdatePalette();
69 if (_sdl_surface
!= _sdl_real_surface
) {
70 /* When using a shadow surface, also set our palette on the real screen. This lets SDL
71 * allocate as many colors (or approximations) as
72 * possible, instead of using only the default SDL
73 * palette. This allows us to get more colors exactly
74 * right and might allow using better approximations for
77 * Note that colors allocations are tried in-order, so
78 * this favors colors further up into the palette. Also
79 * note that if two colors from the same animation
80 * sequence are approximated using the same color, that
81 * animation will stop working.
83 * Since changing the system palette causes the colours
84 * to change right away, and allocations might
85 * drastically change, we can't use this for animation,
86 * since that could cause weird coloring between the
87 * palette change and the blitting below, so we only set
88 * the real palette during initialisation.
90 SDL_SetSurfacePalette(_sdl_real_surface
, _sdl_palette
);
94 void VideoDriver_SDL_Default::Paint()
96 PerformanceMeasurer
framerate(PFE_VIDEO
);
98 if (IsEmptyRect(this->dirty_rect
) && this->local_palette
.count_dirty
== 0) return;
100 if (this->local_palette
.count_dirty
!= 0) {
101 Blitter
*blitter
= BlitterFactory::GetCurrentBlitter();
103 switch (blitter
->UsePaletteAnimation()) {
104 case Blitter::PALETTE_ANIMATION_VIDEO_BACKEND
:
105 this->UpdatePalette();
108 case Blitter::PALETTE_ANIMATION_BLITTER
: {
109 blitter
->PaletteAnimate(this->local_palette
);
113 case Blitter::PALETTE_ANIMATION_NONE
:
119 this->local_palette
.count_dirty
= 0;
122 SDL_Rect r
= { this->dirty_rect
.left
, this->dirty_rect
.top
, this->dirty_rect
.right
- this->dirty_rect
.left
, this->dirty_rect
.bottom
- this->dirty_rect
.top
};
124 if (_sdl_surface
!= _sdl_real_surface
) {
125 SDL_BlitSurface(_sdl_surface
, &r
, _sdl_real_surface
, &r
);
127 SDL_UpdateWindowSurfaceRects(this->sdl_window
, &r
, 1);
129 this->dirty_rect
= {};
132 bool VideoDriver_SDL_Default::AllocateBackingStore(int w
, int h
, bool force
)
134 int bpp
= BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
136 _sdl_real_surface
= SDL_GetWindowSurface(this->sdl_window
);
137 if (_sdl_real_surface
== nullptr) UserError("SDL2: Couldn't get window surface: {}", SDL_GetError());
139 if (!force
&& w
== _sdl_real_surface
->w
&& h
== _sdl_real_surface
->h
) return false;
141 /* Free any previously allocated rgb surface. */
142 if (_sdl_rgb_surface
!= nullptr) {
143 SDL_FreeSurface(_sdl_rgb_surface
);
144 _sdl_rgb_surface
= nullptr;
148 _sdl_rgb_surface
= SDL_CreateRGBSurface(0, w
, h
, 8, 0, 0, 0, 0);
149 if (_sdl_rgb_surface
== nullptr) UserError("SDL2: Couldn't allocate shadow surface: {}", SDL_GetError());
151 _sdl_surface
= _sdl_rgb_surface
;
153 _sdl_surface
= _sdl_real_surface
;
156 /* X11 doesn't appreciate it if we invalidate areas outside the window
157 * if shared memory is enabled (read: it crashes). So, as we might have
158 * gotten smaller, reset our dirty rects. GameSizeChanged() a bit lower
159 * will mark the whole screen dirty again anyway, but this time with the
161 this->dirty_rect
= {};
163 _screen
.width
= _sdl_surface
->w
;
164 _screen
.height
= _sdl_surface
->h
;
165 _screen
.pitch
= _sdl_surface
->pitch
/ (bpp
/ 8);
166 _screen
.dst_ptr
= this->GetVideoPointer();
173 void *VideoDriver_SDL_Default::GetVideoPointer()
175 return _sdl_surface
->pixels
;