OpenTTD Source: src/blitter/32bpp_optimized.cpp Source File

OpenTTD Source 15.0-RC2

32bpp_optimized.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 "../zoom_func.h"
12#include "../settings_type.h"
13#include "../palette_func.h"
14#include "32bpp_optimized.hpp"
15
16#include "../safeguards.h"
17
19 static FBlitter_32bppOptimized iFBlitter_32bppOptimized;
20
28template <BlitterMode mode, bool Tpal_to_rgb>
29 inline void Blitter_32bppOptimized::Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom)
30{
31 const SpriteData *src = (const SpriteData *)bp->sprite;
32
33 /* src_px : each line begins with uint32_t n = 'number of bytes in this line',
34 * then n times is the Colour struct for this line */
35 const Colour *src_px = reinterpret_cast<const Colour *>(src->data + src->offset[0][zoom]);
36 /* src_n : each line begins with uint32_t n = 'number of bytes in this line',
37 * then interleaved stream of 'm' and 'n' channels. 'm' is remap,
38 * 'n' is number of bytes with the same alpha channel class */
39 const uint16_t *src_n = reinterpret_cast<const uint16_t *>(src->data + src->offset[1][zoom]);
40
41 /* skip upper lines in src_px and src_n */
42 for (uint i = bp->skip_top; i != 0; i--) {
43 src_px = (const Colour *)((const uint8_t *)src_px + *(const uint32_t *)src_px);
44 src_n = (const uint16_t *)((const uint8_t *)src_n + *(const uint32_t *)src_n);
45 }
46
47 /* skip lines in dst */
48 Colour *dst = (Colour *)bp->dst + bp->top * bp->pitch + bp->left;
49
50 /* store so we don't have to access it via bp every time (compiler assumes pointer aliasing) */
51 const uint8_t *remap = bp->remap;
52
53 for (int y = 0; y < bp->height; y++) {
54 /* next dst line begins here */
55 Colour *dst_ln = dst + bp->pitch;
56
57 /* next src line begins here */
58 const Colour *src_px_ln = (const Colour *)((const uint8_t *)src_px + *(const uint32_t *)src_px);
59 src_px++;
60
61 /* next src_n line begins here */
62 const uint16_t *src_n_ln = (const uint16_t *)((const uint8_t *)src_n + *(const uint32_t *)src_n);
63 src_n += 2;
64
65 /* we will end this line when we reach this point */
66 Colour *dst_end = dst + bp->skip_left;
67
68 /* number of pixels with the same alpha channel class */
69 uint n;
70
71 while (dst < dst_end) {
72 n = *src_n++;
73
74 if (src_px->a == 0) {
75 dst += n;
76 src_px ++;
77 src_n++;
78 } else {
79 if (dst + n > dst_end) {
80 uint d = dst_end - dst;
81 src_px += d;
82 src_n += d;
83
84 dst = dst_end - bp->skip_left;
85 dst_end = dst + bp->width;
86
87 n = std::min(n - d, (uint)bp->width);
88 goto draw;
89 }
90 dst += n;
91 src_px += n;
92 src_n += n;
93 }
94 }
95
96 dst -= bp->skip_left;
97 dst_end -= bp->skip_left;
98
99 dst_end += bp->width;
100
101 while (dst < dst_end) {
102 n = std::min<uint>(*src_n++, dst_end - dst);
103
104 if (src_px->a == 0) {
105 dst += n;
106 src_px++;
107 src_n++;
108 continue;
109 }
110
111 draw:;
112
113 switch (mode) {
114 case BlitterMode::ColourRemap:
115 if (src_px->a == 255) {
116 do {
117 uint m = *src_n;
118 /* In case the m-channel is zero, do not remap this pixel in any way */
119 if (m == 0) {
120 *dst = src_px->data;
121 } else {
122 uint r = remap[GB(m, 0, 8)];
123 if (r != 0) *dst = AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8));
124 }
125 dst++;
126 src_px++;
127 src_n++;
128 } while (--n != 0);
129 } else {
130 do {
131 uint m = *src_n;
132 if (m == 0) {
133 *dst = ComposeColourRGBANoCheck(src_px->r, src_px->g, src_px->b, src_px->a, *dst);
134 } else {
135 uint r = remap[GB(m, 0, 8)];
136 if (r != 0) *dst = ComposeColourPANoCheck(AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8)), src_px->a, *dst);
137 }
138 dst++;
139 src_px++;
140 src_n++;
141 } while (--n != 0);
142 }
143 break;
144
145 case BlitterMode::CrashRemap:
146 if (src_px->a == 255) {
147 do {
148 uint m = *src_n;
149 if (m == 0) {
150 uint8_t g = MakeDark(src_px->r, src_px->g, src_px->b);
151 *dst = ComposeColourRGBA(g, g, g, src_px->a, *dst);
152 } else {
153 uint r = remap[GB(m, 0, 8)];
154 if (r != 0) *dst = AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8));
155 }
156 dst++;
157 src_px++;
158 src_n++;
159 } while (--n != 0);
160 } else {
161 do {
162 uint m = *src_n;
163 if (m == 0) {
164 if (src_px->a != 0) {
165 uint8_t g = MakeDark(src_px->r, src_px->g, src_px->b);
166 *dst = ComposeColourRGBA(g, g, g, src_px->a, *dst);
167 }
168 } else {
169 uint r = remap[GB(m, 0, 8)];
170 if (r != 0) *dst = ComposeColourPANoCheck(AdjustBrightness(this->LookupColourInPalette(r), GB(m, 8, 8)), src_px->a, *dst);
171 }
172 dst++;
173 src_px++;
174 src_n++;
175 } while (--n != 0);
176 }
177 break;
178
179 case BlitterMode::BlackRemap:
180 do {
181 *dst = Colour(0, 0, 0);
182 dst++;
183 src_px++;
184 src_n++;
185 } while (--n != 0);
186 break;
187
188 case BlitterMode::Transparent:
189 /* Make the current colour a bit more black, so it looks like this image is transparent */
190 src_n += n;
191 if (src_px->a == 255) {
192 src_px += n;
193 do {
194 *dst = MakeTransparent(*dst, 3, 4);
195 dst++;
196 } while (--n != 0);
197 } else {
198 do {
199 *dst = MakeTransparent(*dst, (256 * 4 - src_px->a), 256 * 4);
200 dst++;
201 src_px++;
202 } while (--n != 0);
203 }
204 break;
205
206 case BlitterMode::TransparentRemap:
207 /* Apply custom transparency remap. */
208 src_n += n;
209 if (src_px->a != 0) {
210 src_px += n;
211 do {
212 *dst = this->LookupColourInPalette(remap[GetNearestColourIndex(*dst)]);
213 dst++;
214 } while (--n != 0);
215 } else {
216 dst += n;
217 src_px += n;
218 }
219 break;
220
221 default:
222 if (src_px->a == 255) {
223 /* faster than memcpy(), n is usually low */
224 do {
225 if (Tpal_to_rgb && *src_n != 0) {
226 /* Convert the mapping channel to a RGB value */
227 *dst = AdjustBrightness(this->LookupColourInPalette(GB(*src_n, 0, 8)), GB(*src_n, 8, 8)).data;
228 } else {
229 *dst = src_px->data;
230 }
231 dst++;
232 src_px++;
233 src_n++;
234 } while (--n != 0);
235 } else {
236 do {
237 if (Tpal_to_rgb && *src_n != 0) {
238 /* Convert the mapping channel to a RGB value */
239 Colour colour = AdjustBrightness(this->LookupColourInPalette(GB(*src_n, 0, 8)), GB(*src_n, 8, 8));
240 *dst = ComposeColourRGBANoCheck(colour.r, colour.g, colour.b, src_px->a, *dst);
241 } else {
242 *dst = ComposeColourRGBANoCheck(src_px->r, src_px->g, src_px->b, src_px->a, *dst);
243 }
244 dst++;
245 src_px++;
246 src_n++;
247 } while (--n != 0);
248 }
249 break;
250 }
251 }
252
253 dst = dst_ln;
254 src_px = src_px_ln;
255 src_n = src_n_ln;
256 }
257}
258
259template <bool Tpal_to_rgb>
260 void Blitter_32bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom)
261{
262 switch (mode) {
263 default: NOT_REACHED();
264 case BlitterMode::Normal: Draw<BlitterMode::Normal, Tpal_to_rgb>(bp, zoom); return;
265 case BlitterMode::ColourRemap: Draw<BlitterMode::ColourRemap, Tpal_to_rgb>(bp, zoom); return;
266 case BlitterMode::Transparent: Draw<BlitterMode::Transparent, Tpal_to_rgb>(bp, zoom); return;
267 case BlitterMode::TransparentRemap: Draw<BlitterMode::TransparentRemap, Tpal_to_rgb>(bp, zoom); return;
268 case BlitterMode::CrashRemap: Draw<BlitterMode::CrashRemap, Tpal_to_rgb>(bp, zoom); return;
269 case BlitterMode::BlackRemap: Draw<BlitterMode::BlackRemap, Tpal_to_rgb>(bp, zoom); return;
270 }
271}
272
273template void Blitter_32bppOptimized::Draw<true>(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom);
274template void Blitter_32bppOptimized::Draw<false>(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom);
275
283 void Blitter_32bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom)
284{
285 this->Draw<false>(bp, mode, zoom);
286}
287
288template <bool Tpal_to_rgb>
289 Sprite *Blitter_32bppOptimized::EncodeInternal(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator)
290{
291 /* streams of pixels (a, r, g, b channels)
292 *
293 * stored in separated stream so data are always aligned on 4B boundary */
294 SpriteCollMap<std::unique_ptr<Colour[]>> dst_px_orig;
295
296 /* interleaved stream of 'm' channel and 'n' channel
297 * 'n' is number of following pixels with the same alpha channel class
298 * there are 3 classes: 0, 255, others
299 *
300 * it has to be stored in one stream so fewer registers are used -
301 * x86 has problems with register allocation even with this solution */
302 SpriteCollMap<std::unique_ptr<uint16_t[]>> dst_n_orig;
303
304 /* lengths of streams */
305 SpriteCollMap<uint32_t> lengths[2];
306
307 ZoomLevel zoom_min;
308 ZoomLevel zoom_max;
309
310 if (sprite_type == SpriteType::Font) {
311 zoom_min = ZoomLevel::Min;
312 zoom_max = ZoomLevel::Min;
313 } else {
314 zoom_min = _settings_client.gui.zoom_min;
315 zoom_max = _settings_client.gui.zoom_max;
316 if (zoom_max == zoom_min) zoom_max = ZoomLevel::Max;
317 }
318
319 for (ZoomLevel z = zoom_min; z <= zoom_max; z++) {
320 const SpriteLoader::Sprite *src_orig = &sprite[z];
321
322 uint size = src_orig->height * src_orig->width;
323
324 dst_px_orig[z] = std::make_unique<Colour[]>(size + src_orig->height * 2);
325 dst_n_orig[z] = std::make_unique<uint16_t[]>(size * 2 + src_orig->height * 4 * 2);
326
327 uint32_t *dst_px_ln = reinterpret_cast<uint32_t *>(dst_px_orig[z].get());
328 uint32_t *dst_n_ln = reinterpret_cast<uint32_t *>(dst_n_orig[z].get());
329
330 const SpriteLoader::CommonPixel *src = (const SpriteLoader::CommonPixel *)src_orig->data;
331
332 for (uint y = src_orig->height; y > 0; y--) {
333 /* Index 0 of dst_px and dst_n is left as space to save the length of the row to be filled later. */
334 Colour *dst_px = (Colour *)&dst_px_ln[1];
335 uint16_t *dst_n = (uint16_t *)&dst_n_ln[1];
336
337 uint16_t *dst_len = dst_n++;
338
339 uint last = 3;
340 int len = 0;
341
342 for (uint x = src_orig->width; x > 0; x--) {
343 uint8_t a = src->a;
344 uint t = a > 0 && a < 255 ? 1 : a;
345
346 if (last != t || len == 65535) {
347 if (last != 3) {
348 *dst_len = len;
349 dst_len = dst_n++;
350 }
351 len = 0;
352 }
353
354 last = t;
355 len++;
356
357 if (a != 0) {
358 dst_px->a = a;
359 *dst_n = src->m;
360 if (src->m != 0) {
361 /* Get brightest value */
362 uint8_t rgb_max = std::max({ src->r, src->g, src->b });
363
364 /* Black pixel (8bpp or old 32bpp image), so use default value */
365 if (rgb_max == 0) rgb_max = DEFAULT_BRIGHTNESS;
366 *dst_n |= rgb_max << 8;
367
368 if (Tpal_to_rgb) {
369 /* Pre-convert the mapping channel to a RGB value */
370 Colour colour = AdjustBrightness(this->LookupColourInPalette(src->m), rgb_max);
371 dst_px->r = colour.r;
372 dst_px->g = colour.g;
373 dst_px->b = colour.b;
374 } else {
375 dst_px->r = src->r;
376 dst_px->g = src->g;
377 dst_px->b = src->b;
378 }
379 } else {
380 dst_px->r = src->r;
381 dst_px->g = src->g;
382 dst_px->b = src->b;
383 }
384 dst_px++;
385 dst_n++;
386 } else if (len == 1) {
387 dst_px++;
388 *dst_n = src->m;
389 dst_n++;
390 }
391
392 src++;
393 }
394
395 if (last != 3) {
396 *dst_len = len;
397 }
398
399 dst_px = (Colour *)AlignPtr(dst_px, 4);
400 dst_n = (uint16_t *)AlignPtr(dst_n, 4);
401
402 *dst_px_ln = (uint8_t *)dst_px - (uint8_t *)dst_px_ln;
403 *dst_n_ln = (uint8_t *)dst_n - (uint8_t *)dst_n_ln;
404
405 dst_px_ln = (uint32_t *)dst_px;
406 dst_n_ln = (uint32_t *)dst_n;
407 }
408
409 lengths[0][z] = reinterpret_cast<uint8_t *>(dst_px_ln) - reinterpret_cast<uint8_t *>(dst_px_orig[z].get()); // all are aligned to 4B boundary
410 lengths[1][z] = reinterpret_cast<uint8_t *>(dst_n_ln) - reinterpret_cast<uint8_t *>(dst_n_orig[z].get());
411 }
412
413 uint len = 0; // total length of data
414 for (ZoomLevel z = zoom_min; z <= zoom_max; z++) {
415 len += lengths[0][z] + lengths[1][z];
416 }
417
418 Sprite *dest_sprite = allocator.Allocate<Sprite>(sizeof(*dest_sprite) + sizeof(SpriteData) + len);
419
420 const auto &root_sprite = sprite.Root();
421 dest_sprite->height = root_sprite.height;
422 dest_sprite->width = root_sprite.width;
423 dest_sprite->x_offs = root_sprite.x_offs;
424 dest_sprite->y_offs = root_sprite.y_offs;
425
426 SpriteData *dst = (SpriteData *)dest_sprite->data;
427
428 uint32_t offset = 0;
429 for (ZoomLevel z = zoom_min; z <= zoom_max; z++) {
430 dst->offset[0][z] = offset;
431 offset += lengths[0][z];
432 dst->offset[1][z] = offset;
433 offset += lengths[1][z];
434
435 std::copy_n(reinterpret_cast<uint8_t *>(dst_px_orig[z].get()), lengths[0][z], dst->data + dst->offset[0][z]);
436 std::copy_n(reinterpret_cast<uint8_t *>(dst_n_orig[z].get()), lengths[1][z], dst->data + dst->offset[1][z]);
437 }
438
439 return dest_sprite;
440}
441
442template Sprite *Blitter_32bppOptimized::EncodeInternal<true>(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator);
443template Sprite *Blitter_32bppOptimized::EncodeInternal<false>(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator);
444
445 Sprite *Blitter_32bppOptimized::Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator)
446{
447 return this->EncodeInternal<true>(sprite_type, sprite, allocator);
448}
iFBlitter_32bppOptimized
static FBlitter_32bppOptimized iFBlitter_32bppOptimized
Instantiation of the optimized 32bpp blitter factory.
Definition 32bpp_optimized.cpp:19
32bpp_optimized.hpp
Optimized 32 bpp blitter.
BlitterMode
BlitterMode
The modes of blitting we can do.
Definition base.hpp:17
BlitterMode::Transparent
@ Transparent
Perform transparency darkening remapping.
BlitterMode::CrashRemap
@ CrashRemap
Perform a crash remapping.
BlitterMode::BlackRemap
@ BlackRemap
Perform remapping to a completely blackened sprite.
BlitterMode::Normal
@ Normal
Perform the simple blitting.
BlitterMode::TransparentRemap
@ TransparentRemap
Perform transparency colour remapping.
BlitterMode::ColourRemap
@ ColourRemap
Perform a colour remapping.
GB
static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
Definition bitmath_func.hpp:32
Blitter_32bppBase::ComposeColourRGBANoCheck
static Colour ComposeColourRGBANoCheck(uint r, uint g, uint b, uint a, Colour current)
Compose a colour based on RGBA values and the current pixel value.
Definition 32bpp_base.hpp:44
Blitter_32bppBase::ComposeColourPANoCheck
static Colour ComposeColourPANoCheck(Colour colour, uint a, Colour current)
Compose a colour based on Pixel value, alpha value, and the current pixel value.
Definition 32bpp_base.hpp:72
Blitter_32bppBase::LookupColourInPalette
static Colour LookupColourInPalette(uint index)
Look up the colour in the current palette.
Definition 32bpp_base.hpp:36
Blitter_32bppBase::MakeTransparent
static Colour MakeTransparent(Colour colour, uint nom, uint denom=256)
Make a pixel looks like it is transparent.
Definition 32bpp_base.hpp:103
Blitter_32bppBase::MakeDark
static uint8_t MakeDark(uint8_t r, uint8_t g, uint8_t b)
Make a colour dark grey, for specialized 32bpp remapping.
Definition 32bpp_base.hpp:119
Blitter_32bppBase::ComposeColourRGBA
static Colour ComposeColourRGBA(uint r, uint g, uint b, uint a, Colour current)
Compose a colour based on RGBA values and the current pixel value.
Definition 32bpp_base.hpp:61
Blitter_32bppOptimized::Encode
Sprite * Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override
Convert a sprite from the loader to our own format.
Definition 32bpp_optimized.cpp:445
Blitter_32bppOptimized::Draw
void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override
Draws a sprite to a (screen) buffer.
Definition 32bpp_optimized.cpp:283
FBlitter_32bppOptimized
Factory for the optimised 32 bpp blitter (without palette animation).
Definition 32bpp_optimized.hpp:37
SpriteAllocator
Interface for something that can allocate memory for a sprite.
Definition spriteloader.hpp:104
SpriteAllocator::Allocate
T * Allocate(size_t size)
Allocate memory for a sprite.
Definition spriteloader.hpp:115
SpriteCollMap
Map zoom level to data.
Definition spriteloader.hpp:34
SpriteType
SpriteType
Types of sprites that might be loaded.
Definition gfx_type.h:357
SpriteType::Font
@ Font
A sprite used for fonts.
AlignPtr
constexpr T * AlignPtr(T *x, uint n)
Return the smallest multiple of n equal or greater than x Applies to pointers only.
Definition math_func.hpp:55
GetNearestColourIndex
uint8_t GetNearestColourIndex(uint8_t r, uint8_t g, uint8_t b)
Get nearest colour palette index from an RGB colour.
Definition palette.cpp:151
_settings_client
ClientSettings _settings_client
The current settings for this game.
Definition settings.cpp:60
Blitter::BlitterParams
Parameters related to blitting.
Definition base.hpp:32
Blitter::BlitterParams::skip_top
int skip_top
How much pixels of the source to skip on the top (based on zoom of dst)
Definition base.hpp:37
Blitter::BlitterParams::dst
void * dst
Destination buffer.
Definition base.hpp:45
Blitter::BlitterParams::left
int left
The left offset in the 'dst' in pixels to start drawing.
Definition base.hpp:42
Blitter::BlitterParams::pitch
int pitch
The pitch of the destination buffer.
Definition base.hpp:46
Blitter::BlitterParams::skip_left
int skip_left
How much pixels of the source to skip on the left (based on zoom of dst)
Definition base.hpp:36
Blitter::BlitterParams::height
int height
The height in pixels that needs to be drawn to dst.
Definition base.hpp:39
Blitter::BlitterParams::remap
const uint8_t * remap
XXX – Temporary storage for remap array.
Definition base.hpp:34
Blitter::BlitterParams::width
int width
The width in pixels that needs to be drawn to dst.
Definition base.hpp:38
Blitter::BlitterParams::sprite
const void * sprite
Pointer to the sprite how ever the encoder stored it.
Definition base.hpp:33
Blitter::BlitterParams::top
int top
The top offset in the 'dst' in pixels to start drawing.
Definition base.hpp:43
Blitter_32bppOptimized::SpriteData
Data stored about a (single) sprite.
Definition 32bpp_optimized.hpp:19
Blitter_32bppOptimized::SpriteData::data
uint8_t data[]
Data, all zoomlevels.
Definition 32bpp_optimized.hpp:21
Blitter_32bppOptimized::SpriteData::offset
SpriteCollMap< uint32_t > offset[2]
Offsets (from .data) to streams for different zoom levels, and the normal and remap image information...
Definition 32bpp_optimized.hpp:20
ClientSettings::gui
GUISettings gui
settings related to the GUI
Definition settings_type.h:650
GUISettings::zoom_min
ZoomLevel zoom_min
minimum zoom out level
Definition settings_type.h:191
GUISettings::zoom_max
ZoomLevel zoom_max
maximum zoom out level
Definition settings_type.h:192
SpriteLoader::CommonPixel
Definition of a common pixel in OpenTTD's realm.
Definition spriteloader.hpp:48
SpriteLoader::Sprite
Structure for passing information from the sprite loader to the blitter.
Definition spriteloader.hpp:62
SpriteLoader::Sprite::width
uint16_t width
Width of the sprite.
Definition spriteloader.hpp:64
SpriteLoader::Sprite::data
SpriteLoader::CommonPixel * data
The sprite itself.
Definition spriteloader.hpp:68
SpriteLoader::Sprite::height
uint16_t height
Height of the sprite.
Definition spriteloader.hpp:63
Sprite
Data structure describing a sprite.
Definition spritecache_type.h:16
ZoomLevel
ZoomLevel
All zoom levels we know.
Definition zoom_type.h:20
ZoomLevel::Max
@ Max
Maximum zoom level.
ZoomLevel::Min
@ Min
Minimum zoom level.