OpenTTD Source  20241108-master-g80f628063a
sdl2_default_v.cpp
Go to the documentation of this file.
1 /*
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/>.
6  */
7 
10 #include "../stdafx.h"
11 #include "../openttd.h"
12 #include "../error_func.h"
13 #include "../gfx_func.h"
14 #include "../rev.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"
27 #include <SDL.h>
28 #include <mutex>
29 #include <condition_variable>
30 #ifdef __EMSCRIPTEN__
31 # include <emscripten.h>
32 # include <emscripten/html5.h>
33 #endif
34 
35 #include "../safeguards.h"
36 
37 static FVideoDriver_SDL_Default iFVideoDriver_SDL_Default;
38 
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;
43 
44 void VideoDriver_SDL_Default::UpdatePalette()
45 {
46  SDL_Color pal[256];
47 
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;
52  pal[i].a = 0;
53  }
54 
55  SDL_SetPaletteColors(_sdl_palette, pal, this->local_palette.first_dirty, this->local_palette.count_dirty);
56  SDL_SetSurfacePalette(_sdl_surface, _sdl_palette);
57 }
58 
59 void VideoDriver_SDL_Default::MakePalette()
60 {
61  if (_sdl_palette == nullptr) {
62  _sdl_palette = SDL_AllocPalette(256);
63  if (_sdl_palette == nullptr) UserError("SDL2: Couldn't allocate palette: {}", SDL_GetError());
64  }
65 
66  CopyPalette(this->local_palette, true);
67  this->UpdatePalette();
68 
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
75  * other colors.
76  *
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.
82  *
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.
89  */
90  SDL_SetSurfacePalette(_sdl_real_surface, _sdl_palette);
91  }
92 }
93 
95 {
96  PerformanceMeasurer framerate(PFE_VIDEO);
97 
98  if (IsEmptyRect(this->dirty_rect) && this->local_palette.count_dirty == 0) return;
99 
100  if (this->local_palette.count_dirty != 0) {
102 
103  switch (blitter->UsePaletteAnimation()) {
105  this->UpdatePalette();
106  break;
107 
109  blitter->PaletteAnimate(this->local_palette);
110  break;
111  }
112 
114  break;
115 
116  default:
117  NOT_REACHED();
118  }
119  this->local_palette.count_dirty = 0;
120  }
121 
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 };
123 
124  if (_sdl_surface != _sdl_real_surface) {
125  SDL_BlitSurface(_sdl_surface, &r, _sdl_real_surface, &r);
126  }
127  SDL_UpdateWindowSurfaceRects(this->sdl_window, &r, 1);
128 
129  this->dirty_rect = {};
130 }
131 
132 bool VideoDriver_SDL_Default::AllocateBackingStore(int w, int h, bool force)
133 {
135 
136  _sdl_real_surface = SDL_GetWindowSurface(this->sdl_window);
137  if (_sdl_real_surface == nullptr) UserError("SDL2: Couldn't get window surface: {}", SDL_GetError());
138 
139  if (!force && w == _sdl_real_surface->w && h == _sdl_real_surface->h) return false;
140 
141  /* Free any previously allocated rgb surface. */
142  if (_sdl_rgb_surface != nullptr) {
143  SDL_FreeSurface(_sdl_rgb_surface);
144  _sdl_rgb_surface = nullptr;
145  }
146 
147  if (bpp == 8) {
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());
150 
151  _sdl_surface = _sdl_rgb_surface;
152  } else {
153  _sdl_surface = _sdl_real_surface;
154  }
155 
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
160  * new dimensions. */
161  this->dirty_rect = {};
162 
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();
167 
168  this->MakePalette();
169 
170  return true;
171 }
172 
174 {
175  return _sdl_surface->pixels;
176 }
static Blitter * GetCurrentBlitter()
Get the current active blitter (always set by calling SelectBlitter).
Definition: factory.hpp:138
How all blitters should look like.
Definition: base.hpp:29
virtual uint8_t GetScreenDepth()=0
Get the screen depth this blitter works for.
virtual Blitter::PaletteAnimation UsePaletteAnimation()=0
Check if the blitter uses palette animation at all.
@ PALETTE_ANIMATION_NONE
No palette animation.
Definition: base.hpp:51
@ PALETTE_ANIMATION_VIDEO_BACKEND
Palette animation should be done by video backend (8bpp only!)
Definition: base.hpp:52
@ PALETTE_ANIMATION_BLITTER
The blitter takes care of the palette animation.
Definition: base.hpp:53
virtual void PaletteAnimate(const Palette &palette)=0
Called when the 8bpp palette is changed; you should redraw all pixels on the screen that are equal to...
Factory for the SDL video driver.
RAII class for measuring simple elements of performance.
Palette local_palette
Current palette to use for drawing.
Definition: sdl2_v.h:48
Rect dirty_rect
Rectangle encompassing the dirty area of the video buffer.
Definition: sdl2_v.h:50
struct SDL_Window * sdl_window
Main SDL window.
Definition: sdl2_v.h:47
bool AllocateBackingStore(int w, int h, bool force=false) override
(Re-)create the backing store.
void * GetVideoPointer() override
Get a pointer to the video buffer.
void Paint() override
Paint the window.
@ PFE_VIDEO
Speed of painting drawn video buffer.
bool IsEmptyRect(const Rect &r)
Check if a rectangle is empty.
bool CopyPalette(Palette &local_palette, bool force_copy)
Copy the current palette if the palette was updated.
Definition: palette.cpp:152
Default backend of the SDL2 video driver.
int first_dirty
The first dirty element.
Definition: gfx_type.h:330
int count_dirty
The number of dirty elements.
Definition: gfx_type.h:331
Colour palette[256]
Current palette. Entry 0 has to be always fully transparent!
Definition: gfx_type.h:329