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 40bpp_optimized.cpp Implementation of the optimized 40 bpp blitter. */
10 #include "../stdafx.h"
11 #include "../zoom_func.h"
12 #include "../settings_type.h"
13 #include "../video/video_driver.hpp"
14 #include "../palette_func.h"
15 #include "40bpp_anim.hpp"
18 #include "../table/sprites.h"
20 #include "../safeguards.h"
23 /** Instantiation of the 40bpp with animation blitter factory. */
24 static FBlitter_40bppAnim iFBlitter_40bppAnim
;
26 /** Cached black value. */
27 static const Colour
_black_colour(0, 0, 0);
30 void Blitter_40bppAnim::SetPixel(void *video
, int x
, int y
, uint8_t colour
)
32 if (_screen_disable_anim
) {
33 Blitter_32bppOptimized::SetPixel(video
, x
, y
, colour
);
35 size_t y_offset
= static_cast<size_t>(y
) * _screen
.pitch
;
36 *((Colour
*)video
+ x
+ y_offset
) = _black_colour
;
38 VideoDriver::GetInstance()->GetAnimBuffer()[((uint32_t *)video
- (uint32_t *)_screen
.dst_ptr
) + x
+ y_offset
] = colour
;
42 void Blitter_40bppAnim::DrawRect(void *video
, int width
, int height
, uint8_t colour
)
44 if (_screen_disable_anim
) {
45 /* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent DrawRect() */
46 Blitter_32bppOptimized::DrawRect(video
, width
, height
, colour
);
50 assert(VideoDriver::GetInstance()->GetAnimBuffer() != nullptr);
51 uint8_t *anim_line
= ((uint32_t *)video
- (uint32_t *)_screen
.dst_ptr
) + VideoDriver::GetInstance()->GetAnimBuffer();
54 Colour
*dst
= (Colour
*)video
;
55 uint8_t *anim
= anim_line
;
57 for (int i
= width
; i
> 0; i
--) {
63 video
= (uint32_t *)video
+ _screen
.pitch
;
64 anim_line
+= _screen
.pitch
;
68 void Blitter_40bppAnim::DrawLine(void *video
, int x
, int y
, int x2
, int y2
, int screen_width
, int screen_height
, uint8_t colour
, int width
, int dash
)
70 if (_screen_disable_anim
) {
71 /* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent DrawRect() */
72 Blitter_32bppOptimized::DrawLine(video
, x
, y
, x2
, y2
, screen_width
, screen_height
, colour
, width
, dash
);
76 assert(VideoDriver::GetInstance()->GetAnimBuffer() != nullptr);
77 uint8_t *anim
= ((uint32_t *)video
- (uint32_t *)_screen
.dst_ptr
) + VideoDriver::GetInstance()->GetAnimBuffer();
79 this->DrawLineGeneric(x
, y
, x2
, y2
, screen_width
, screen_height
, width
, dash
, [=](int x
, int y
) {
80 *((Colour
*)video
+ x
+ y
* _screen
.pitch
) = _black_colour
;
81 *(anim
+ x
+ y
* _screen
.pitch
) = colour
;
86 * Draws a sprite to a (screen) buffer. It is templated to allow faster operation.
88 * @tparam mode blitter mode
89 * @param bp further blitting parameters
90 * @param zoom zoom level at which we are drawing
92 template <BlitterMode mode
>
93 inline void Blitter_40bppAnim::Draw(const Blitter::BlitterParams
*bp
, ZoomLevel zoom
)
95 const SpriteData
*src
= (const SpriteData
*)bp
->sprite
;
97 /* src_px : each line begins with uint32_t n = 'number of bytes in this line',
98 * then n times is the Colour struct for this line */
99 const Colour
*src_px
= (const Colour
*)(src
->data
+ src
->offset
[zoom
][0]);
100 /* src_n : each line begins with uint32_t n = 'number of bytes in this line',
101 * then interleaved stream of 'm' and 'n' channels. 'm' is remap,
102 * 'n' is number of bytes with the same alpha channel class */
103 const uint16_t *src_n
= (const uint16_t *)(src
->data
+ src
->offset
[zoom
][1]);
105 /* skip upper lines in src_px and src_n */
106 for (uint i
= bp
->skip_top
; i
!= 0; i
--) {
107 src_px
= (const Colour
*)((const uint8_t *)src_px
+ *(const uint32_t *)src_px
);
108 src_n
= (const uint16_t *)((const uint8_t *)src_n
+ *(const uint32_t *)src_n
);
111 /* skip lines in dst */
112 Colour
*dst
= (Colour
*)bp
->dst
+ bp
->top
* bp
->pitch
+ bp
->left
;
113 assert(VideoDriver::GetInstance()->GetAnimBuffer() != nullptr);
114 uint8_t *anim
= VideoDriver::GetInstance()->GetAnimBuffer() + ((uint32_t *)bp
->dst
- (uint32_t *)_screen
.dst_ptr
) + bp
->top
* bp
->pitch
+ bp
->left
;
116 /* store so we don't have to access it via bp everytime (compiler assumes pointer aliasing) */
117 const uint8_t *remap
= bp
->remap
;
119 for (int y
= 0; y
< bp
->height
; y
++) {
120 /* next dst line begins here */
121 Colour
*dst_ln
= dst
+ bp
->pitch
;
122 uint8_t *anim_ln
= anim
+ bp
->pitch
;
124 /* next src line begins here */
125 const Colour
*src_px_ln
= (const Colour
*)((const uint8_t *)src_px
+ *(const uint32_t *)src_px
);
128 /* next src_n line begins here */
129 const uint16_t *src_n_ln
= (const uint16_t *)((const uint8_t *)src_n
+ *(const uint32_t *)src_n
);
132 /* we will end this line when we reach this point */
133 Colour
*dst_end
= dst
+ bp
->skip_left
;
135 /* number of pixels with the same alpha channel class */
138 while (dst
< dst_end
) {
141 if (src_px
->a
== 0) {
146 if (dst
> dst_end
) anim
+= dst
- dst_end
;
148 if (dst
+ n
> dst_end
) {
149 uint d
= dst_end
- dst
;
153 dst
= dst_end
- bp
->skip_left
;
154 dst_end
= dst
+ bp
->width
;
156 n
= std::min
<uint
>(n
- d
, (uint
)bp
->width
);
165 dst
-= bp
->skip_left
;
166 dst_end
-= bp
->skip_left
;
168 dst_end
+= bp
->width
;
170 while (dst
< dst_end
) {
171 n
= std::min
<uint
>(*src_n
++, (uint
)(dst_end
- dst
));
173 if (src_px
->a
== 0) {
184 case BM_COLOUR_REMAP
:
186 if (src_px
->a
== 255) {
188 uint8_t m
= GB(*src_n
, 0, 8);
189 /* In case the m-channel is zero, only apply the crash remap by darkening the RGB colour. */
191 *dst
= mode
== BM_CRASH_REMAP
? this->MakeDark(*src_px
) : *src_px
;
207 uint8_t m
= GB(*src_n
, 0, 8);
208 Colour b
= this->RealizeBlendedColour(*anim
, *dst
);
210 Colour c
= mode
== BM_CRASH_REMAP
? this->MakeDark(*src_px
) : *src_px
;
211 *dst
= this->ComposeColourRGBANoCheck(c
.r
, c
.g
, c
.b
, src_px
->a
, b
);
216 *dst
= this->ComposeColourPANoCheck(this->LookupColourInPalette(r
), src_px
->a
, b
);
217 *anim
= 0; // Animation colours don't work with alpha-blending.
231 *dst
++ = _black_colour
;
238 /* Make the current colour a bit more black, so it looks like this image is transparent */
240 if (src_px
->a
== 255) {
243 /* If the anim buffer contains a color value, the image composition will
244 * only look at the RGB brightness value. As such, we can simply darken the
245 * RGB value to darken the anim color. */
246 Colour b
= *anim
!= 0 ? Colour(this->GetColourBrightness(*dst
), 0, 0) : *dst
;
247 *dst
= this->MakeTransparent(b
, 3, 4);
253 Colour b
= this->RealizeBlendedColour(*anim
, *dst
);
254 *dst
= this->MakeTransparent(b
, (256 * 4 - src_px
->a
), 256 * 4);
255 *anim
= 0; // Animation colours don't work with alpha-blending.
263 case BM_TRANSPARENT_REMAP
:
264 /* Apply custom transparency remap. */
266 if (src_px
->a
!= 0) {
270 *anim
= remap
[*anim
];
272 *dst
= this->LookupColourInPalette(remap
[GetNearestColourIndex(*dst
)]);
286 if (src_px
->a
== 255) {
288 *anim
++ = GB(*src_n
, 0, 8);
289 *dst
++ = src_px
->data
;
296 uint8_t m
= GB(*src_n
, 0, 8);
297 Colour b
= this->RealizeBlendedColour(*anim
, *dst
);
300 *dst
= this->ComposeColourRGBANoCheck(src_px
->r
, src_px
->g
, src_px
->b
, src_px
->a
, b
);
303 *dst
= this->ComposeColourPANoCheck(this->LookupColourInPalette(m
), src_px
->a
, b
);
324 * Draws a sprite to a (screen) buffer. Calls adequate templated function.
326 * @param bp further blitting parameters
327 * @param mode blitter mode
328 * @param zoom zoom level at which we are drawing
330 void Blitter_40bppAnim::Draw(Blitter::BlitterParams
*bp
, BlitterMode mode
, ZoomLevel zoom
)
332 assert(_screen
.dst_ptr
!= nullptr);
334 if (_screen_disable_anim
|| VideoDriver::GetInstance()->GetAnimBuffer() == nullptr) {
335 /* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent Draw() */
336 Blitter_32bppOptimized::Draw
<true>(bp
, mode
, zoom
);
341 default: NOT_REACHED();
342 case BM_NORMAL
: Draw
<BM_NORMAL
> (bp
, zoom
); return;
343 case BM_COLOUR_REMAP
: Draw
<BM_COLOUR_REMAP
>(bp
, zoom
); return;
344 case BM_TRANSPARENT
: Draw
<BM_TRANSPARENT
> (bp
, zoom
); return;
345 case BM_TRANSPARENT_REMAP
: Draw
<BM_TRANSPARENT_REMAP
>(bp
, zoom
); return;
346 case BM_CRASH_REMAP
: Draw
<BM_CRASH_REMAP
> (bp
, zoom
); return;
347 case BM_BLACK_REMAP
: Draw
<BM_BLACK_REMAP
> (bp
, zoom
); return;
351 void Blitter_40bppAnim::DrawColourMappingRect(void *dst
, int width
, int height
, PaletteID pal
)
353 if (_screen_disable_anim
) {
354 /* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent DrawColourMappingRect() */
355 Blitter_32bppOptimized::DrawColourMappingRect(dst
, width
, height
, pal
);
359 Colour
*udst
= (Colour
*)dst
;
360 uint8_t *anim
= VideoDriver::GetInstance()->GetAnimBuffer() + ((uint32_t *)dst
- (uint32_t *)_screen
.dst_ptr
);
362 if (pal
== PALETTE_TO_TRANSPARENT
) {
363 /* If the anim buffer contains a color value, the image composition will
364 * only look at the RGB brightness value. As such, we can simply darken the
365 * RGB value to darken the anim color. */
367 for (int i
= 0; i
!= width
; i
++) {
368 Colour b
= *anim
!= 0 ? Colour(this->GetColourBrightness(*udst
), 0, 0) : *udst
;
369 *udst
= MakeTransparent(b
, 154);
373 udst
= udst
- width
+ _screen
.pitch
;
374 anim
= anim
- width
+ _screen
.pitch
;
376 } else if (pal
== PALETTE_NEWSPAPER
) {
377 const uint8_t *remap
= GetNonSprite(pal
, SpriteType::Recolour
) + 1;
379 for (int i
= 0; i
!= width
; i
++) {
380 if (*anim
== 0) *udst
= MakeGrey(*udst
);
381 *anim
= remap
[*anim
];
385 udst
= udst
- width
+ _screen
.pitch
;
386 anim
= anim
- width
+ _screen
.pitch
;
389 const uint8_t *remap
= GetNonSprite(pal
, SpriteType::Recolour
) + 1;
391 for (int i
= 0; i
!= width
; i
++) {
392 *anim
= remap
[*anim
];
395 anim
= anim
- width
+ _screen
.pitch
;
400 Sprite
*Blitter_40bppAnim::Encode(const SpriteLoader::SpriteCollection
&sprite
, SpriteAllocator
&allocator
)
402 return this->EncodeInternal
<false>(sprite
, allocator
);
406 void Blitter_40bppAnim::CopyFromBuffer(void *video
, const void *src
, int width
, int height
)
408 assert(!_screen_disable_anim
);
409 assert(video
>= _screen
.dst_ptr
&& video
<= (uint32_t *)_screen
.dst_ptr
+ _screen
.width
+ _screen
.height
* _screen
.pitch
);
410 uint32_t *dst
= (uint32_t *)video
;
411 const uint32_t *usrc
= (const uint32_t *)src
;
413 uint8_t *anim_buf
= VideoDriver::GetInstance()->GetAnimBuffer();
414 if (anim_buf
== nullptr) return;
415 uint8_t *anim_line
= ((uint32_t *)video
- (uint32_t *)_screen
.dst_ptr
) + anim_buf
;
417 for (; height
> 0; height
--) {
418 memcpy(dst
, usrc
, width
* sizeof(uint32_t));
420 dst
+= _screen
.pitch
;
421 /* Copy back the anim-buffer */
422 memcpy(anim_line
, usrc
, width
* sizeof(uint8_t));
423 usrc
= (const uint32_t *)((const uint8_t *)usrc
+ width
);
424 anim_line
+= _screen
.pitch
;
428 void Blitter_40bppAnim::CopyToBuffer(const void *video
, void *dst
, int width
, int height
)
430 assert(!_screen_disable_anim
);
431 assert(video
>= _screen
.dst_ptr
&& video
<= (uint32_t *)_screen
.dst_ptr
+ _screen
.width
+ _screen
.height
* _screen
.pitch
);
432 uint32_t *udst
= (uint32_t *)dst
;
433 const uint32_t *src
= (const uint32_t *)video
;
435 uint8_t *anim_buf
= VideoDriver::GetInstance()->GetAnimBuffer();
436 if (anim_buf
== nullptr) return;
437 const uint8_t *anim_line
= ((const uint32_t *)video
- (uint32_t *)_screen
.dst_ptr
) + anim_buf
;
439 for (; height
> 0; height
--) {
440 memcpy(udst
, src
, width
* sizeof(uint32_t));
441 src
+= _screen
.pitch
;
443 /* Copy the anim-buffer */
444 memcpy(udst
, anim_line
, width
* sizeof(uint8_t));
445 udst
= (uint32_t *)((uint8_t *)udst
+ width
);
446 anim_line
+= _screen
.pitch
;
450 void Blitter_40bppAnim::CopyImageToBuffer(const void *video
, void *dst
, int width
, int height
, int dst_pitch
)
452 uint8_t *anim_buf
= VideoDriver::GetInstance()->GetAnimBuffer();
453 if (anim_buf
== nullptr) {
454 Blitter_32bppOptimized::CopyImageToBuffer(video
, dst
, width
, height
, dst_pitch
);
458 uint32_t *udst
= (uint32_t *)dst
;
459 const uint32_t *src
= (const uint32_t *)video
;
460 const uint8_t *anim_line
= ((const uint32_t *)video
- (uint32_t *)_screen
.dst_ptr
) + anim_buf
;
462 for (; height
> 0; height
--) {
463 for (int x
= 0; x
< width
; x
++) {
464 udst
[x
] = this->RealizeBlendedColour(anim_line
[x
], src
[x
]).data
;
466 src
+= _screen
.pitch
;
467 anim_line
+= _screen
.pitch
;
472 void Blitter_40bppAnim::ScrollBuffer(void *video
, int &left
, int &top
, int &width
, int &height
, int scroll_x
, int scroll_y
)
474 assert(!_screen_disable_anim
);
475 assert(video
>= _screen
.dst_ptr
&& video
<= (uint32_t *)_screen
.dst_ptr
+ _screen
.width
+ _screen
.height
* _screen
.pitch
);
476 uint8_t *anim_buf
= VideoDriver::GetInstance()->GetAnimBuffer();
479 /* We need to scroll the anim-buffer too */
481 dst
= anim_buf
+ left
+ (top
+ height
- 1) * _screen
.pitch
;
482 src
= dst
- scroll_y
* _screen
.pitch
;
484 /* Adjust left & width */
491 uint tw
= width
+ (scroll_x
>= 0 ? -scroll_x
: scroll_x
);
492 uint th
= height
- scroll_y
;
493 for (; th
> 0; th
--) {
494 memcpy(dst
, src
, tw
* sizeof(uint8_t));
495 src
-= _screen
.pitch
;
496 dst
-= _screen
.pitch
;
499 /* Calculate pointers */
500 dst
= anim_buf
+ left
+ top
* _screen
.pitch
;
501 src
= dst
- scroll_y
* _screen
.pitch
;
503 /* Adjust left & width */
510 /* the y-displacement may be 0 therefore we have to use memmove,
511 * because source and destination may overlap */
512 uint tw
= width
+ (scroll_x
>= 0 ? -scroll_x
: scroll_x
);
513 uint th
= height
+ scroll_y
;
514 for (; th
> 0; th
--) {
515 memmove(dst
, src
, tw
* sizeof(uint8_t));
516 src
+= _screen
.pitch
;
517 dst
+= _screen
.pitch
;
521 Blitter_32bppBase::ScrollBuffer(video
, left
, top
, width
, height
, scroll_x
, scroll_y
);
524 size_t Blitter_40bppAnim::BufferSize(uint width
, uint height
)
526 return (sizeof(uint32_t) + sizeof(uint8_t)) * width
* height
;
529 Blitter::PaletteAnimation
Blitter_40bppAnim::UsePaletteAnimation()
531 return Blitter::PALETTE_ANIMATION_VIDEO_BACKEND
;
534 bool Blitter_40bppAnim::NeedsAnimationBuffer()