OpenTTD Source 20251213-master-g1091fa6071
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 <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
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/geometry_func.hpp"
20#include "../fileio_func.h"
21#include "../framerate_type.h"
22#include "../window_func.h"
23#include "sdl2_default_v.h"
24#include <SDL.h>
25#include <mutex>
26#include <condition_variable>
27#ifdef __EMSCRIPTEN__
28# include <emscripten.h>
29# include <emscripten/html5.h>
30#endif
31
32#include "../safeguards.h"
33
34static FVideoDriver_SDL_Default iFVideoDriver_SDL_Default;
35
36static SDL_Surface *_sdl_surface;
37static SDL_Surface *_sdl_rgb_surface;
38static SDL_Surface *_sdl_real_surface;
39static SDL_Palette *_sdl_palette;
40
41void VideoDriver_SDL_Default::UpdatePalette()
42{
43 SDL_Color pal[256];
44
45 for (int i = 0; i != this->local_palette.count_dirty; i++) {
46 pal[i].r = this->local_palette.palette[this->local_palette.first_dirty + i].r;
47 pal[i].g = this->local_palette.palette[this->local_palette.first_dirty + i].g;
48 pal[i].b = this->local_palette.palette[this->local_palette.first_dirty + i].b;
49 pal[i].a = 0;
50 }
51
52 SDL_SetPaletteColors(_sdl_palette, pal, this->local_palette.first_dirty, this->local_palette.count_dirty);
53 SDL_SetSurfacePalette(_sdl_surface, _sdl_palette);
54}
55
56void VideoDriver_SDL_Default::MakePalette()
57{
58 if (_sdl_palette == nullptr) {
59 _sdl_palette = SDL_AllocPalette(256);
60 if (_sdl_palette == nullptr) UserError("SDL2: Couldn't allocate palette: {}", SDL_GetError());
61 }
62
63 CopyPalette(this->local_palette, true);
64 this->UpdatePalette();
65
66 if (_sdl_surface != _sdl_real_surface) {
67 /* When using a shadow surface, also set our palette on the real screen. This lets SDL
68 * allocate as many colours (or approximations) as
69 * possible, instead of using only the default SDL
70 * palette. This allows us to get more colours exactly
71 * right and might allow using better approximations for
72 * other colours.
73 *
74 * Note that colours allocations are tried in-order, so
75 * this favors colours further up into the palette. Also
76 * note that if two colours from the same animation
77 * sequence are approximated using the same colour, that
78 * animation will stop working.
79 *
80 * Since changing the system palette causes the colours
81 * to change right away, and allocations might
82 * drastically change, we can't use this for animation,
83 * since that could cause weird colouring between the
84 * palette change and the blitting below, so we only set
85 * the real palette during initialisation.
86 */
87 SDL_SetSurfacePalette(_sdl_real_surface, _sdl_palette);
88 }
89}
90
92{
94
95 if (IsEmptyRect(this->dirty_rect) && this->local_palette.count_dirty == 0) return;
96
97 if (this->local_palette.count_dirty != 0) {
99
100 switch (blitter->UsePaletteAnimation()) {
102 this->UpdatePalette();
103 break;
104
106 blitter->PaletteAnimate(this->local_palette);
107 break;
108 }
109
111 break;
112
113 default:
114 NOT_REACHED();
115 }
116 this->local_palette.count_dirty = 0;
117 }
118
119 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 };
120
121 if (_sdl_surface != _sdl_real_surface) {
122 SDL_BlitSurface(_sdl_surface, &r, _sdl_real_surface, &r);
123 }
124 SDL_UpdateWindowSurfaceRects(this->sdl_window, &r, 1);
125
126 this->dirty_rect = {};
127}
128
130{
132
133 _sdl_real_surface = SDL_GetWindowSurface(this->sdl_window);
134 if (_sdl_real_surface == nullptr) UserError("SDL2: Couldn't get window surface: {}", SDL_GetError());
135
136 if (!force && w == _sdl_real_surface->w && h == _sdl_real_surface->h) return false;
137
138 /* Free any previously allocated rgb surface. */
139 if (_sdl_rgb_surface != nullptr) {
140 SDL_FreeSurface(_sdl_rgb_surface);
141 _sdl_rgb_surface = nullptr;
142 }
143
144 if (bpp == 8) {
145 _sdl_rgb_surface = SDL_CreateRGBSurface(0, w, h, 8, 0, 0, 0, 0);
146 if (_sdl_rgb_surface == nullptr) UserError("SDL2: Couldn't allocate shadow surface: {}", SDL_GetError());
147
148 _sdl_surface = _sdl_rgb_surface;
149 } else {
150 _sdl_surface = _sdl_real_surface;
151 }
152
153 /* X11 doesn't appreciate it if we invalidate areas outside the window
154 * if shared memory is enabled (read: it crashes). So, as we might have
155 * gotten smaller, reset our dirty rects. GameSizeChanged() a bit lower
156 * will mark the whole screen dirty again anyway, but this time with the
157 * new dimensions. */
158 this->dirty_rect = {};
159
160 _screen.width = _sdl_surface->w;
161 _screen.height = _sdl_surface->h;
162 _screen.pitch = _sdl_surface->pitch / (bpp / 8);
163 _screen.dst_ptr = this->GetVideoPointer();
164
165 this->MakePalette();
166
167 return true;
168}
169
171{
172 return _sdl_surface->pixels;
173}
static Blitter * GetCurrentBlitter()
Get the current active blitter (always set by calling SelectBlitter).
Definition factory.hpp:136
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.
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...
@ None
No palette animation.
@ Blitter
The blitter takes care of the palette animation.
@ VideoBackend
Palette animation should be done by video backend (8bpp only!)
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:225
Default backend of the SDL2 video driver.
int first_dirty
The first dirty element.
Definition gfx_type.h:375
int count_dirty
The number of dirty elements.
Definition gfx_type.h:376
Colour palette[256]
Current palette. Entry 0 has to be always fully transparent!
Definition gfx_type.h:374