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_sse4_anim.cpp Implementation of the SSE4 32 bpp blitter with animation support. */
14 #include "../stdafx.h"
15 #include "../video/video_driver.hpp"
16 #include "../table/sprites.h"
17 #include "32bpp_anim_sse4.hpp"
18 #include "32bpp_sse_func.hpp"
20 #include "../safeguards.h"
22 /** Instantiation of the SSE4 32bpp blitter factory. */
23 static FBlitter_32bppSSE4_Anim iFBlitter_32bppSSE4_Anim
;
26 * Draws a sprite to a (screen) buffer. It is templated to allow faster operation.
28 * @tparam mode blitter mode
29 * @param bp further blitting parameters
30 * @param zoom zoom level at which we are drawing
32 IGNORE_UNINITIALIZED_WARNING_START
33 template <BlitterMode mode
, Blitter_32bppSSE2::ReadMode read_mode
, Blitter_32bppSSE2::BlockType bt_last
, bool translucent
, bool animated
>
34 inline void Blitter_32bppSSE4_Anim::Draw(const Blitter::BlitterParams
*bp
, ZoomLevel zoom
)
36 const byte
* const remap
= bp
->remap
;
37 Colour
*dst_line
= (Colour
*) bp
->dst
+ bp
->top
* bp
->pitch
+ bp
->left
;
38 assert(_screen
.pitch
== this->anim_buf_pitch
); // precondition for translating 'bp->dst' into an 'anim_buf' offset below.
39 uint16
*anim_line
= this->anim_buf
+ ((uint32
*)bp
->dst
- (uint32
*)_screen
.dst_ptr
) + bp
->top
* this->anim_buf_pitch
+ bp
->left
;
40 int effective_width
= bp
->width
;
42 /* Find where to start reading in the source sprite. */
43 const Blitter_32bppSSE_Base::SpriteData
* const sd
= (const Blitter_32bppSSE_Base::SpriteData
*) bp
->sprite
;
44 const SpriteInfo
* const si
= &sd
->infos
[zoom
];
45 const MapValue
*src_mv_line
= (const MapValue
*) &sd
->data
[si
->mv_offset
] + bp
->skip_top
* si
->sprite_width
;
46 const Colour
*src_rgba_line
= (const Colour
*) ((const byte
*) &sd
->data
[si
->sprite_offset
] + bp
->skip_top
* si
->sprite_line_size
);
48 if (read_mode
!= RM_WITH_MARGIN
) {
49 src_rgba_line
+= bp
->skip_left
;
50 src_mv_line
+= bp
->skip_left
;
52 const MapValue
*src_mv
= src_mv_line
;
54 /* Load these variables into register before loop. */
55 const __m128i a_cm
= ALPHA_CONTROL_MASK
;
56 const __m128i pack_low_cm
= PACK_LOW_CONTROL_MASK
;
57 const __m128i tr_nom_base
= TRANSPARENT_NOM_BASE
;
59 for (int y
= bp
->height
; y
!= 0; y
--) {
60 Colour
*dst
= dst_line
;
61 const Colour
*src
= src_rgba_line
+ META_LENGTH
;
62 if (mode
!= BM_TRANSPARENT
) src_mv
= src_mv_line
;
63 uint16
*anim
= anim_line
;
65 if (read_mode
== RM_WITH_MARGIN
) {
66 assert(bt_last
== BT_NONE
); // or you must ensure block type is preserved
67 anim
+= src_rgba_line
[0].data
;
68 src
+= src_rgba_line
[0].data
;
69 dst
+= src_rgba_line
[0].data
;
70 if (mode
!= BM_TRANSPARENT
) src_mv
+= src_rgba_line
[0].data
;
71 const int width_diff
= si
->sprite_width
- bp
->width
;
72 effective_width
= bp
->width
- (int) src_rgba_line
[0].data
;
73 const int delta_diff
= (int) src_rgba_line
[1].data
- width_diff
;
74 const int new_width
= effective_width
- delta_diff
;
75 effective_width
= delta_diff
> 0 ? new_width
: effective_width
;
76 if (effective_width
<= 0) goto next_line
;
82 for (uint x
= (uint
) effective_width
; x
> 0; x
--) {
85 *anim
= *(const uint16
*) src_mv
;
86 *dst
= (src_mv
->m
>= PALETTE_ANIM_START
) ? AdjustBrightneSSE(this->LookupColourInPalette(src_mv
->m
), src_mv
->v
) : src
->data
;
92 if (animated
) src_mv
++;
100 for (uint x
= (uint
) effective_width
/2; x
!= 0; x
--) {
101 uint32 mvX2
= *((uint32
*) const_cast<MapValue
*>(src_mv
));
102 __m128i srcABCD
= _mm_loadl_epi64((const __m128i
*) src
);
103 __m128i dstABCD
= _mm_loadl_epi64((__m128i
*) dst
);
107 const byte m0
= mvX2
;
108 if (m0
>= PALETTE_ANIM_START
) {
109 const Colour c0
= (this->LookupColourInPalette(m0
).data
& 0x00FFFFFF) | (src
[0].data
& 0xFF000000);
110 InsertFirstUint32(AdjustBrightneSSE(c0
, (byte
) (mvX2
>> 8)).data
, srcABCD
);
112 const byte m1
= mvX2
>> 16;
113 if (m1
>= PALETTE_ANIM_START
) {
114 const Colour c1
= (this->LookupColourInPalette(m1
).data
& 0x00FFFFFF) | (src
[1].data
& 0xFF000000);
115 InsertSecondUint32(AdjustBrightneSSE(c1
, (byte
) (mvX2
>> 24)).data
, srcABCD
);
118 /* Update anim buffer. */
119 const byte a0
= src
[0].a
;
120 const byte a1
= src
[1].a
;
124 *(uint32
*) anim
= mvX2
;
125 goto bmno_full_opacity
;
127 anim01
= (uint16
) mvX2
;
128 } else if (a0
== 0) {
130 goto bmno_full_transparency
;
132 if (a1
== 255) anim
[1] = (uint16
) (mvX2
>> 16);
133 goto bmno_alpha_blend
;
137 if (a1
== 255) anim01
|= mvX2
& 0xFFFF0000;
138 *(uint32
*) anim
= anim01
;
140 anim
[0] = (uint16
) anim01
;
143 if (src
[0].a
) anim
[0] = 0;
144 if (src
[1].a
) anim
[1] = 0;
149 srcABCD
= AlphaBlendTwoPixels(srcABCD
, dstABCD
, a_cm
, pack_low_cm
);
151 _mm_storel_epi64((__m128i
*) dst
, srcABCD
);
152 bmno_full_transparency
:
159 if ((bt_last
== BT_NONE
&& effective_width
& 1) || bt_last
== BT_ODD
) {
161 } else if (src
->a
== 255) {
162 *anim
= *(const uint16
*) src_mv
;
163 *dst
= (src_mv
->m
>= PALETTE_ANIM_START
) ? AdjustBrightneSSE(LookupColourInPalette(src_mv
->m
), src_mv
->v
) : *src
;
167 __m128i dstABCD
= _mm_cvtsi32_si128(dst
->data
);
168 if (src_mv
->m
>= PALETTE_ANIM_START
) {
169 Colour colour
= AdjustBrightneSSE(LookupColourInPalette(src_mv
->m
), src_mv
->v
);
171 srcABCD
= _mm_cvtsi32_si128(colour
.data
);
173 srcABCD
= _mm_cvtsi32_si128(src
->data
);
175 dst
->data
= _mm_cvtsi128_si32(AlphaBlendTwoPixels(srcABCD
, dstABCD
, a_cm
, pack_low_cm
));
180 case BM_COLOUR_REMAP
:
181 for (uint x
= (uint
) effective_width
/ 2; x
!= 0; x
--) {
182 uint32 mvX2
= *((uint32
*) const_cast<MapValue
*>(src_mv
));
183 __m128i srcABCD
= _mm_loadl_epi64((const __m128i
*) src
);
184 __m128i dstABCD
= _mm_loadl_epi64((__m128i
*) dst
);
187 const uint m0
= (byte
) mvX2
;
188 const uint r0
= remap
[m0
];
189 const uint m1
= (byte
) (mvX2
>> 16);
190 const uint r1
= remap
[m1
];
191 if (mvX2
& 0x00FF00FF) {
192 #define CMOV_REMAP(m_colour, m_colour_init, m_src, m_m) \
193 /* Written so the compiler uses CMOV. */ \
194 Colour m_colour = m_colour_init; \
196 const Colour srcm = (Colour) (m_src); \
197 const uint m = (byte) (m_m); \
198 const uint r = remap[m]; \
199 const Colour cmap = (this->LookupColourInPalette(r).data & 0x00FFFFFF) | (srcm.data & 0xFF000000); \
200 m_colour = r == 0 ? m_colour : cmap; \
201 m_colour = m != 0 ? m_colour : srcm; \
204 uint64 srcs
= _mm_cvtsi128_si64(srcABCD
);
206 if (animated
) dsts
= _mm_cvtsi128_si64(dstABCD
);
207 uint64 remapped_src
= 0;
208 CMOV_REMAP(c0
, animated
? dsts
: 0, srcs
, mvX2
);
209 remapped_src
= c0
.data
;
210 CMOV_REMAP(c1
, animated
? dsts
>> 32 : 0, srcs
>> 32, mvX2
>> 16);
211 remapped_src
|= (uint64
) c1
.data
<< 32;
212 srcABCD
= _mm_cvtsi64_si128(remapped_src
);
214 Colour remapped_src
[2];
215 CMOV_REMAP(c0
, animated
? _mm_cvtsi128_si32(dstABCD
) : 0, _mm_cvtsi128_si32(srcABCD
), mvX2
);
216 remapped_src
[0] = c0
.data
;
217 CMOV_REMAP(c1
, animated
? dst
[1] : 0, src
[1], mvX2
>> 16);
218 remapped_src
[1] = c1
.data
;
219 srcABCD
= _mm_loadl_epi64((__m128i
*) &remapped_src
);
222 if ((mvX2
& 0xFF00FF00) != 0x80008000) srcABCD
= AdjustBrightnessOfTwoPixels(srcABCD
, mvX2
);
225 /* Update anim buffer. */
227 const byte a0
= src
[0].a
;
228 const byte a1
= src
[1].a
;
229 uint32 anim01
= mvX2
& 0xFF00FF00;
233 *(uint32
*) anim
= anim01
| (r1
<< 16);
234 goto bmcr_full_opacity
;
236 } else if (a0
== 0) {
238 goto bmcr_full_transparency
;
241 anim
[1] = r1
| (anim01
>> 16);
243 goto bmcr_alpha_blend
;
247 if (a1
== 255) anim01
|= r1
<< 16;
248 *(uint32
*) anim
= anim01
;
250 anim
[0] = (uint16
) anim01
;
253 if (src
[0].a
) anim
[0] = 0;
254 if (src
[1].a
) anim
[1] = 0;
259 srcABCD
= AlphaBlendTwoPixels(srcABCD
, dstABCD
, a_cm
, pack_low_cm
);
261 _mm_storel_epi64((__m128i
*) dst
, srcABCD
);
262 bmcr_full_transparency
:
269 if ((bt_last
== BT_NONE
&& effective_width
& 1) || bt_last
== BT_ODD
) {
270 /* In case the m-channel is zero, do not remap this pixel in any way. */
272 if (src
->a
== 0) break;
274 const uint r
= remap
[src_mv
->m
];
275 *anim
= (animated
&& src
->a
== 255) ? r
| ((uint16
) src_mv
->v
<< 8 ) : 0;
277 Colour remapped_colour
= AdjustBrightneSSE(this->LookupColourInPalette(r
), src_mv
->v
);
279 *dst
= remapped_colour
;
281 remapped_colour
.a
= src
->a
;
282 srcABCD
= _mm_cvtsi32_si128(remapped_colour
.data
);
283 goto bmcr_alpha_blend_single
;
288 srcABCD
= _mm_cvtsi32_si128(src
->data
);
290 bmcr_alpha_blend_single
:
291 __m128i dstABCD
= _mm_cvtsi32_si128(dst
->data
);
292 srcABCD
= AlphaBlendTwoPixels(srcABCD
, dstABCD
, a_cm
, pack_low_cm
);
294 dst
->data
= _mm_cvtsi128_si32(srcABCD
);
300 /* Make the current colour a bit more black, so it looks like this image is transparent. */
301 for (uint x
= (uint
) bp
->width
/ 2; x
> 0; x
--) {
302 __m128i srcABCD
= _mm_loadl_epi64((const __m128i
*) src
);
303 __m128i dstABCD
= _mm_loadl_epi64((__m128i
*) dst
);
304 _mm_storel_epi64((__m128i
*) dst
, DarkenTwoPixels(srcABCD
, dstABCD
, a_cm
, tr_nom_base
));
308 if (src
[-2].a
) anim
[-2] = 0;
309 if (src
[-1].a
) anim
[-1] = 0;
312 if ((bt_last
== BT_NONE
&& bp
->width
& 1) || bt_last
== BT_ODD
) {
313 __m128i srcABCD
= _mm_cvtsi32_si128(src
->data
);
314 __m128i dstABCD
= _mm_cvtsi32_si128(dst
->data
);
315 dst
->data
= _mm_cvtsi128_si32(DarkenTwoPixels(srcABCD
, dstABCD
, a_cm
, tr_nom_base
));
316 if (src
[0].a
) anim
[0] = 0;
321 for (uint x
= (uint
) bp
->width
; x
> 0; x
--) {
322 if (src_mv
->m
== 0) {
324 uint8 g
= MakeDark(src
->r
, src
->g
, src
->b
);
325 *dst
= ComposeColourRGBA(g
, g
, g
, src
->a
, *dst
);
329 uint r
= remap
[src_mv
->m
];
330 if (r
!= 0) *dst
= ComposeColourPANoCheck(this->AdjustBrightness(this->LookupColourInPalette(r
), src_mv
->v
), src
->a
, *dst
);
340 for (uint x
= (uint
) bp
->width
; x
> 0; x
--) {
342 *dst
= Colour(0, 0, 0);
354 if (mode
!= BM_TRANSPARENT
) src_mv_line
+= si
->sprite_width
;
355 src_rgba_line
= (const Colour
*) ((const byte
*) src_rgba_line
+ si
->sprite_line_size
);
356 dst_line
+= bp
->pitch
;
357 anim_line
+= this->anim_buf_pitch
;
360 IGNORE_UNINITIALIZED_WARNING_STOP
363 * Draws a sprite to a (screen) buffer. Calls adequate templated function.
365 * @param bp further blitting parameters
366 * @param mode blitter mode
367 * @param zoom zoom level at which we are drawing
369 void Blitter_32bppSSE4_Anim::Draw(Blitter::BlitterParams
*bp
, BlitterMode mode
, ZoomLevel zoom
)
371 const Blitter_32bppSSE_Base::SpriteFlags sprite_flags
= ((const Blitter_32bppSSE_Base::SpriteData
*) bp
->sprite
)->flags
;
375 if (bp
->skip_left
!= 0 || bp
->width
<= MARGIN_NORMAL_THRESHOLD
) {
376 const BlockType bt_last
= (BlockType
) (bp
->width
& 1);
377 if (bt_last
== BT_EVEN
) {
378 if (sprite_flags
& SF_NO_ANIM
) Draw
<BM_NORMAL
, RM_WITH_SKIP
, BT_EVEN
, true, false>(bp
, zoom
);
379 else Draw
<BM_NORMAL
, RM_WITH_SKIP
, BT_EVEN
, true, true>(bp
, zoom
);
381 if (sprite_flags
& SF_NO_ANIM
) Draw
<BM_NORMAL
, RM_WITH_SKIP
, BT_ODD
, true, false>(bp
, zoom
);
382 else Draw
<BM_NORMAL
, RM_WITH_SKIP
, BT_ODD
, true, true>(bp
, zoom
);
386 if (sprite_flags
& SF_TRANSLUCENT
) {
387 if (sprite_flags
& SF_NO_ANIM
) Draw
<BM_NORMAL
, RM_WITH_MARGIN
, BT_NONE
, true, false>(bp
, zoom
);
388 else Draw
<BM_NORMAL
, RM_WITH_MARGIN
, BT_NONE
, true, true>(bp
, zoom
);
390 if (sprite_flags
& SF_NO_ANIM
) Draw
<BM_NORMAL
, RM_WITH_MARGIN
, BT_NONE
, false, false>(bp
, zoom
);
391 else Draw
<BM_NORMAL
, RM_WITH_MARGIN
, BT_NONE
, false, true>(bp
, zoom
);
394 if (sprite_flags
& SF_NO_ANIM
) Draw
<BM_NORMAL
, RM_WITH_MARGIN
, BT_NONE
, true, false>(bp
, zoom
);
395 else Draw
<BM_NORMAL
, RM_WITH_MARGIN
, BT_NONE
, true, true>(bp
, zoom
);
400 case BM_COLOUR_REMAP
:
401 if (sprite_flags
& SF_NO_REMAP
) goto bm_normal
;
402 if (bp
->skip_left
!= 0 || bp
->width
<= MARGIN_REMAP_THRESHOLD
) {
403 if (sprite_flags
& SF_NO_ANIM
) Draw
<BM_COLOUR_REMAP
, RM_WITH_SKIP
, BT_NONE
, true, false>(bp
, zoom
);
404 else Draw
<BM_COLOUR_REMAP
, RM_WITH_SKIP
, BT_NONE
, true, true>(bp
, zoom
);
406 if (sprite_flags
& SF_NO_ANIM
) Draw
<BM_COLOUR_REMAP
, RM_WITH_MARGIN
, BT_NONE
, true, false>(bp
, zoom
);
407 else Draw
<BM_COLOUR_REMAP
, RM_WITH_MARGIN
, BT_NONE
, true, true>(bp
, zoom
);
410 case BM_TRANSPARENT
: Draw
<BM_TRANSPARENT
, RM_NONE
, BT_NONE
, true, true>(bp
, zoom
); return;
411 case BM_CRASH_REMAP
: Draw
<BM_CRASH_REMAP
, RM_NONE
, BT_NONE
, true, true>(bp
, zoom
); return;
412 case BM_BLACK_REMAP
: Draw
<BM_BLACK_REMAP
, RM_NONE
, BT_NONE
, true, true>(bp
, zoom
); return;
416 #endif /* WITH_SSE */