26 #if defined(__SSE2__) || \
28 (defined(_MSC_VER) && defined(_M_IX86_FP) && _M_IX86_FP >= 2)
30 #include <emmintrin.h>
35 #elif defined(__ARM_NEON) || defined(__aarch64__)
55 void mask_surface_simd_sse2(uint32_t* surf_ptr,
const uint32_t* mask_ptr,
const std::size_t pixel_count,
bool& empty)
60 const __m128i ALPHA_MASK = _mm_set1_epi32(0xFF000000);
64 __m128i alpha_acc = _mm_setzero_si128();
67 for(std::size_t offset = 0; offset < pixel_count; offset += 4) {
69 const __m128i
s = _mm_loadu_si128(
reinterpret_cast<const __m128i*
>(surf_ptr + offset));
70 const __m128i m = _mm_loadu_si128(
reinterpret_cast<const __m128i*
>(mask_ptr + offset));
74 const __m128i minv = _mm_min_epu8(
s, m);
77 const __m128i newa = _mm_and_si128(minv, ALPHA_MASK);
81 const __m128i res = _mm_or_si128(_mm_andnot_si128(ALPHA_MASK,
s), newa);
84 _mm_storeu_si128(
reinterpret_cast<__m128i*
>(surf_ptr + offset), res);
88 alpha_acc = _mm_or_si128(alpha_acc, newa);
94 if(_mm_movemask_epi8(_mm_cmpeq_epi8(alpha_acc, _mm_setzero_si128())) != 0xFFFF) {
99 void apply_surface_opacity_simd_sse2(uint32_t* surf_ptr,
const std::size_t pixel_count,
const uint8_t alpha_mod_scalar)
101 const __m128i ALPHA_MASK = _mm_set1_epi32(0xFF000000);
104 const __m128i MOD_VEC = _mm_set1_epi32(0x10100 * alpha_mod_scalar);
105 const __m128i C128 = _mm_set1_epi32(0x800000);
107 for(std::size_t offset = 0; offset < pixel_count; offset += 4) {
108 const __m128i
p = _mm_loadu_si128(
reinterpret_cast<const __m128i*
>(surf_ptr + offset));
111 if (_mm_movemask_epi8(_mm_cmpeq_epi8(_mm_and_si128(
p, ALPHA_MASK), _mm_setzero_si128())) == 0xFFFF)
continue;
114 const __m128i a = _mm_srli_epi32(
p, 24);
117 const __m128i m0 = _mm_mul_epu32(a, MOD_VEC);
118 const __m128i m1 = _mm_mul_epu32(_mm_srli_si128(a, 4), MOD_VEC);
121 const __m128i merged = _mm_or_si128(m0, _mm_slli_si128(m1, 4));
124 const __m128i new_alpha = _mm_and_si128(_mm_add_epi32(merged, C128), ALPHA_MASK);
127 _mm_storeu_si128(
reinterpret_cast<__m128i*
>(surf_ptr + offset),
128 _mm_or_si128(_mm_andnot_si128(ALPHA_MASK,
p), new_alpha));
132 void adjust_surface_color_simd_sse2(uint32_t* surf_ptr,
const std::size_t pixel_count,
int r,
int g,
int b)
134 r = std::clamp(r, -255, 255);
135 g = std::clamp(
g, -255, 255);
136 b = std::clamp(
b, -255, 255);
139 uint32_t add_mask = 0, sub_mask = 0;
141 if(r > 0) add_mask |= (
static_cast<uint8_t
>(r) << 16);
142 else sub_mask |= (
static_cast<uint8_t
>(std::abs(r)) << 16);
143 if(
g > 0) add_mask |= (
static_cast<uint8_t
>(
g) << 8);
144 else sub_mask |= (
static_cast<uint8_t
>(std::abs(
g)) << 8);
145 if(
b > 0) add_mask |=
static_cast<uint8_t
>(
b);
146 else sub_mask |=
static_cast<uint8_t
>(std::abs(
b));
148 const __m128i ADD_VEC = _mm_set1_epi32(add_mask);
149 const __m128i SUB_VEC = _mm_set1_epi32(sub_mask);
151 for(std::size_t offset = 0; offset < pixel_count; offset += 4) {
152 __m128i
p = _mm_loadu_si128(
reinterpret_cast<const __m128i*
>(surf_ptr + offset));
155 p = _mm_subs_epu8(
p, SUB_VEC);
156 p = _mm_adds_epu8(
p, ADD_VEC);
158 _mm_storeu_si128(
reinterpret_cast<__m128i*
>(surf_ptr + offset),
p);
162 void flip_image_simd_sse2(uint32_t* pixels,
const std::size_t width,
const std::size_t height)
164 const std::size_t half_width = width / 2;
167 auto reverse_sse2 = [](__m128i v) -> __m128i {
168 return _mm_shuffle_epi32(v, _MM_SHUFFLE(0, 1, 2, 3));
171 for(std::size_t y = 0; y < height; ++y) {
172 uint32_t* row = pixels + y * width;
175 for(; x + 4 <= half_width; x += 4) {
176 const std::size_t r_idx = width - x - 4;
178 __m128i l = _mm_loadu_si128(
reinterpret_cast<const __m128i*
>(row + x));
179 __m128i r = _mm_loadu_si128(
reinterpret_cast<const __m128i*
>(row + r_idx));
181 _mm_storeu_si128(
reinterpret_cast<__m128i*
>(row + x), reverse_sse2(r));
182 _mm_storeu_si128(
reinterpret_cast<__m128i*
>(row + r_idx), reverse_sse2(l));
195 void mask_surface_simd_neon(uint32_t* __restrict surf_ptr,
const uint32_t* __restrict mask_ptr,
const std::size_t pixel_count,
bool& empty)
198 const uint32x4_t ALPHA_MASK = vdupq_n_u32(0xFF000000);
199 uint32x4_t alpha_acc = vdupq_n_u32(0);
201 for(std::size_t offset = 0; offset < pixel_count; offset += 4) {
203 const uint32x4_t
s = vld1q_u32(surf_ptr + offset);
204 const uint32x4_t m = vld1q_u32(mask_ptr + offset);
207 const uint32x4_t min_val = vminq_u32(
s, m);
210 alpha_acc = vorrq_u32(alpha_acc, min_val);
214 const uint32x4_t res = vbslq_u32(ALPHA_MASK, min_val,
s);
215 vst1q_u32(surf_ptr + offset, res);
220 alpha_acc = vandq_u32(alpha_acc, ALPHA_MASK);
221 uint32x2_t combined_halves = vorr_u32(vget_low_u32(alpha_acc), vget_high_u32(alpha_acc));
222 combined_halves = vpmax_u32(combined_halves, combined_halves);
223 if(vget_lane_u32(combined_halves, 0) > 0) {
228 void apply_surface_opacity_simd_neon(uint32_t* __restrict surf_ptr,
const std::size_t pixel_count,
const uint8_t alpha_mod_scalar)
230 const uint32x4_t ALPHA_MASK = vdupq_n_u32(0xFF000000);
231 const uint32x4_t MOD_VEC = vdupq_n_u32(0x10100 * alpha_mod_scalar);
232 const uint32x4_t C128 = vdupq_n_u32(0x800000);
234 for(std::size_t offset = 0; offset < pixel_count; offset += 4) {
235 uint32x4_t
p = vld1q_u32(surf_ptr + offset);
237 uint32x4_t alpha_check = vandq_u32(
p, ALPHA_MASK);
238 if (vgetq_lane_u64(vreinterpretq_u64_u32(alpha_check), 0) == 0 &&
239 vgetq_lane_u64(vreinterpretq_u64_u32(alpha_check), 1) == 0)
continue;
241 const uint32x4_t alpha_32 = vshrq_n_u32(
p, 24);
242 const uint32x4_t new_alpha_shifted = vaddq_u32(vmulq_u32(alpha_32, MOD_VEC), C128);
243 const uint32x4_t res = vbslq_u32(ALPHA_MASK, new_alpha_shifted,
p);
245 vst1q_u32(surf_ptr + offset, res);
249 void adjust_surface_color_simd_neon(uint32_t* __restrict surf_ptr,
const std::size_t pixel_count,
int r,
int g,
int b)
251 r = std::clamp(r, -255, 255);
252 g = std::clamp(
g, -255, 255);
253 b = std::clamp(
b, -255, 255);
255 uint32_t add_pkd = ((r > 0 ? r : 0) << 16) | ((
g > 0 ?
g : 0) << 8) | (
b > 0 ?
b : 0);
256 uint32_t sub_pkd = ((r < 0 ? -r : 0) << 16) | ((
g < 0 ? -
g : 0) << 8) | (
b < 0 ? -
b : 0);
258 const uint8x16_t ADD_VEC = vreinterpretq_u8_u32(vdupq_n_u32(add_pkd));
259 const uint8x16_t SUB_VEC = vreinterpretq_u8_u32(vdupq_n_u32(sub_pkd));
261 for(std::size_t offset = 0; offset < pixel_count; offset += 4) {
263 uint8x16_t
p = vld1q_u8(
reinterpret_cast<uint8_t*
>(surf_ptr + offset));
264 p = vqaddq_u8(vqsubq_u8(
p, SUB_VEC), ADD_VEC);
265 vst1q_u8(
reinterpret_cast<uint8_t*
>(surf_ptr + offset),
p);
269 void flip_image_simd_neon(uint32_t* __restrict pixels,
const std::size_t width,
const std::size_t height)
271 const std::size_t half_width = width / 2;
274 auto reverse_vector_u32 = [](uint32x4_t v) -> uint32x4_t {
276 return vcombine_u32(vget_high_u32(v), vget_low_u32(v));
279 for(std::size_t y = 0; y < height; ++y) {
280 uint32_t* row = pixels + y * width;
283 for(; x + 4 <= half_width; x += 4) {
284 const std::size_t r_idx = width - x - 4;
285 const uint32x4_t L = vld1q_u32(row + x);
286 const uint32x4_t R = vld1q_u32(row + r_idx);
288 vst1q_u32(row + x, reverse_vector_u32(R));
289 vst1q_u32(row + r_idx, reverse_vector_u32(L));
306 const std::size_t simd_count = (
surf.
area() / 4) * 4;
310 mask_surface_simd_sse2(lock.
pixels(), mlock.
pixels(), simd_count, empty);
313 mask_surface_simd_neon(lock.
pixels(), mlock.
pixels(), simd_count, empty);
323 const std::size_t simd_count = (
surf.
area() / 4) * 4;
327 apply_surface_opacity_simd_sse2(lock.
pixels(), simd_count, alpha_mod);
330 apply_surface_opacity_simd_neon(lock.
pixels(), simd_count, alpha_mod);
340 const std::size_t simd_count = (
surf.
area() / 4) * 4;
344 adjust_surface_color_simd_sse2(lock.
pixels(), simd_count, r,
g,
b);
347 adjust_surface_color_simd_neon(lock.
pixels(), simd_count, r,
g,
b);
357 const std::size_t simd_cols = (
surf->w / 2) / 4 * 4;
Helper class for pinning SDL surfaces into memory.
std::size_t area() const
Total area of the surface in square pixels.
constexpr std::size_t SIMD_THRESHOLD
Minimum number of pixels required to trigger SIMD optimizations.
static map_location::direction s
std::size_t flip_image_simd(surface &surf)
Flips each row of pixels (horizontal mirror) using SIMD.
std::size_t adjust_surface_color_simd(surface &surf, const int r, const int g, const int b)
Adjusts the color channels of a surface using saturated SIMD arithmetic.
std::size_t apply_surface_opacity_simd(surface &surf, const uint8_t alpha_mod)
Applies an alpha modification to the whole surface using SIMD.
std::size_t mask_surface_simd(surface &surf, const surface &mask, bool &empty)
Modifies the alpha channel of the surface pixels based on the mask.
SIMD-accelerated helper functions for image manipulation.