4 * This file is part of OpenTTD.
5 * 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.
6 * 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.
7 * 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/>.
10 /** @file 32bpp_anim.cpp Implementation of the optimized 32 bpp blitter with animation support. */
12 #include "../stdafx.h"
13 #include "../video/video_driver.hpp"
14 #include "../zoom_func.h"
15 #include "32bpp_anim.hpp"
17 #include "../table/sprites.h"
19 #include "../safeguards.h"
21 /** Instantiation of the 32bpp with animation blitter factory. */
22 static FBlitter_32bppAnim iFBlitter_32bppAnim
;
24 Blitter_32bppAnim::~Blitter_32bppAnim()
29 template <BlitterMode mode
, bool fast_path
>
30 inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams
*bp
, ZoomLevel zoom
)
32 const SpriteData
*src
= (const SpriteData
*)bp
->sprite
;
33 const BlitterSpriteFlags sprite_flags
= src
->flags
;
35 const Colour
*src_px
= (const Colour
*)(src
->data
+ src
->offset
[zoom
][0]);
36 const uint16
*src_n
= (const uint16
*)(src
->data
+ src
->offset
[zoom
][1]);
38 for (uint i
= bp
->skip_top
; i
!= 0; i
--) {
39 src_px
= (const Colour
*)((const byte
*)src_px
+ *(const uint32
*)src_px
);
40 src_n
= (const uint16
*)((const byte
*)src_n
+ *(const uint32
*)src_n
);
43 Colour
*dst
= (Colour
*)bp
->dst
+ bp
->top
* bp
->pitch
+ bp
->left
;
44 assert(_screen
.pitch
== this->anim_buf_pitch
); // precondition for translating 'bp->dst' into an 'anim_buf' offset below.
45 uint16
*anim
= this->anim_buf
+ ((uint32
*)bp
->dst
- (uint32
*)_screen
.dst_ptr
) + bp
->top
* this->anim_buf_pitch
+ bp
->left
;
47 const byte
*remap
= bp
->remap
; // store so we don't have to access it via bp everytime
48 const int width
= bp
->width
;
49 const int pitch
= bp
->pitch
;
50 const int anim_pitch
= this->anim_buf_pitch
;
51 const int skip_left
= bp
->skip_left
;
52 const int height
= bp
->height
;
54 for (int y
= 0; y
< height
; y
++) {
55 Colour
*dst_ln
= dst
+ pitch
;
56 uint16
*anim_ln
= anim
+ anim_pitch
;
58 const Colour
*src_px_ln
= (const Colour
*)((const byte
*)src_px
+ *(const uint32
*)src_px
);
61 const uint16
*src_n_ln
= (const uint16
*)((const byte
*)src_n
+ *(const uint32
*)src_n
);
64 Colour
*dst_end
= dst
;
71 while (dst
< dst_end
) {
79 if (dst
> dst_end
) anim
+= dst
- dst_end
;
81 if (dst
+ n
> dst_end
) {
82 uint d
= dst_end
- dst
;
86 dst
= dst_end
- skip_left
;
87 dst_end
= dst
+ width
;
89 n
= min
<uint
>(n
- d
, (uint
)width
);
104 while (dst
< dst_end
) {
108 n
= min
<uint
>(*src_n
++, (uint
)(dst_end
- dst
));
111 if (src_px
->a
== 0) {
122 case BM_COLOUR_REMAP
:
123 if (src_px
->a
== 255) {
126 /* In case the m-channel is zero, do not remap this pixel in any way */
131 uint r
= remap
[GB(m
, 0, 8)];
132 *anim
= r
| (m
& 0xFF00);
133 if (r
!= 0) *dst
= this->AdjustBrightness(this->LookupColourInPalette(r
), GB(m
, 8, 8));
144 *dst
= ComposeColourRGBANoCheck(src_px
->r
, src_px
->g
, src_px
->b
, src_px
->a
, *dst
);
147 uint r
= remap
[GB(m
, 0, 8)];
149 if (r
!= 0) *dst
= ComposeColourPANoCheck(this->AdjustBrightness(this->LookupColourInPalette(r
), GB(m
, 8, 8)), src_px
->a
, *dst
);
160 if (src_px
->a
== 255) {
164 uint8 g
= MakeDark(src_px
->r
, src_px
->g
, src_px
->b
);
165 *dst
= ComposeColourRGBA(g
, g
, g
, src_px
->a
, *dst
);
168 uint r
= remap
[GB(m
, 0, 8)];
169 *anim
= r
| (m
& 0xFF00);
170 if (r
!= 0) *dst
= this->AdjustBrightness(this->LookupColourInPalette(r
), GB(m
, 8, 8));
181 if (src_px
->a
!= 0) {
182 uint8 g
= MakeDark(src_px
->r
, src_px
->g
, src_px
->b
);
183 *dst
= ComposeColourRGBA(g
, g
, g
, src_px
->a
, *dst
);
187 uint r
= remap
[GB(m
, 0, 8)];
189 if (r
!= 0) *dst
= ComposeColourPANoCheck(this->AdjustBrightness(this->LookupColourInPalette(r
), GB(m
, 8, 8)), src_px
->a
, *dst
);
202 *dst
++ = Colour(0, 0, 0);
210 /* TODO -- We make an assumption here that the remap in fact is transparency, not some colour.
211 * This is never a problem with the code we produce, but newgrfs can make it fail... or at least:
212 * we produce a result the newgrf maker didn't expect ;) */
214 /* Make the current colour a bit more black, so it looks like this image is transparent */
216 if (src_px
->a
== 255) {
219 *dst
= MakeTransparent(*dst
, 3, 4);
226 *dst
= MakeTransparent(*dst
, (256 * 4 - src_px
->a
), 256 * 4);
236 if (fast_path
|| (src_px
->a
== 255 && (sprite_flags
& SF_NO_ANIM
))) {
239 *dst
++ = src_px
->data
;
243 } else if (src_px
->a
== 255) {
245 /* Compiler assumes pointer aliasing, can't optimise this on its own */
246 uint m
= GB(*src_n
, 0, 8);
247 /* Above PALETTE_ANIM_START is palette animation */
249 *dst
++ = (m
>= PALETTE_ANIM_START
) ? this->AdjustBrightness(this->LookupColourInPalette(m
), GB(*src_n
, 8, 8)) : src_px
->data
;
255 uint m
= GB(*src_n
, 0, 8);
257 if (m
>= PALETTE_ANIM_START
) {
258 *dst
= ComposeColourPANoCheck(this->AdjustBrightness(this->LookupColourInPalette(m
), GB(*src_n
, 8, 8)), src_px
->a
, *dst
);
260 *dst
= ComposeColourRGBANoCheck(src_px
->r
, src_px
->g
, src_px
->b
, src_px
->a
, *dst
);
278 void Blitter_32bppAnim::Draw(Blitter::BlitterParams
*bp
, BlitterMode mode
, ZoomLevel zoom
)
280 if (_screen_disable_anim
) {
281 /* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent Draw() */
282 Blitter_32bppOptimized::Draw(bp
, mode
, zoom
);
286 const BlitterSpriteFlags sprite_flags
= ((const SpriteData
*)bp
->sprite
)->flags
;
289 default: NOT_REACHED();
291 case BM_COLOUR_REMAP
:
292 if (!(sprite_flags
& SF_NO_REMAP
)) {
293 Draw
<BM_COLOUR_REMAP
, false>(bp
, zoom
);
299 if ((sprite_flags
& (SF_NO_ANIM
| SF_TRANSLUCENT
)) == SF_NO_ANIM
&&
300 bp
->skip_left
== 0 && bp
->width
== UnScaleByZoom(bp
->sprite_width
, zoom
)) {
301 Draw
<BM_NORMAL
, true>(bp
, zoom
);
303 Draw
<BM_NORMAL
, false>(bp
, zoom
);
307 case BM_TRANSPARENT
: Draw
<BM_TRANSPARENT
, false> (bp
, zoom
); return;
308 case BM_CRASH_REMAP
: Draw
<BM_CRASH_REMAP
, false> (bp
, zoom
); return;
309 case BM_BLACK_REMAP
: Draw
<BM_BLACK_REMAP
, false> (bp
, zoom
); return;
313 void Blitter_32bppAnim::DrawColourMappingRect(void *dst
, int width
, int height
, PaletteID pal
)
315 if (_screen_disable_anim
) {
316 /* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent DrawColourMappingRect() */
317 Blitter_32bppOptimized::DrawColourMappingRect(dst
, width
, height
, pal
);
321 Colour
*udst
= (Colour
*)dst
;
322 assert(_screen
.pitch
== this->anim_buf_pitch
); // precondition for translating 'dst' into an 'anim_buf' offset below.
323 uint16
*anim
= this->anim_buf
+ ((uint32
*)dst
- (uint32
*)_screen
.dst_ptr
);
325 if (pal
== PALETTE_TO_TRANSPARENT
) {
327 for (int i
= 0; i
!= width
; i
++) {
328 *udst
= MakeTransparent(*udst
, 154);
333 udst
= udst
- width
+ _screen
.pitch
;
334 anim
= anim
- width
+ this->anim_buf_pitch
;
338 if (pal
== PALETTE_NEWSPAPER
) {
340 for (int i
= 0; i
!= width
; i
++) {
341 *udst
= MakeGrey(*udst
);
346 udst
= udst
- width
+ _screen
.pitch
;
347 anim
= anim
- width
+ this->anim_buf_pitch
;
352 DEBUG(misc
, 0, "32bpp blitter doesn't know how to draw this colour table ('%d')", pal
);
355 void Blitter_32bppAnim::SetPixel(void *video
, int x
, int y
, uint8 colour
)
357 *((Colour
*)video
+ x
+ y
* _screen
.pitch
) = LookupColourInPalette(colour
);
359 /* Set the colour in the anim-buffer too, if we are rendering to the screen */
360 if (_screen_disable_anim
) return;
361 assert(_screen
.pitch
== this->anim_buf_pitch
); // precondition for translating 'video' into an 'anim_buf' offset below.
362 this->anim_buf
[((uint32
*)video
- (uint32
*)_screen
.dst_ptr
) + x
+ y
* this->anim_buf_pitch
] = colour
| (DEFAULT_BRIGHTNESS
<< 8);
365 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
)
367 const Colour c
= LookupColourInPalette(colour
);
369 if (_screen_disable_anim
) {
370 this->DrawLineGeneric(x
, y
, x2
, y2
, screen_width
, screen_height
, width
, dash
, [&](int x
, int y
) {
371 *((Colour
*)video
+ x
+ y
* _screen
.pitch
) = c
;
374 uint16
* const offset_anim_buf
= this->anim_buf
+ this->ScreenToAnimOffset((uint32
*)video
);
375 const uint16 anim_colour
= colour
| (DEFAULT_BRIGHTNESS
<< 8);
376 this->DrawLineGeneric(x
, y
, x2
, y2
, screen_width
, screen_height
, width
, dash
, [&](int x
, int y
) {
377 *((Colour
*)video
+ x
+ y
* _screen
.pitch
) = c
;
378 offset_anim_buf
[x
+ y
* this->anim_buf_pitch
] = anim_colour
;
383 void Blitter_32bppAnim::SetLine(void *video
, int x
, int y
, uint8
*colours
, uint width
)
385 Colour
*dst
= (Colour
*)video
+ x
+ y
* _screen
.pitch
;
387 if (_screen_disable_anim
) {
389 *dst
= LookupColourInPalette(*colours
);
395 uint16
*dstanim
= (uint16
*)(&this->anim_buf
[(uint32
*)video
- (uint32
*)_screen
.dst_ptr
+ x
+ y
* _screen
.pitch
]);
397 *dstanim
= *colours
| (DEFAULT_BRIGHTNESS
<< 8);
398 *dst
= LookupColourInPalette(*colours
);
406 void Blitter_32bppAnim::SetLine32(void *video
, int x
, int y
, uint32
*colours
, uint width
)
408 Colour
*dst
= (Colour
*)video
+ x
+ y
* _screen
.pitch
;
410 if (_screen_disable_anim
) {
418 uint16
*dstanim
= (uint16
*)(&this->anim_buf
[(uint32
*)video
- (uint32
*)_screen
.dst_ptr
+ x
+ y
* _screen
.pitch
]);
430 void Blitter_32bppAnim::DrawRect(void *video
, int width
, int height
, uint8 colour
)
432 if (_screen_disable_anim
) {
433 /* This means our output is not to the screen, so we can't be doing any animation stuff, so use our parent DrawRect() */
434 Blitter_32bppOptimized::DrawRect(video
, width
, height
, colour
);
438 Colour colour32
= LookupColourInPalette(colour
);
439 assert(_screen
.pitch
== this->anim_buf_pitch
); // precondition for translating 'video' into an 'anim_buf' offset below.
440 uint16
*anim_line
= ((uint32
*)video
- (uint32
*)_screen
.dst_ptr
) + this->anim_buf
;
443 Colour
*dst
= (Colour
*)video
;
444 uint16
*anim
= anim_line
;
446 for (int i
= width
; i
> 0; i
--) {
448 /* Set the colour in the anim-buffer too */
449 *anim
= colour
| (DEFAULT_BRIGHTNESS
<< 8);
453 video
= (uint32
*)video
+ _screen
.pitch
;
454 anim_line
+= this->anim_buf_pitch
;
458 void Blitter_32bppAnim::CopyFromBuffer(void *video
, const void *src
, int width
, int height
)
460 assert(!_screen_disable_anim
);
461 assert(video
>= _screen
.dst_ptr
&& video
<= (uint32
*)_screen
.dst_ptr
+ _screen
.width
+ _screen
.height
* _screen
.pitch
);
462 Colour
*dst
= (Colour
*)video
;
463 const uint32
*usrc
= (const uint32
*)src
;
464 assert(_screen
.pitch
== this->anim_buf_pitch
); // precondition for translating 'video' into an 'anim_buf' offset below.
465 uint16
*anim_line
= ((uint32
*)video
- (uint32
*)_screen
.dst_ptr
) + this->anim_buf
;
467 for (; height
> 0; height
--) {
468 /* We need to keep those for palette animation. */
469 Colour
*dst_pal
= dst
;
470 uint16
*anim_pal
= anim_line
;
472 memcpy(dst
, usrc
, width
* sizeof(uint32
));
474 dst
+= _screen
.pitch
;
475 /* Copy back the anim-buffer */
476 memcpy(anim_line
, usrc
, width
* sizeof(uint16
));
477 usrc
= (const uint32
*)((const uint16
*)usrc
+ width
);
478 anim_line
+= this->anim_buf_pitch
;
480 /* Okay, it is *very* likely that the image we stored is using
481 * the wrong palette animated colours. There are two things we
482 * can do to fix this. The first is simply reviewing the whole
483 * screen after we copied the buffer, i.e. run PaletteAnimate,
484 * however that forces a full screen redraw which is expensive
485 * for just the cursor. This just copies the implementation of
486 * palette animation, much cheaper though slightly nastier. */
487 for (int i
= 0; i
< width
; i
++) {
488 uint colour
= GB(*anim_pal
, 0, 8);
489 if (colour
>= PALETTE_ANIM_START
) {
490 /* Update this pixel */
491 *dst_pal
= this->AdjustBrightness(LookupColourInPalette(colour
), GB(*anim_pal
, 8, 8));
499 void Blitter_32bppAnim::CopyToBuffer(const void *video
, void *dst
, int width
, int height
)
501 assert(!_screen_disable_anim
);
502 assert(video
>= _screen
.dst_ptr
&& video
<= (uint32
*)_screen
.dst_ptr
+ _screen
.width
+ _screen
.height
* _screen
.pitch
);
503 uint32
*udst
= (uint32
*)dst
;
504 const uint32
*src
= (const uint32
*)video
;
506 if (this->anim_buf
== NULL
) return;
508 assert(_screen
.pitch
== this->anim_buf_pitch
); // precondition for translating 'video' into an 'anim_buf' offset below.
509 const uint16
*anim_line
= ((const uint32
*)video
- (uint32
*)_screen
.dst_ptr
) + this->anim_buf
;
511 for (; height
> 0; height
--) {
512 memcpy(udst
, src
, width
* sizeof(uint32
));
513 src
+= _screen
.pitch
;
515 /* Copy the anim-buffer */
516 memcpy(udst
, anim_line
, width
* sizeof(uint16
));
517 udst
= (uint32
*)((uint16
*)udst
+ width
);
518 anim_line
+= this->anim_buf_pitch
;
522 void Blitter_32bppAnim::ScrollBuffer(void *video
, int &left
, int &top
, int &width
, int &height
, int scroll_x
, int scroll_y
)
524 assert(!_screen_disable_anim
);
525 assert(video
>= _screen
.dst_ptr
&& video
<= (uint32
*)_screen
.dst_ptr
+ _screen
.width
+ _screen
.height
* _screen
.pitch
);
528 /* We need to scroll the anim-buffer too */
530 dst
= this->anim_buf
+ left
+ (top
+ height
- 1) * this->anim_buf_pitch
;
531 src
= dst
- scroll_y
* this->anim_buf_pitch
;
533 /* Adjust left & width */
540 uint tw
= width
+ (scroll_x
>= 0 ? -scroll_x
: scroll_x
);
541 uint th
= height
- scroll_y
;
542 for (; th
> 0; th
--) {
543 memcpy(dst
, src
, tw
* sizeof(uint16
));
544 src
-= this->anim_buf_pitch
;
545 dst
-= this->anim_buf_pitch
;
548 /* Calculate pointers */
549 dst
= this->anim_buf
+ left
+ top
* this->anim_buf_pitch
;
550 src
= dst
- scroll_y
* this->anim_buf_pitch
;
552 /* Adjust left & width */
559 /* the y-displacement may be 0 therefore we have to use memmove,
560 * because source and destination may overlap */
561 uint tw
= width
+ (scroll_x
>= 0 ? -scroll_x
: scroll_x
);
562 uint th
= height
+ scroll_y
;
563 for (; th
> 0; th
--) {
564 memmove(dst
, src
, tw
* sizeof(uint16
));
565 src
+= this->anim_buf_pitch
;
566 dst
+= this->anim_buf_pitch
;
570 Blitter_32bppBase::ScrollBuffer(video
, left
, top
, width
, height
, scroll_x
, scroll_y
);
573 int Blitter_32bppAnim::BufferSize(int width
, int height
)
575 return width
* height
* (sizeof(uint32
) + sizeof(uint16
));
578 void Blitter_32bppAnim::PaletteAnimate(const Palette
&palette
)
580 assert(!_screen_disable_anim
);
582 this->palette
= palette
;
583 /* If first_dirty is 0, it is for 8bpp indication to send the new
584 * palette. However, only the animation colours might possibly change.
585 * Especially when going between toyland and non-toyland. */
586 assert(this->palette
.first_dirty
== PALETTE_ANIM_START
|| this->palette
.first_dirty
== 0);
588 const uint16
*anim
= this->anim_buf
;
589 Colour
*dst
= (Colour
*)_screen
.dst_ptr
;
591 /* Let's walk the anim buffer and try to find the pixels */
592 for (int y
= this->anim_buf_height
; y
!= 0 ; y
--) {
593 for (int x
= this->anim_buf_width
; x
!= 0 ; x
--) {
594 uint colour
= GB(*anim
, 0, 8);
595 if (colour
>= PALETTE_ANIM_START
) {
596 /* Update this pixel */
597 *dst
= this->AdjustBrightness(LookupColourInPalette(colour
), GB(*anim
, 8, 8));
602 dst
+= _screen
.pitch
- this->anim_buf_width
;
603 anim
+= this->anim_buf_pitch
- this->anim_buf_width
;
606 /* Make sure the backend redraws the whole screen */
607 VideoDriver::GetInstance()->MakeDirty(0, 0, _screen
.width
, _screen
.height
);
610 Blitter::PaletteAnimation
Blitter_32bppAnim::UsePaletteAnimation()
612 return Blitter::PALETTE_ANIMATION_BLITTER
;
615 void Blitter_32bppAnim::PostResize()
617 if (_screen
.width
!= this->anim_buf_width
|| _screen
.height
!= this->anim_buf_height
||
618 _screen
.pitch
!= this->anim_buf_pitch
) {
619 /* The size of the screen changed; we can assume we can wipe all data from our buffer */
620 free(this->anim_buf
);
621 this->anim_buf_width
= _screen
.width
;
622 this->anim_buf_height
= _screen
.height
;
623 this->anim_buf_pitch
= _screen
.pitch
;
624 this->anim_buf
= CallocT
<uint16
>(this->anim_buf_height
* this->anim_buf_pitch
);