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 32bpp_anim.cpp Implementation of the optimized 32 bpp blitter with animation support. */
10 #include "../stdafx.h"
11 #include "../video/video_driver.hpp"
12 #include "32bpp_anim.hpp"
15 #include "../table/sprites.h"
17 #include "../safeguards.h"
19 /** Instantiation of the 32bpp with animation blitter factory. */
20 static FBlitter_32bppAnim iFBlitter_32bppAnim
;
22 Blitter_32bppAnim::~Blitter_32bppAnim()
24 free(this->anim_alloc
);
27 template <BlitterMode mode
>
28 inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams
*bp
, ZoomLevel zoom
)
30 const SpriteData
*src
= (const SpriteData
*)bp
->sprite
;
32 const Colour
*src_px
= (const Colour
*)(src
->data
+ src
->offset
[zoom
][0]);
33 const uint16
*src_n
= (const uint16
*)(src
->data
+ src
->offset
[zoom
][1]);
35 for (uint i
= bp
->skip_top
; i
!= 0; i
--) {
36 src_px
= (const Colour
*)((const byte
*)src_px
+ *(const uint32
*)src_px
);
37 src_n
= (const uint16
*)((const byte
*)src_n
+ *(const uint32
*)src_n
);
40 Colour
*dst
= (Colour
*)bp
->dst
+ bp
->top
* bp
->pitch
+ bp
->left
;
41 uint16
*anim
= this->anim_buf
+ this->ScreenToAnimOffset((uint32
*)bp
->dst
) + bp
->top
* this->anim_buf_pitch
+ bp
->left
;
43 const byte
*remap
= bp
->remap
; // store so we don't have to access it via bp every time
45 for (int y
= 0; y
< bp
->height
; y
++) {
46 Colour
*dst_ln
= dst
+ bp
->pitch
;
47 uint16
*anim_ln
= anim
+ this->anim_buf_pitch
;
49 const Colour
*src_px_ln
= (const Colour
*)((const byte
*)src_px
+ *(const uint32
*)src_px
);
52 const uint16
*src_n_ln
= (const uint16
*)((const byte
*)src_n
+ *(const uint32
*)src_n
);
55 Colour
*dst_end
= dst
+ bp
->skip_left
;
59 while (dst
< dst_end
) {
67 if (dst
> dst_end
) anim
+= dst
- dst_end
;
69 if (dst
+ n
> dst_end
) {
70 uint d
= dst_end
- dst
;
74 dst
= dst_end
- bp
->skip_left
;
75 dst_end
= dst
+ bp
->width
;
77 n
= std::min(n
- d
, (uint
)bp
->width
);
87 dst_end
-= bp
->skip_left
;
91 while (dst
< dst_end
) {
92 n
= std::min
<uint
>(*src_n
++, dst_end
- dst
);
105 case BM_COLOUR_REMAP
:
106 if (src_px
->a
== 255) {
109 /* In case the m-channel is zero, do not remap this pixel in any way */
114 uint r
= remap
[GB(m
, 0, 8)];
115 *anim
= r
| (m
& 0xFF00);
116 if (r
!= 0) *dst
= this->AdjustBrightness(this->LookupColourInPalette(r
), GB(m
, 8, 8));
127 *dst
= ComposeColourRGBANoCheck(src_px
->r
, src_px
->g
, src_px
->b
, src_px
->a
, *dst
);
130 uint r
= remap
[GB(m
, 0, 8)];
132 if (r
!= 0) *dst
= ComposeColourPANoCheck(this->AdjustBrightness(this->LookupColourInPalette(r
), GB(m
, 8, 8)), src_px
->a
, *dst
);
143 if (src_px
->a
== 255) {
147 uint8 g
= MakeDark(src_px
->r
, src_px
->g
, src_px
->b
);
148 *dst
= ComposeColourRGBA(g
, g
, g
, src_px
->a
, *dst
);
151 uint r
= remap
[GB(m
, 0, 8)];
152 *anim
= r
| (m
& 0xFF00);
153 if (r
!= 0) *dst
= this->AdjustBrightness(this->LookupColourInPalette(r
), GB(m
, 8, 8));
164 if (src_px
->a
!= 0) {
165 uint8 g
= MakeDark(src_px
->r
, src_px
->g
, src_px
->b
);
166 *dst
= ComposeColourRGBA(g
, g
, g
, src_px
->a
, *dst
);
170 uint r
= remap
[GB(m
, 0, 8)];
172 if (r
!= 0) *dst
= ComposeColourPANoCheck(this->AdjustBrightness(this->LookupColourInPalette(r
), GB(m
, 8, 8)), src_px
->a
, *dst
);
185 *dst
++ = Colour(0, 0, 0);
193 /* TODO -- We make an assumption here that the remap in fact is transparency, not some colour.
194 * This is never a problem with the code we produce, but newgrfs can make it fail... or at least:
195 * we produce a result the newgrf maker didn't expect ;) */
197 /* Make the current colour a bit more black, so it looks like this image is transparent */
199 if (src_px
->a
== 255) {
202 *dst
= MakeTransparent(*dst
, 3, 4);
209 *dst
= MakeTransparent(*dst
, (256 * 4 - src_px
->a
), 256 * 4);
219 if (src_px
->a
== 255) {
221 /* Compiler assumes pointer aliasing, can't optimise this on its own */
222 uint m
= GB(*src_n
, 0, 8);
223 /* Above PALETTE_ANIM_START is palette animation */
225 *dst
++ = (m
>= PALETTE_ANIM_START
) ? this->AdjustBrightness(this->LookupColourInPalette(m
), GB(*src_n
, 8, 8)) : src_px
->data
;
231 uint m
= GB(*src_n
, 0, 8);
233 if (m
>= PALETTE_ANIM_START
) {
234 *dst
= ComposeColourPANoCheck(this->AdjustBrightness(this->LookupColourInPalette(m
), GB(*src_n
, 8, 8)), src_px
->a
, *dst
);
236 *dst
= ComposeColourRGBANoCheck(src_px
->r
, src_px
->g
, src_px
->b
, src_px
->a
, *dst
);
254 void Blitter_32bppAnim::Draw(Blitter::BlitterParams
*bp
, BlitterMode mode
, ZoomLevel zoom
)
256 if (_screen_disable_anim
) {
257 /* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent Draw() */
258 Blitter_32bppOptimized::Draw(bp
, mode
, zoom
);
263 default: NOT_REACHED();
264 case BM_NORMAL
: Draw
<BM_NORMAL
> (bp
, zoom
); return;
265 case BM_COLOUR_REMAP
: Draw
<BM_COLOUR_REMAP
>(bp
, zoom
); return;
266 case BM_TRANSPARENT
: Draw
<BM_TRANSPARENT
> (bp
, zoom
); return;
267 case BM_CRASH_REMAP
: Draw
<BM_CRASH_REMAP
> (bp
, zoom
); return;
268 case BM_BLACK_REMAP
: Draw
<BM_BLACK_REMAP
> (bp
, zoom
); return;
272 void Blitter_32bppAnim::DrawColourMappingRect(void *dst
, int width
, int height
, PaletteID pal
)
274 if (_screen_disable_anim
) {
275 /* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent DrawColourMappingRect() */
276 Blitter_32bppOptimized::DrawColourMappingRect(dst
, width
, height
, pal
);
280 Colour
*udst
= (Colour
*)dst
;
281 uint16
*anim
= this->anim_buf
+ this->ScreenToAnimOffset((uint32
*)dst
);
283 if (pal
== PALETTE_TO_TRANSPARENT
) {
285 for (int i
= 0; i
!= width
; i
++) {
286 *udst
= MakeTransparent(*udst
, 154);
291 udst
= udst
- width
+ _screen
.pitch
;
292 anim
= anim
- width
+ this->anim_buf_pitch
;
296 if (pal
== PALETTE_NEWSPAPER
) {
298 for (int i
= 0; i
!= width
; i
++) {
299 *udst
= MakeGrey(*udst
);
304 udst
= udst
- width
+ _screen
.pitch
;
305 anim
= anim
- width
+ this->anim_buf_pitch
;
310 Debug(misc
, 0, "32bpp blitter doesn't know how to draw this colour table ('{}')", pal
);
313 void Blitter_32bppAnim::SetPixel(void *video
, int x
, int y
, uint8 colour
)
315 *((Colour
*)video
+ x
+ y
* _screen
.pitch
) = LookupColourInPalette(colour
);
317 /* Set the colour in the anim-buffer too, if we are rendering to the screen */
318 if (_screen_disable_anim
) return;
320 this->anim_buf
[this->ScreenToAnimOffset((uint32
*)video
) + x
+ y
* this->anim_buf_pitch
] = colour
| (DEFAULT_BRIGHTNESS
<< 8);
323 void Blitter_32bppAnim::DrawLine(void *video
, int x
, int y
, int x2
, int y2
, int screen_width
, int screen_height
, uint8 colour
, int width
, int dash
)
325 const Colour c
= LookupColourInPalette(colour
);
327 if (_screen_disable_anim
) {
328 this->DrawLineGeneric(x
, y
, x2
, y2
, screen_width
, screen_height
, width
, dash
, [&](int x
, int y
) {
329 *((Colour
*)video
+ x
+ y
* _screen
.pitch
) = c
;
332 uint16
* const offset_anim_buf
= this->anim_buf
+ this->ScreenToAnimOffset((uint32
*)video
);
333 const uint16 anim_colour
= colour
| (DEFAULT_BRIGHTNESS
<< 8);
334 this->DrawLineGeneric(x
, y
, x2
, y2
, screen_width
, screen_height
, width
, dash
, [&](int x
, int y
) {
335 *((Colour
*)video
+ x
+ y
* _screen
.pitch
) = c
;
336 offset_anim_buf
[x
+ y
* this->anim_buf_pitch
] = anim_colour
;
341 void Blitter_32bppAnim::DrawRect(void *video
, int width
, int height
, uint8 colour
)
343 if (_screen_disable_anim
) {
344 /* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent DrawRect() */
345 Blitter_32bppOptimized::DrawRect(video
, width
, height
, colour
);
349 Colour colour32
= LookupColourInPalette(colour
);
350 uint16
*anim_line
= this->ScreenToAnimOffset((uint32
*)video
) + this->anim_buf
;
353 Colour
*dst
= (Colour
*)video
;
354 uint16
*anim
= anim_line
;
356 for (int i
= width
; i
> 0; i
--) {
358 /* Set the colour in the anim-buffer too */
359 *anim
= colour
| (DEFAULT_BRIGHTNESS
<< 8);
363 video
= (uint32
*)video
+ _screen
.pitch
;
364 anim_line
+= this->anim_buf_pitch
;
368 void Blitter_32bppAnim::CopyFromBuffer(void *video
, const void *src
, int width
, int height
)
370 assert(!_screen_disable_anim
);
371 assert(video
>= _screen
.dst_ptr
&& video
<= (uint32
*)_screen
.dst_ptr
+ _screen
.width
+ _screen
.height
* _screen
.pitch
);
372 Colour
*dst
= (Colour
*)video
;
373 const uint32
*usrc
= (const uint32
*)src
;
374 uint16
*anim_line
= this->ScreenToAnimOffset((uint32
*)video
) + this->anim_buf
;
376 for (; height
> 0; height
--) {
377 /* We need to keep those for palette animation. */
378 Colour
*dst_pal
= dst
;
379 uint16
*anim_pal
= anim_line
;
381 memcpy(static_cast<void *>(dst
), usrc
, width
* sizeof(uint32
));
383 dst
+= _screen
.pitch
;
384 /* Copy back the anim-buffer */
385 memcpy(anim_line
, usrc
, width
* sizeof(uint16
));
386 usrc
= (const uint32
*)((const uint16
*)usrc
+ width
);
387 anim_line
+= this->anim_buf_pitch
;
389 /* Okay, it is *very* likely that the image we stored is using
390 * the wrong palette animated colours. There are two things we
391 * can do to fix this. The first is simply reviewing the whole
392 * screen after we copied the buffer, i.e. run PaletteAnimate,
393 * however that forces a full screen redraw which is expensive
394 * for just the cursor. This just copies the implementation of
395 * palette animation, much cheaper though slightly nastier. */
396 for (int i
= 0; i
< width
; i
++) {
397 uint colour
= GB(*anim_pal
, 0, 8);
398 if (colour
>= PALETTE_ANIM_START
) {
399 /* Update this pixel */
400 *dst_pal
= this->AdjustBrightness(LookupColourInPalette(colour
), GB(*anim_pal
, 8, 8));
408 void Blitter_32bppAnim::CopyToBuffer(const void *video
, void *dst
, int width
, int height
)
410 assert(!_screen_disable_anim
);
411 assert(video
>= _screen
.dst_ptr
&& video
<= (uint32
*)_screen
.dst_ptr
+ _screen
.width
+ _screen
.height
* _screen
.pitch
);
412 uint32
*udst
= (uint32
*)dst
;
413 const uint32
*src
= (const uint32
*)video
;
415 if (this->anim_buf
== nullptr) return;
417 const uint16
*anim_line
= this->ScreenToAnimOffset((const uint32
*)video
) + this->anim_buf
;
419 for (; height
> 0; height
--) {
420 memcpy(udst
, src
, width
* sizeof(uint32
));
421 src
+= _screen
.pitch
;
423 /* Copy the anim-buffer */
424 memcpy(udst
, anim_line
, width
* sizeof(uint16
));
425 udst
= (uint32
*)((uint16
*)udst
+ width
);
426 anim_line
+= this->anim_buf_pitch
;
430 void Blitter_32bppAnim::ScrollBuffer(void *video
, int &left
, int &top
, int &width
, int &height
, int scroll_x
, int scroll_y
)
432 assert(!_screen_disable_anim
);
433 assert(video
>= _screen
.dst_ptr
&& video
<= (uint32
*)_screen
.dst_ptr
+ _screen
.width
+ _screen
.height
* _screen
.pitch
);
436 /* We need to scroll the anim-buffer too */
438 dst
= this->anim_buf
+ left
+ (top
+ height
- 1) * this->anim_buf_pitch
;
439 src
= dst
- scroll_y
* this->anim_buf_pitch
;
441 /* Adjust left & width */
448 uint tw
= width
+ (scroll_x
>= 0 ? -scroll_x
: scroll_x
);
449 uint th
= height
- scroll_y
;
450 for (; th
> 0; th
--) {
451 memcpy(dst
, src
, tw
* sizeof(uint16
));
452 src
-= this->anim_buf_pitch
;
453 dst
-= this->anim_buf_pitch
;
456 /* Calculate pointers */
457 dst
= this->anim_buf
+ left
+ top
* this->anim_buf_pitch
;
458 src
= dst
- scroll_y
* this->anim_buf_pitch
;
460 /* Adjust left & width */
467 /* the y-displacement may be 0 therefore we have to use memmove,
468 * because source and destination may overlap */
469 uint tw
= width
+ (scroll_x
>= 0 ? -scroll_x
: scroll_x
);
470 uint th
= height
+ scroll_y
;
471 for (; th
> 0; th
--) {
472 memmove(dst
, src
, tw
* sizeof(uint16
));
473 src
+= this->anim_buf_pitch
;
474 dst
+= this->anim_buf_pitch
;
478 Blitter_32bppBase::ScrollBuffer(video
, left
, top
, width
, height
, scroll_x
, scroll_y
);
481 int Blitter_32bppAnim::BufferSize(int width
, int height
)
483 return width
* height
* (sizeof(uint32
) + sizeof(uint16
));
486 void Blitter_32bppAnim::PaletteAnimate(const Palette
&palette
)
488 assert(!_screen_disable_anim
);
490 this->palette
= palette
;
491 /* If first_dirty is 0, it is for 8bpp indication to send the new
492 * palette. However, only the animation colours might possibly change.
493 * Especially when going between toyland and non-toyland. */
494 assert(this->palette
.first_dirty
== PALETTE_ANIM_START
|| this->palette
.first_dirty
== 0);
496 const uint16
*anim
= this->anim_buf
;
497 Colour
*dst
= (Colour
*)_screen
.dst_ptr
;
499 /* Let's walk the anim buffer and try to find the pixels */
500 const int width
= this->anim_buf_width
;
501 const int pitch_offset
= _screen
.pitch
- width
;
502 const int anim_pitch_offset
= this->anim_buf_pitch
- width
;
503 for (int y
= this->anim_buf_height
; y
!= 0 ; y
--) {
504 for (int x
= width
; x
!= 0 ; x
--) {
505 uint16 value
= *anim
;
506 uint8 colour
= GB(value
, 0, 8);
507 if (colour
>= PALETTE_ANIM_START
) {
508 /* Update this pixel */
509 *dst
= this->AdjustBrightness(LookupColourInPalette(colour
), GB(value
, 8, 8));
515 anim
+= anim_pitch_offset
;
518 /* Make sure the backend redraws the whole screen */
519 VideoDriver::GetInstance()->MakeDirty(0, 0, _screen
.width
, _screen
.height
);
522 Blitter::PaletteAnimation
Blitter_32bppAnim::UsePaletteAnimation()
524 return Blitter::PALETTE_ANIMATION_BLITTER
;
527 void Blitter_32bppAnim::PostResize()
529 if (_screen
.width
!= this->anim_buf_width
|| _screen
.height
!= this->anim_buf_height
||
530 _screen
.pitch
!= this->anim_buf_pitch
) {
531 /* The size of the screen changed; we can assume we can wipe all data from our buffer */
532 free(this->anim_alloc
);
533 this->anim_buf_width
= _screen
.width
;
534 this->anim_buf_height
= _screen
.height
;
535 this->anim_buf_pitch
= (_screen
.width
+ 7) & ~7;
536 this->anim_alloc
= CallocT
<uint16
>(this->anim_buf_pitch
* this->anim_buf_height
+ 8);
538 /* align buffer to next 16 byte boundary */
539 this->anim_buf
= reinterpret_cast<uint16
*>((reinterpret_cast<uintptr_t>(this->anim_alloc
) + 0xF) & (~0xF));