Add an enum with all available blitters
[openttd/fttd.git] / src / blitter / blitter.cpp
blob2114f44ff7bfa5828276ba24d49547fae478d050
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 */
8 /** @file blitter.cpp Common blitter code. */
10 #include "../stdafx.h"
12 #include <bitset>
14 #include "../debug.h"
15 #include "../string.h"
16 #include "blitter.h"
17 #include "../core/math_func.hpp"
19 #include "null.hpp"
21 #ifndef DEDICATED
22 #include "8bpp_simple.hpp"
23 #include "8bpp_optimized.hpp"
24 #include "32bpp_simple.hpp"
25 #include "32bpp_optimized.hpp"
26 #include "32bpp_anim.hpp"
27 #ifdef WITH_SSE
28 #include "32bpp_sse2.hpp"
29 #include "32bpp_ssse3.hpp"
30 #include "32bpp_sse4.hpp"
31 #include "32bpp_anim_sse4.hpp"
32 #endif
33 #endif
35 #ifdef DEDICATED
36 #define IF_BLITTER_GUI(...)
37 #else
38 #define IF_BLITTER_GUI(...) __VA_ARGS__
39 #endif
41 #ifdef WITH_SSE
42 #define IF_BLITTER_SSE IF_BLITTER_GUI
43 #else
44 #define IF_BLITTER_SSE(...)
45 #endif
47 #define BLITTER_LIST(p) p(Blitter_Null), \
48 IF_BLITTER_GUI (p(Blitter_8bppSimple),) \
49 IF_BLITTER_GUI (p(Blitter_8bppOptimized),) \
50 IF_BLITTER_GUI (p(Blitter_32bppSimple),) \
51 IF_BLITTER_GUI (p(Blitter_32bppOptimized),) \
52 IF_BLITTER_GUI (p(Blitter_32bppAnim),) \
53 IF_BLITTER_SSE (p(Blitter_32bppSSE2),) \
54 IF_BLITTER_SSE (p(Blitter_32bppSSSE3),) \
55 IF_BLITTER_SSE (p(Blitter_32bppSSE4),) \
56 IF_BLITTER_SSE (p(Blitter_32bppSSE4_Anim),)
58 /** List of blitters. */
59 enum Blitters {
60 #define BLITTER(B) BLITTER_##B
61 BLITTER_LIST(BLITTER)
62 #undef BLITTER
65 /** Static blitter data. */
66 static const Blitter::Info blitter_data[] = {
67 #define BLITTER(B) { B::name, B::desc, &B::usable, &B::create, &B::Encode, B::screen_depth, B::palette_animation }
68 BLITTER_LIST(BLITTER)
69 #undef BLITTER
72 /** Usable blitter set type. */
73 typedef std::bitset <lengthof(blitter_data)> BlitterSet;
75 /** Blitter usability test function. */
76 static BlitterSet get_usable_blitters (void)
78 BlitterSet set;
80 for (uint i = 0; i < set.size(); i++) {
81 const Blitter::Info *data = &blitter_data[i];
82 bool usable = data->usable();
83 set.set (i, usable);
84 DEBUG(driver, 1, "Blitter %s%s registered", data->name,
85 usable ? "" : " not");
88 return set;
91 /** Set of usable blitters. */
92 static const BlitterSet usable_blitters (get_usable_blitters());
95 /** The blitter as stored in the configuration file. */
96 char *Blitter::ini;
98 /** Current blitter info. */
99 const Blitter::Info *current_blitter;
101 /** Whether the current blitter was autodetected or specified by the user. */
102 bool Blitter::autodetected;
105 * Get the blitter data with the given name.
106 * @param name The blitter to select.
107 * @return The blitter data, or NULL when there isn't one with the wanted name.
109 const Blitter::Info *Blitter::find (const char *name)
111 for (uint i = 0; i < lengthof(blitter_data); i++) {
112 if (usable_blitters.test (i)) {
113 const Blitter::Info *data = &blitter_data[i];
114 if (strcasecmp (name, data->name) == 0) return data;
118 return NULL;
122 * Find a replacement blitter given some requirements.
123 * @param anim Whether animation is wanted.
124 * @param base_32bpp Whether the baseset requires 32 bpp.
125 * @param grf_32bpp Whether a NewGRF requires 32 bpp.
126 * @return A suitable replacement blitter.
128 const Blitter::Info *Blitter::choose (bool anim, bool base_32bpp, bool grf_32bpp)
130 #ifdef DEDICATED
131 return &blitter_data[BLITTER_Blitter_Null];
132 #else
133 static const struct {
134 byte blitter; ///< Blitter index into blitter_data.
135 byte animation; ///< 0: no support, 1: do support, 2: both
136 byte base_depth; ///< 0: 8bpp, 1: 32bpp, 2: both
137 byte grf_depth; ///< 0: 8bpp, 1: 32bpp, 2: both
138 } replacement_blitters[] = {
139 #ifdef WITH_SSE
140 { BLITTER_Blitter_32bppSSE4, 0, 1, 2 },
141 { BLITTER_Blitter_32bppSSSE3, 0, 1, 2 },
142 { BLITTER_Blitter_32bppSSE2, 0, 1, 2 },
143 { BLITTER_Blitter_32bppSSE4_Anim, 1, 1, 2 },
144 #endif
145 { BLITTER_Blitter_8bppOptimized, 2, 0, 0 },
146 { BLITTER_Blitter_32bppOptimized, 0, 2, 2 },
147 { BLITTER_Blitter_32bppAnim, 1, 2, 2 },
150 for (uint i = 0; ; i++) {
151 /* One of the last two blitters should always match. */
152 assert (i < lengthof(replacement_blitters));
154 if (replacement_blitters[i].animation == (anim ? 0 : 1)) continue;
155 if (replacement_blitters[i].base_depth == (base_32bpp ? 0 : 1)) continue;
156 if (replacement_blitters[i].grf_depth == (grf_32bpp ? 0 : 1)) continue;
158 return &blitter_data[replacement_blitters[i].blitter];
160 #endif
164 * Make the given blitter current.
165 * @param blitter Blitter to set.
166 * @post Sets the blitter so Blitter::get() returns it too.
168 void Blitter::select (const Blitter::Info *blitter)
170 current_blitter = blitter;
171 DEBUG(driver, 1, "Successfully loaded blitter %s", blitter->name);
175 * Fill a buffer with information about the blitters.
176 * @param buf The buffer to fill.
178 void Blitter::list (stringb *buf)
180 buf->append ("List of blitters:\n");
181 for (uint i = 0; i < lengthof(blitter_data); i++) {
182 if (usable_blitters.test (i)) {
183 const Blitter::Info *data = &blitter_data[i];
184 buf->append_fmt ("%18s: %s\n", data->name, data->desc);
187 buf->append ('\n');
191 bool Blitter::Surface::palette_animate (const Palette &palette)
193 /* The null driver does not need to animate anything, for the 8bpp
194 * blitters the video backend takes care of the palette animation
195 * and 32bpp blitters do not have palette animation by default,
196 * so this provides a suitable default for most blitters. */
197 return false;
200 void Blitter::Surface::draw_line (void *video, int x, int y, int x2, int y2, int screen_width, int screen_height, uint8 colour, int width, int dash)
202 int dy;
203 int dx;
204 int stepx;
205 int stepy;
207 dy = (y2 - y) * 2;
208 if (dy < 0) {
209 dy = -dy;
210 stepy = -1;
211 } else {
212 stepy = 1;
215 dx = (x2 - x) * 2;
216 if (dx < 0) {
217 dx = -dx;
218 stepx = -1;
219 } else {
220 stepx = 1;
223 if (dx == 0 && dy == 0) {
224 /* The algorithm below cannot handle this special case; make it work at least for line width 1 */
225 if (x >= 0 && x < screen_width && y >= 0 && y < screen_height) this->set_pixel (video, x, y, colour);
226 return;
229 int frac_diff = width * max(dx, dy);
230 if (width > 1) {
231 /* compute frac_diff = width * sqrt(dx*dx + dy*dy)
232 * Start interval:
233 * max(dx, dy) <= sqrt(dx*dx + dy*dy) <= sqrt(2) * max(dx, dy) <= 3/2 * max(dx, dy) */
234 int frac_sq = width * width * (dx * dx + dy * dy);
235 int frac_max = 3 * frac_diff / 2;
236 while (frac_diff < frac_max) {
237 int frac_test = (frac_diff + frac_max) / 2;
238 if (frac_test * frac_test < frac_sq) {
239 frac_diff = frac_test + 1;
240 } else {
241 frac_max = frac_test - 1;
246 int gap = dash;
247 if (dash == 0) dash = 1;
248 int dash_count = 0;
249 if (dx > dy) {
250 int y_low = y;
251 int y_high = y;
252 int frac_low = dy - frac_diff / 2;
253 int frac_high = dy + frac_diff / 2;
255 while (frac_low + dx / 2 < 0) {
256 frac_low += dx;
257 y_low -= stepy;
259 while (frac_high - dx / 2 >= 0) {
260 frac_high -= dx;
261 y_high += stepy;
263 x2 += stepx;
265 while (x != x2) {
266 if (dash_count < dash && x >= 0 && x < screen_width) {
267 for (int y = y_low; y != y_high; y += stepy) {
268 if (y >= 0 && y < screen_height) this->set_pixel (video, x, y, colour);
271 if (frac_low >= 0) {
272 y_low += stepy;
273 frac_low -= dx;
275 if (frac_high >= 0) {
276 y_high += stepy;
277 frac_high -= dx;
279 x += stepx;
280 frac_low += dy;
281 frac_high += dy;
282 if (++dash_count >= dash + gap) dash_count = 0;
284 } else {
285 int x_low = x;
286 int x_high = x;
287 int frac_low = dx - frac_diff / 2;
288 int frac_high = dx + frac_diff / 2;
290 while (frac_low + dy / 2 < 0) {
291 frac_low += dy;
292 x_low -= stepx;
294 while (frac_high - dy / 2 >= 0) {
295 frac_high -= dy;
296 x_high += stepx;
298 y2 += stepy;
300 while (y != y2) {
301 if (dash_count < dash && y >= 0 && y < screen_height) {
302 for (int x = x_low; x != x_high; x += stepx) {
303 if (x >= 0 && x < screen_width) this->set_pixel (video, x, y, colour);
306 if (frac_low >= 0) {
307 x_low += stepx;
308 frac_low -= dy;
310 if (frac_high >= 0) {
311 x_high += stepx;
312 frac_high -= dy;
314 y += stepy;
315 frac_low += dx;
316 frac_high += dx;
317 if (++dash_count >= dash + gap) dash_count = 0;