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 uint16
*anim_line
= this->anim_buf
+ this->ScreenToAnimOffset((uint32
*)bp
->dst
) + bp
->top
* this->anim_buf_pitch
+ bp
->left
;
39 int effective_width
= bp
->width
;
41 /* Find where to start reading in the source sprite. */
42 const Blitter_32bppSSE_Base::SpriteData
* const sd
= (const Blitter_32bppSSE_Base::SpriteData
*) bp
->sprite
;
43 const SpriteInfo
* const si
= &sd
->infos
[zoom
];
44 const MapValue
*src_mv_line
= (const MapValue
*)&sd
->data
[si
->mv_offset
] + bp
->skip_top
* si
->sprite_width
;
45 const Colour
*src_rgba_line
= (const Colour
*)((const byte
*)&sd
->data
[si
->sprite_offset
] + bp
->skip_top
* si
->sprite_line_size
);
47 if (read_mode
!= RM_WITH_MARGIN
) {
48 src_rgba_line
+= bp
->skip_left
;
49 src_mv_line
+= bp
->skip_left
;
51 const MapValue
*src_mv
= src_mv_line
;
53 /* Load these variables into register before loop. */
54 const __m128i a_cm
= ALPHA_CONTROL_MASK
;
55 const __m128i pack_low_cm
= PACK_LOW_CONTROL_MASK
;
56 const __m128i tr_nom_base
= TRANSPARENT_NOM_BASE
;
58 for (int y
= bp
->height
; y
!= 0; y
--) {
59 Colour
*dst
= dst_line
;
60 const Colour
*src
= src_rgba_line
+ META_LENGTH
;
61 if (mode
!= BM_TRANSPARENT
) src_mv
= src_mv_line
;
62 uint16
*anim
= anim_line
;
64 if (read_mode
== RM_WITH_MARGIN
) {
65 assert(bt_last
== BT_NONE
); // or you must ensure block type is preserved
66 anim
+= src_rgba_line
[0].data
;
67 src
+= src_rgba_line
[0].data
;
68 dst
+= src_rgba_line
[0].data
;
69 if (mode
!= BM_TRANSPARENT
) src_mv
+= src_rgba_line
[0].data
;
70 const int width_diff
= si
->sprite_width
- bp
->width
;
71 effective_width
= bp
->width
- (int)src_rgba_line
[0].data
;
72 const int delta_diff
= (int)src_rgba_line
[1].data
- width_diff
;
73 const int new_width
= effective_width
- delta_diff
;
74 effective_width
= delta_diff
> 0 ? new_width
: effective_width
;
75 if (effective_width
<= 0) goto next_line
;
81 for (uint x
= (uint
)effective_width
; x
> 0; x
--) {
84 *anim
= *(const uint16
*)src_mv
;
85 *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
;
131 goto bmno_full_transparency
;
134 if (a1
== 255) anim
[1] = (uint16
)(mvX2
>> 16);
135 goto bmno_alpha_blend
;
139 if (a1
== 255) anim01
|= mvX2
& 0xFFFF0000;
140 *(uint32
*)anim
= anim01
;
143 anim
[0] = (uint16
)anim01
;
147 if (src
[0].a
) anim
[0] = 0;
148 if (src
[1].a
) anim
[1] = 0;
153 srcABCD
= AlphaBlendTwoPixels(srcABCD
, dstABCD
, a_cm
, pack_low_cm
);
155 _mm_storel_epi64((__m128i
*) dst
, srcABCD
);
156 bmno_full_transparency
:
163 if ((bt_last
== BT_NONE
&& effective_width
& 1) || bt_last
== BT_ODD
) {
166 else if (src
->a
== 255) {
167 *anim
= *(const uint16
*)src_mv
;
168 *dst
= (src_mv
->m
>= PALETTE_ANIM_START
) ? AdjustBrightneSSE(LookupColourInPalette(src_mv
->m
), src_mv
->v
) : *src
;
173 __m128i dstABCD
= _mm_cvtsi32_si128(dst
->data
);
174 if (src_mv
->m
>= PALETTE_ANIM_START
) {
175 Colour colour
= AdjustBrightneSSE(LookupColourInPalette(src_mv
->m
), src_mv
->v
);
177 srcABCD
= _mm_cvtsi32_si128(colour
.data
);
180 srcABCD
= _mm_cvtsi32_si128(src
->data
);
182 dst
->data
= _mm_cvtsi128_si32(AlphaBlendTwoPixels(srcABCD
, dstABCD
, a_cm
, pack_low_cm
));
187 case BM_COLOUR_REMAP
:
188 for (uint x
= (uint
)effective_width
/ 2; x
!= 0; x
--) {
189 uint32 mvX2
= *((uint32
*) const_cast<MapValue
*>(src_mv
));
190 __m128i srcABCD
= _mm_loadl_epi64((const __m128i
*) src
);
191 __m128i dstABCD
= _mm_loadl_epi64((__m128i
*) dst
);
194 const uint m0
= (byte
)mvX2
;
195 const uint r0
= remap
[m0
];
196 const uint m1
= (byte
)(mvX2
>> 16);
197 const uint r1
= remap
[m1
];
198 if (mvX2
& 0x00FF00FF) {
199 #define CMOV_REMAP(m_colour, m_colour_init, m_src, m_m) \
200 /* Written so the compiler uses CMOV. */ \
201 Colour m_colour = m_colour_init; \
203 const Colour srcm = (Colour) (m_src); \
204 const uint m = (byte) (m_m); \
205 const uint r = remap[m]; \
206 const Colour cmap = (this->LookupColourInPalette(r).data & 0x00FFFFFF) | (srcm.data & 0xFF000000); \
207 m_colour = r == 0 ? m_colour : cmap; \
208 m_colour = m != 0 ? m_colour : srcm; \
211 uint64 srcs
= _mm_cvtsi128_si64(srcABCD
);
213 if (animated
) dsts
= _mm_cvtsi128_si64(dstABCD
);
214 uint64 remapped_src
= 0;
215 CMOV_REMAP(c0
, animated
? dsts
: 0, srcs
, mvX2
);
216 remapped_src
= c0
.data
;
217 CMOV_REMAP(c1
, animated
? dsts
>> 32 : 0, srcs
>> 32, mvX2
>> 16);
218 remapped_src
|= (uint64
)c1
.data
<< 32;
219 srcABCD
= _mm_cvtsi64_si128(remapped_src
);
221 Colour remapped_src
[2];
222 CMOV_REMAP(c0
, animated
? _mm_cvtsi128_si32(dstABCD
) : 0, _mm_cvtsi128_si32(srcABCD
), mvX2
);
223 remapped_src
[0] = c0
.data
;
224 CMOV_REMAP(c1
, animated
? dst
[1] : 0, src
[1], mvX2
>> 16);
225 remapped_src
[1] = c1
.data
;
226 srcABCD
= _mm_loadl_epi64((__m128i
*) &remapped_src
);
229 if ((mvX2
& 0xFF00FF00) != 0x80008000) srcABCD
= AdjustBrightnessOfTwoPixels(srcABCD
, mvX2
);
232 /* Update anim buffer. */
234 const byte a0
= src
[0].a
;
235 const byte a1
= src
[1].a
;
236 uint32 anim01
= mvX2
& 0xFF00FF00;
240 *(uint32
*)anim
= anim01
| (r1
<< 16);
241 goto bmcr_full_opacity
;
246 goto bmcr_full_transparency
;
250 anim
[1] = r1
| (anim01
>> 16);
252 goto bmcr_alpha_blend
;
256 if (a1
== 255) anim01
|= r1
<< 16;
257 *(uint32
*)anim
= anim01
;
260 anim
[0] = (uint16
)anim01
;
264 if (src
[0].a
) anim
[0] = 0;
265 if (src
[1].a
) anim
[1] = 0;
270 srcABCD
= AlphaBlendTwoPixels(srcABCD
, dstABCD
, a_cm
, pack_low_cm
);
272 _mm_storel_epi64((__m128i
*) dst
, srcABCD
);
273 bmcr_full_transparency
:
280 if ((bt_last
== BT_NONE
&& effective_width
& 1) || bt_last
== BT_ODD
) {
281 /* In case the m-channel is zero, do not remap this pixel in any way. */
283 if (src
->a
== 0) break;
285 const uint r
= remap
[src_mv
->m
];
286 *anim
= (animated
&& src
->a
== 255) ? r
| ((uint16
)src_mv
->v
<< 8) : 0;
288 Colour remapped_colour
= AdjustBrightneSSE(this->LookupColourInPalette(r
), src_mv
->v
);
290 *dst
= remapped_colour
;
293 remapped_colour
.a
= src
->a
;
294 srcABCD
= _mm_cvtsi32_si128(remapped_colour
.data
);
295 goto bmcr_alpha_blend_single
;
301 srcABCD
= _mm_cvtsi32_si128(src
->data
);
303 bmcr_alpha_blend_single
:
304 __m128i dstABCD
= _mm_cvtsi32_si128(dst
->data
);
305 srcABCD
= AlphaBlendTwoPixels(srcABCD
, dstABCD
, a_cm
, pack_low_cm
);
307 dst
->data
= _mm_cvtsi128_si32(srcABCD
);
313 /* Make the current colour a bit more black, so it looks like this image is transparent. */
314 for (uint x
= (uint
)bp
->width
/ 2; x
> 0; x
--) {
315 __m128i srcABCD
= _mm_loadl_epi64((const __m128i
*) src
);
316 __m128i dstABCD
= _mm_loadl_epi64((__m128i
*) dst
);
317 _mm_storel_epi64((__m128i
*) dst
, DarkenTwoPixels(srcABCD
, dstABCD
, a_cm
, tr_nom_base
));
321 if (src
[-2].a
) anim
[-2] = 0;
322 if (src
[-1].a
) anim
[-1] = 0;
325 if ((bt_last
== BT_NONE
&& bp
->width
& 1) || bt_last
== BT_ODD
) {
326 __m128i srcABCD
= _mm_cvtsi32_si128(src
->data
);
327 __m128i dstABCD
= _mm_cvtsi32_si128(dst
->data
);
328 dst
->data
= _mm_cvtsi128_si32(DarkenTwoPixels(srcABCD
, dstABCD
, a_cm
, tr_nom_base
));
329 if (src
[0].a
) anim
[0] = 0;
334 for (uint x
= (uint
)bp
->width
; x
> 0; x
--) {
335 if (src_mv
->m
== 0) {
337 uint8 g
= MakeDark(src
->r
, src
->g
, src
->b
);
338 *dst
= ComposeColourRGBA(g
, g
, g
, src
->a
, *dst
);
343 uint r
= remap
[src_mv
->m
];
344 if (r
!= 0) *dst
= ComposeColourPANoCheck(this->AdjustBrightness(this->LookupColourInPalette(r
), src_mv
->v
), src
->a
, *dst
);
354 for (uint x
= (uint
)bp
->width
; x
> 0; x
--) {
356 *dst
= Colour(0, 0, 0);
368 if (mode
!= BM_TRANSPARENT
) src_mv_line
+= si
->sprite_width
;
369 src_rgba_line
= (const Colour
*)((const byte
*)src_rgba_line
+ si
->sprite_line_size
);
370 dst_line
+= bp
->pitch
;
371 anim_line
+= this->anim_buf_pitch
;
374 IGNORE_UNINITIALIZED_WARNING_STOP
377 * Draws a sprite to a (screen) buffer. Calls adequate templated function.
379 * @param bp further blitting parameters
380 * @param mode blitter mode
381 * @param zoom zoom level at which we are drawing
383 void Blitter_32bppSSE4_Anim::Draw(Blitter::BlitterParams
*bp
, BlitterMode mode
, ZoomLevel zoom
)
385 const BlitterSpriteFlags sprite_flags
= ((const Blitter_32bppSSE_Base::SpriteData
*) bp
->sprite
)->flags
;
389 if (bp
->skip_left
!= 0 || bp
->width
<= MARGIN_NORMAL_THRESHOLD
) {
390 const BlockType bt_last
= (BlockType
)(bp
->width
& 1);
391 if (bt_last
== BT_EVEN
) {
392 if (sprite_flags
& SF_NO_ANIM
) Draw
<BM_NORMAL
, RM_WITH_SKIP
, BT_EVEN
, true, false>(bp
, zoom
);
393 else Draw
<BM_NORMAL
, RM_WITH_SKIP
, BT_EVEN
, true, true>(bp
, zoom
);
396 if (sprite_flags
& SF_NO_ANIM
) Draw
<BM_NORMAL
, RM_WITH_SKIP
, BT_ODD
, true, false>(bp
, zoom
);
397 else Draw
<BM_NORMAL
, RM_WITH_SKIP
, BT_ODD
, true, true>(bp
, zoom
);
402 if (sprite_flags
& SF_TRANSLUCENT
) {
403 if (sprite_flags
& SF_NO_ANIM
) Draw
<BM_NORMAL
, RM_WITH_MARGIN
, BT_NONE
, true, false>(bp
, zoom
);
404 else Draw
<BM_NORMAL
, RM_WITH_MARGIN
, BT_NONE
, true, true>(bp
, zoom
);
407 if (sprite_flags
& SF_NO_ANIM
) Draw
<BM_NORMAL
, RM_WITH_MARGIN
, BT_NONE
, false, false>(bp
, zoom
);
408 else Draw
<BM_NORMAL
, RM_WITH_MARGIN
, BT_NONE
, false, true>(bp
, zoom
);
411 if (sprite_flags
& SF_NO_ANIM
) Draw
<BM_NORMAL
, RM_WITH_MARGIN
, BT_NONE
, true, false>(bp
, zoom
);
412 else Draw
<BM_NORMAL
, RM_WITH_MARGIN
, BT_NONE
, true, true>(bp
, zoom
);
417 case BM_COLOUR_REMAP
:
418 if (sprite_flags
& SF_NO_REMAP
) goto bm_normal
;
419 if (bp
->skip_left
!= 0 || bp
->width
<= MARGIN_REMAP_THRESHOLD
) {
420 if (sprite_flags
& SF_NO_ANIM
) Draw
<BM_COLOUR_REMAP
, RM_WITH_SKIP
, BT_NONE
, true, false>(bp
, zoom
);
421 else Draw
<BM_COLOUR_REMAP
, RM_WITH_SKIP
, BT_NONE
, true, true>(bp
, zoom
);
424 if (sprite_flags
& SF_NO_ANIM
) Draw
<BM_COLOUR_REMAP
, RM_WITH_MARGIN
, BT_NONE
, true, false>(bp
, zoom
);
425 else Draw
<BM_COLOUR_REMAP
, RM_WITH_MARGIN
, BT_NONE
, true, true>(bp
, zoom
);
428 case BM_TRANSPARENT
: Draw
<BM_TRANSPARENT
, RM_NONE
, BT_NONE
, true, true>(bp
, zoom
); return;
429 case BM_CRASH_REMAP
: Draw
<BM_CRASH_REMAP
, RM_NONE
, BT_NONE
, true, true>(bp
, zoom
); return;
430 case BM_BLACK_REMAP
: Draw
<BM_BLACK_REMAP
, RM_NONE
, BT_NONE
, true, true>(bp
, zoom
); return;
434 #endif /* WITH_SSE */