filters/intel/layer_avx2.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // AviSynth+. Copyright 2026- AviSynth+ Project | ||
| 2 | // https://avs-plus.net | ||
| 3 | // http://avisynth.nl | ||
| 4 | // This program is free software; you can redistribute it and/or modify | ||
| 5 | // it under the terms of the GNU General Public License as published by | ||
| 6 | // the Free Software Foundation; either version 2 of the License, or | ||
| 7 | // (at your option) any later version. | ||
| 8 | // | ||
| 9 | // This program is distributed in the hope that it will be useful, | ||
| 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 12 | // GNU General Public License for more details. | ||
| 13 | // | ||
| 14 | // You should have received a copy of the GNU General Public License | ||
| 15 | // along with this program; if not, write to the Free Software | ||
| 16 | // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit | ||
| 17 | // http://www.gnu.org/copyleft/gpl.html . | ||
| 18 | // | ||
| 19 | // Linking Avisynth statically or dynamically with other modules is making a | ||
| 20 | // combined work based on Avisynth. Thus, the terms and conditions of the GNU | ||
| 21 | // General Public License cover the whole combination. | ||
| 22 | // | ||
| 23 | // As a special exception, the copyright holders of Avisynth give you | ||
| 24 | // permission to link Avisynth with independent modules that communicate with | ||
| 25 | // Avisynth solely through the interfaces defined in avisynth.h, regardless of the license | ||
| 26 | // terms of these independent modules, and to copy and distribute the | ||
| 27 | // resulting combined work under terms of your choice, provided that | ||
| 28 | // every copy of the combined work is accompanied by a complete copy of | ||
| 29 | // the source code of Avisynth (the version of Avisynth used to produce the | ||
| 30 | // combined work), being distributed under the terms of the GNU General | ||
| 31 | // Public License plus this exception. An independent module is a module | ||
| 32 | // which is not derived from or based on Avisynth, such as 3rd-party filters, | ||
| 33 | // import and export plugins, or graphical user interfaces. | ||
| 34 | |||
| 35 | #include "../layer.h" | ||
| 36 | #include "layer_avx2.h" | ||
| 37 | |||
| 38 | #if defined(_MSC_VER) | ||
| 39 | #include <intrin.h> // MSVC | ||
| 40 | #else | ||
| 41 | #include <x86intrin.h> // GCC/MinGW/Clang/LLVM | ||
| 42 | #endif | ||
| 43 | #include <immintrin.h> | ||
| 44 | |||
| 45 | #include <cstdint> | ||
| 46 | #include <vector> | ||
| 47 | |||
| 48 | #include <avs/minmax.h> | ||
| 49 | #include <avs/alignment.h> | ||
| 50 | #include "../core/internal.h" | ||
| 51 | |||
| 52 | // masked_merge_avx2_impl<maskMode> — implementation-include, no guards. | ||
| 53 | #include "../overlay/intel/masked_merge_avx2_impl.hpp" | ||
| 54 | #include "../overlay/intel/blend_common_avx2.h" | ||
| 55 | |||
| 56 | #include "../convert/convert_planar.h" | ||
| 57 | #include <algorithm> | ||
| 58 | #include <vector> | ||
| 59 | #include <type_traits> | ||
| 60 | |||
| 61 | // Mostly RGB32 stuff, unaligned addresses, pixels grouped by 4 | ||
| 62 | |||
| 63 | static AVS_FORCEINLINE __m128i mask_core_avx2(__m128i& src, __m128i& alpha, __m128i& not_alpha_mask, __m128i& zero, __m128i& matrix, __m128i& round_mask) { | ||
| 64 | 26 | __m128i not_alpha = _mm_and_si128(src, not_alpha_mask); | |
| 65 | |||
| 66 | 13 | __m128i pixel0 = _mm_unpacklo_epi8(alpha, zero); | |
| 67 | 13 | __m128i pixel1 = _mm_unpackhi_epi8(alpha, zero); | |
| 68 | |||
| 69 | 13 | pixel0 = _mm_madd_epi16(pixel0, matrix); | |
| 70 | 26 | pixel1 = _mm_madd_epi16(pixel1, matrix); | |
| 71 | |||
| 72 | 39 | __m128i tmp = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(pixel0), _mm_castsi128_ps(pixel1), _MM_SHUFFLE(3, 1, 3, 1))); | |
| 73 | 39 | __m128i tmp2 = _mm_castps_si128(_mm_shuffle_ps(_mm_castsi128_ps(pixel0), _mm_castsi128_ps(pixel1), _MM_SHUFFLE(2, 0, 2, 0))); | |
| 74 | |||
| 75 | 13 | tmp = _mm_add_epi32(tmp, tmp2); | |
| 76 | 26 | tmp = _mm_add_epi32(tmp, round_mask); | |
| 77 | 13 | tmp = _mm_srli_epi32(tmp, 15); | |
| 78 | 13 | __m128i result_alpha = _mm_slli_epi32(tmp, 24); | |
| 79 | |||
| 80 | 13 | return _mm_or_si128(result_alpha, not_alpha); | |
| 81 | } | ||
| 82 | |||
| 83 | // called for RGB32 | ||
| 84 | 2 | void mask_avx2(BYTE* srcp, const BYTE* alphap, int src_pitch, int alpha_pitch, size_t width, size_t height) { | |
| 85 | 2 | __m128i matrix = _mm_set_epi16(0, cyr, cyg, cyb, 0, cyr, cyg, cyb); | |
| 86 | 2 | __m128i zero = _mm_setzero_si128(); | |
| 87 | 2 | __m128i round_mask = _mm_set1_epi32(16384); | |
| 88 | 2 | __m128i not_alpha_mask = _mm_set1_epi32(0x00FFFFFF); | |
| 89 | |||
| 90 | 2 | size_t width_bytes = width * 4; | |
| 91 | 2 | size_t width_mod16 = width_bytes / 16 * 16; | |
| 92 | |||
| 93 |
2/2✓ Branch 99 → 15 taken 5 times.
✓ Branch 99 → 100 taken 2 times.
|
7 | for (size_t y = 0; y < height; ++y) { |
| 94 |
2/2✓ Branch 56 → 16 taken 8 times.
✓ Branch 56 → 57 taken 5 times.
|
13 | for (size_t x = 0; x < width_mod16; x += 16) { |
| 95 | 8 | __m128i src = _mm_load_si128(reinterpret_cast<const __m128i*>(srcp + x)); | |
| 96 | 16 | __m128i alpha = _mm_load_si128(reinterpret_cast<const __m128i*>(alphap + x)); | |
| 97 | 8 | __m128i result = mask_core_avx2(src, alpha, not_alpha_mask, zero, matrix, round_mask); | |
| 98 | |||
| 99 | 8 | _mm_store_si128(reinterpret_cast<__m128i*>(srcp + x), result); | |
| 100 | } | ||
| 101 | |||
| 102 |
1/2✓ Branch 57 → 58 taken 5 times.
✗ Branch 57 → 98 not taken.
|
5 | if (width_mod16 < width_bytes) { |
| 103 | 5 | __m128i src = _mm_loadu_si128(reinterpret_cast<const __m128i*>(srcp + width_bytes - 16)); | |
| 104 | 10 | __m128i alpha = _mm_loadu_si128(reinterpret_cast<const __m128i*>(alphap + width_bytes - 16)); | |
| 105 | 5 | __m128i result = mask_core_avx2(src, alpha, not_alpha_mask, zero, matrix, round_mask); | |
| 106 | |||
| 107 | 5 | _mm_storeu_si128(reinterpret_cast<__m128i*>(srcp + width_bytes - 16), result); | |
| 108 | } | ||
| 109 | |||
| 110 | 5 | srcp += src_pitch; | |
| 111 | 5 | alphap += alpha_pitch; | |
| 112 | } | ||
| 113 | 2 | } | |
| 114 | |||
| 115 | 2 | void colorkeymask_avx2(BYTE* pf, int pitch, int color, int height, int width, int tolB, int tolG, int tolR) { | |
| 116 | 2 | unsigned int t = 0xFF000000 | (tolR << 16) | (tolG << 8) | tolB; | |
| 117 | 4 | __m128i tolerance = _mm_set1_epi32(t); | |
| 118 | 2 | __m128i colorv = _mm_set1_epi32(color); | |
| 119 | 2 | __m128i zero = _mm_setzero_si128(); | |
| 120 | |||
| 121 | 2 | BYTE* endp = pf + pitch * height; | |
| 122 | |||
| 123 |
2/2✓ Branch 31 → 13 taken 20 times.
✓ Branch 31 → 32 taken 2 times.
|
22 | while (pf < endp) |
| 124 | { | ||
| 125 | 20 | __m128i src = _mm_load_si128(reinterpret_cast<const __m128i*>(pf)); | |
| 126 | 20 | __m128i gt = _mm_subs_epu8(colorv, src); | |
| 127 | 20 | __m128i lt = _mm_subs_epu8(src, colorv); | |
| 128 | 20 | __m128i absdiff = _mm_or_si128(gt, lt); //abs(color - src) | |
| 129 | |||
| 130 | 20 | __m128i not_passed = _mm_subs_epu8(absdiff, tolerance); | |
| 131 | 20 | __m128i passed = _mm_cmpeq_epi32(not_passed, zero); | |
| 132 | 20 | passed = _mm_slli_epi32(passed, 24); | |
| 133 | 20 | __m128i result = _mm_andnot_si128(passed, src); | |
| 134 | |||
| 135 | _mm_store_si128(reinterpret_cast<__m128i*>(pf), result); | ||
| 136 | |||
| 137 | 20 | pf += 16; | |
| 138 | } | ||
| 139 | 2 | } | |
| 140 | |||
| 141 | // by 4 bytes, when rgba mask can be separate FF bytes, for plane FF FF FF FF | ||
| 142 | // to simple, even C is identical speed | ||
| 143 | 2 | void invert_frame_inplace_avx2(BYTE* frame, int pitch, int width, int height, int mask) { | |
| 144 | 2 | __m256i maskv = _mm256_set1_epi32(mask); | |
| 145 | |||
| 146 | 2 | BYTE* endp = frame + pitch * height; | |
| 147 | // geee, no y loop | ||
| 148 |
2/2✓ Branch 11 → 5 taken 29 times.
✓ Branch 11 → 12 taken 2 times.
|
31 | while (frame < endp) { |
| 149 | 29 | __m256i src = _mm256_load_si256(reinterpret_cast<const __m256i*>(frame)); | |
| 150 | 29 | __m256i inv = _mm256_xor_si256(src, maskv); | |
| 151 | _mm256_store_si256(reinterpret_cast<__m256i*>(frame), inv); | ||
| 152 | 29 | frame += 32; | |
| 153 | } | ||
| 154 | 2 | } | |
| 155 | |||
| 156 | // to simple, even C is identical speed | ||
| 157 | 2 | void invert_frame_uint16_inplace_avx2(BYTE* frame, int pitch, int width, int height, uint64_t mask64) { | |
| 158 | 4 | __m256i maskv = _mm256_set_epi32( | |
| 159 | 2 | (uint32_t)(mask64 >> 32), (uint32_t)mask64, (uint32_t)(mask64 >> 32), (uint32_t)mask64, | |
| 160 | 2 | (uint32_t)(mask64 >> 32), (uint32_t)mask64, (uint32_t)(mask64 >> 32), (uint32_t)mask64); | |
| 161 | |||
| 162 | 2 | BYTE* endp = frame + pitch * height; | |
| 163 | // geee, no y loop | ||
| 164 |
2/2✓ Branch 11 → 5 taken 29 times.
✓ Branch 11 → 12 taken 2 times.
|
31 | while (frame < endp) { |
| 165 | 29 | __m256i src = _mm256_load_si256(reinterpret_cast<const __m256i*>(frame)); | |
| 166 | 29 | __m256i inv = _mm256_xor_si256(src, maskv); | |
| 167 | _mm256_store_si256(reinterpret_cast<__m256i*>(frame), inv); | ||
| 168 | 29 | frame += 32; | |
| 169 | } | ||
| 170 | 2 | } | |
| 171 | |||
| 172 | // --------------------------------------------------------------------------- | ||
| 173 | // invert_plane_avx2_u8: 8-bit luma/chroma inversion. | ||
| 174 | // Luma: result = 255 - src (XOR with 0xFF) | ||
| 175 | // Chroma: result = min(256 - src, 255) | ||
| 176 | // = XOR(saturating_sub(src, 1), 0xFF) | ||
| 177 | // reason: 256-src overflows uint8 for src=0 → clamp to 255; | ||
| 178 | // all other src values: 256-src ∈ [1,255] fits exactly. | ||
| 179 | // --------------------------------------------------------------------------- | ||
| 180 | template<bool chroma> | ||
| 181 | 4 | void invert_plane_avx2_u8(uint8_t* dstp, const uint8_t* srcp, int src_pitch, int dst_pitch, int width, int height, int /*bits_per_pixel*/) | |
| 182 | { | ||
| 183 | 4 | const __m256i v_ff = _mm256_set1_epi8((char)0xFF); | |
| 184 | 4 | const __m256i v_one = _mm256_set1_epi8(1); | |
| 185 | |||
| 186 |
4/4void invert_plane_avx2_u8<false>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 20 → 11 taken 6 times.
✓ Branch 20 → 21 taken 2 times.
void invert_plane_avx2_u8<true>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 22 → 11 taken 6 times.
✓ Branch 22 → 23 taken 2 times.
|
16 | for (int y = 0; y < height; ++y) { |
| 187 |
4/4void invert_plane_avx2_u8<false>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 18 → 12 taken 9 times.
✓ Branch 18 → 19 taken 6 times.
void invert_plane_avx2_u8<true>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 20 → 12 taken 9 times.
✓ Branch 20 → 21 taken 6 times.
|
30 | for (int x = 0; x < width; x += 32) { |
| 188 | 36 | __m256i s = _mm256_load_si256(reinterpret_cast<const __m256i*>(srcp + x)); | |
| 189 | __m256i r; | ||
| 190 | if constexpr (chroma) | ||
| 191 | 9 | r = _mm256_xor_si256(_mm256_subs_epu8(s, v_one), v_ff); | |
| 192 | else | ||
| 193 | 9 | r = _mm256_xor_si256(s, v_ff); | |
| 194 | 18 | _mm256_store_si256(reinterpret_cast<__m256i*>(dstp + x), r); | |
| 195 | } | ||
| 196 | 12 | srcp += src_pitch; | |
| 197 | 12 | dstp += dst_pitch; | |
| 198 | } | ||
| 199 | 4 | } | |
| 200 | |||
| 201 | // --------------------------------------------------------------------------- | ||
| 202 | // invert_plane_avx2_u16: 10/12/14/16-bit luma/chroma inversion. | ||
| 203 | // Luma: result = max_pixel_value - src | ||
| 204 | // = XOR(src, v_max) (works because max = (1<<bpp)-1 = all ones in bpp bits) | ||
| 205 | // Chroma: result = min(2*half - src, max) = min((1<<bpp) - src, max) | ||
| 206 | // = XOR(saturating_sub(src, 1), v_max) | ||
| 207 | // same overflow-at-zero reasoning as u8; saturating_sub_u16 exists in AVX2. | ||
| 208 | // --------------------------------------------------------------------------- | ||
| 209 | template<bool chroma> | ||
| 210 | 10 | void invert_plane_avx2_u16(uint8_t* dstp, const uint8_t* srcp, int src_pitch, int dst_pitch, int width, int height, int bits_per_pixel) | |
| 211 | { | ||
| 212 | 10 | const int max_pixel_value = (1 << bits_per_pixel) - 1; | |
| 213 | 20 | const __m256i v_max = _mm256_set1_epi16((short)max_pixel_value); | |
| 214 | 10 | const __m256i v_one = _mm256_set1_epi16(1); | |
| 215 | |||
| 216 |
4/4void invert_plane_avx2_u16<false>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 20 → 11 taken 17 times.
✓ Branch 20 → 21 taken 5 times.
void invert_plane_avx2_u16<true>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 22 → 11 taken 17 times.
✓ Branch 22 → 23 taken 5 times.
|
44 | for (int y = 0; y < height; ++y) { |
| 217 | 34 | const uint16_t* s16 = reinterpret_cast<const uint16_t*>(srcp); | |
| 218 | 34 | uint16_t* d16 = reinterpret_cast< uint16_t*>(dstp); | |
| 219 | |||
| 220 |
4/4void invert_plane_avx2_u16<false>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 18 → 12 taken 34 times.
✓ Branch 18 → 19 taken 17 times.
void invert_plane_avx2_u16<true>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 20 → 12 taken 34 times.
✓ Branch 20 → 21 taken 17 times.
|
102 | for (int x = 0; x < width; x += 16) { |
| 221 | 136 | __m256i s = _mm256_load_si256(reinterpret_cast<const __m256i*>(s16 + x)); | |
| 222 | __m256i r; | ||
| 223 | if constexpr (chroma) | ||
| 224 | 34 | r = _mm256_xor_si256(_mm256_subs_epu16(s, v_one), v_max); | |
| 225 | else | ||
| 226 | 34 | r = _mm256_xor_si256(s, v_max); | |
| 227 | 68 | _mm256_store_si256(reinterpret_cast<__m256i*>(d16 + x), r); | |
| 228 | } | ||
| 229 | 34 | srcp += src_pitch; | |
| 230 | 34 | dstp += dst_pitch; | |
| 231 | } | ||
| 232 | 10 | } | |
| 233 | |||
| 234 | // --------------------------------------------------------------------------- | ||
| 235 | // invert_plane_avx2_f32: 32-bit float luma/chroma inversion. | ||
| 236 | // Luma: result = 1.0f - src (sub from 1.0) | ||
| 237 | // Chroma: result = -src (flip sign bit via XOR) | ||
| 238 | // --------------------------------------------------------------------------- | ||
| 239 | template<bool chroma> | ||
| 240 | 4 | void invert_plane_avx2_f32(uint8_t* dstp, const uint8_t* srcp, int src_pitch, int dst_pitch, int width, int height, int /*bits_per_pixel*/) | |
| 241 | { | ||
| 242 | 4 | const __m256 v_one = _mm256_set1_ps(1.0f); | |
| 243 | 4 | const __m256 v_sign = _mm256_set1_ps(-0.0f); // 0x80000000 — sign-flip mask | |
| 244 | |||
| 245 |
4/4void invert_plane_avx2_f32<false>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 16 → 7 taken 8 times.
✓ Branch 16 → 17 taken 2 times.
void invert_plane_avx2_f32<true>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 16 → 7 taken 8 times.
✓ Branch 16 → 17 taken 2 times.
|
20 | for (int y = 0; y < height; ++y) { |
| 246 | 16 | const float* sf = reinterpret_cast<const float*>(srcp); | |
| 247 | 16 | float* df = reinterpret_cast< float*>(dstp); | |
| 248 | |||
| 249 |
4/4void invert_plane_avx2_f32<false>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 14 → 8 taken 16 times.
✓ Branch 14 → 15 taken 8 times.
void invert_plane_avx2_f32<true>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 14 → 8 taken 16 times.
✓ Branch 14 → 15 taken 8 times.
|
48 | for (int x = 0; x < width; x += 8) { |
| 250 | 64 | __m256 s = _mm256_load_ps(sf + x); | |
| 251 | __m256 r; | ||
| 252 | if constexpr (chroma) | ||
| 253 | 16 | r = _mm256_xor_ps(s, v_sign); | |
| 254 | else | ||
| 255 | 16 | r = _mm256_sub_ps(v_one, s); | |
| 256 | 32 | _mm256_store_ps(df + x, r); | |
| 257 | } | ||
| 258 | 16 | srcp += src_pitch; | |
| 259 | 16 | dstp += dst_pitch; | |
| 260 | } | ||
| 261 | 4 | } | |
| 262 | |||
| 263 | template void invert_plane_avx2_u8<false>(uint8_t*, const uint8_t*, int, int, int, int, int); | ||
| 264 | template void invert_plane_avx2_u8<true> (uint8_t*, const uint8_t*, int, int, int, int, int); | ||
| 265 | template void invert_plane_avx2_u16<false>(uint8_t*, const uint8_t*, int, int, int, int, int); | ||
| 266 | template void invert_plane_avx2_u16<true> (uint8_t*, const uint8_t*, int, int, int, int, int); | ||
| 267 | template void invert_plane_avx2_f32<false>(uint8_t*, const uint8_t*, int, int, int, int, int); | ||
| 268 | template void invert_plane_avx2_f32<true> (uint8_t*, const uint8_t*, int, int, int, int, int); | ||
| 269 | |||
| 270 | |||
| 271 | /******************************* | ||
| 272 | ******* Layer Filter ****** | ||
| 273 | *******************************/ | ||
| 274 | |||
| 275 | // chroma placement to mask helpers | ||
| 276 | // yuv add, subtract, mul, lighten, darken | ||
| 277 | // included in base and the avx2 source module, to get different optimizations | ||
| 278 | // Use AVX2 SIMD rowprep variants — masked_rowprep_avx2.hpp is already included above | ||
| 279 | // via masked_merge_avx2_impl.hpp. Both prepare_effective_mask_for_row_avx2 and | ||
| 280 | // prepare_effective_mask_for_row_level_baked_avx2 are therefore visible here. | ||
| 281 | #define LAYER_ROWPREP_FN prepare_effective_mask_for_row_avx2 | ||
| 282 | #include "../layer.hpp" | ||
| 283 | #undef LAYER_ROWPREP_FN | ||
| 284 | |||
| 285 | |||
| 286 | // Wrapper function that calls the local scoped get_layer_yuv_mul_functions | ||
| 287 | // This ensures the AVX2-compiled versions of the functions are selected | ||
| 288 | 3 | void get_layer_yuv_mul_functions_avx2( | |
| 289 | bool is_chroma, bool hasAlpha, | ||
| 290 | int placement, VideoInfo& vi, int bits_per_pixel, | ||
| 291 | /*out*/layer_yuv_mul_c_t** layer_fn, | ||
| 292 | /*out*/layer_yuv_mul_f_c_t** layer_f_fn) | ||
| 293 | { | ||
| 294 | 3 | get_layer_yuv_mul_functions(is_chroma, hasAlpha, placement, vi, bits_per_pixel, layer_fn, layer_f_fn); | |
| 295 | 3 | } | |
| 296 | |||
| 297 | // --------------------------------------------------------------------------- | ||
| 298 | // AVX2 Layer add dispatcher. | ||
| 299 | // --------------------------------------------------------------------------- | ||
| 300 | 5281 | void get_layer_yuv_masked_add_functions_avx2( | |
| 301 | bool is_chroma, | ||
| 302 | int placement, VideoInfo& vi, int bits_per_pixel, | ||
| 303 | /*out*/masked_merge_fn_t** layer_fn, | ||
| 304 | /*out*/masked_merge_float_fn_t** layer_f_fn) | ||
| 305 | { | ||
| 306 | // only masked merge is left here, simple weighted blend is already handled | ||
| 307 | |||
| 308 | // Use the unified (Layer,Overlay) masked merge functions | ||
| 309 | // Determine MaskMode from format and placement | ||
| 310 | 5281 | MaskMode maskMode = MASK444; | |
| 311 |
2/2✓ Branch 2 → 3 taken 4552 times.
✓ Branch 2 → 20 taken 729 times.
|
5281 | if (is_chroma) { |
| 312 |
1/2✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 4552 times.
|
4552 | if (vi.IsYV411()) |
| 313 | ✗ | maskMode = MASK411; | |
| 314 |
2/2✓ Branch 7 → 8 taken 2368 times.
✓ Branch 7 → 13 taken 2184 times.
|
4552 | else if (vi.Is420()) |
| 315 |
4/4✓ Branch 8 → 9 taken 1638 times.
✓ Branch 8 → 12 taken 730 times.
✓ Branch 9 → 10 taken 728 times.
✓ Branch 9 → 11 taken 910 times.
|
2368 | maskMode = (placement == PLACEMENT_MPEG1) ? MASK420 : (placement == PLACEMENT_TOPLEFT) ? MASK420_TOPLEFT : MASK420_MPEG2; |
| 316 |
1/2✓ Branch 14 → 15 taken 2184 times.
✗ Branch 14 → 20 not taken.
|
2184 | else if (vi.Is422()) |
| 317 |
4/4✓ Branch 15 → 16 taken 1456 times.
✓ Branch 15 → 19 taken 728 times.
✓ Branch 16 → 17 taken 728 times.
✓ Branch 16 → 18 taken 728 times.
|
2184 | maskMode = (placement == PLACEMENT_MPEG1) ? MASK422 : (placement == PLACEMENT_TOPLEFT) ? MASK422_TOPLEFT : MASK422_MPEG2; |
| 318 | // Is444() / IsY(): stay MASK444 | ||
| 319 | } | ||
| 320 | // is_chroma=false (luma): always MASK444 | ||
| 321 | 5281 | *layer_fn = get_overlay_blend_masked_fn_avx2(is_chroma, maskMode); | |
| 322 | 5281 | *layer_f_fn = get_overlay_blend_masked_float_fn_avx2(is_chroma, maskMode); | |
| 323 | 5281 | } | |
| 324 | |||
| 325 | |||
| 326 | // AVX2 lighten/darken dispatcher. | ||
| 327 | ✗ | void get_layer_planarrgb_lighten_darken_functions_avx2(bool isLighten, bool hasAlpha, bool blendAlpha, int bits_per_pixel, /*out*/layer_planarrgb_lighten_darken_c_t** layer_fn, /*out*/layer_planarrgb_lighten_darken_f_c_t** layer_f_fn) { | |
| 328 | // FIXME: add AVX2 see layer_rgb32_lighten_darken_avx2, but use then opacity_i, so | ||
| 329 | // look into the planar_rgb c implementation | ||
| 330 | ✗ | get_layer_planarrgb_lighten_darken_functions(isLighten, hasAlpha, blendAlpha, bits_per_pixel, layer_fn, layer_f_fn); | |
| 331 | ✗ | } | |
| 332 | |||
| 333 | |||
| 334 | // Planar RGB add — AVX2 per-plane wrappers. | ||
| 335 | // All planar RGB planes are at full luma resolution (MASK444). | ||
| 336 | // maskp8 is the per-pixel blend weight (overlay A, or saved pre-Subtract A from mask_child). | ||
| 337 | // ovrp8[i] is the blend target for each colour plane; for blend_alpha, ovrp8[3] is the alpha target. | ||
| 338 | // Subtract is handled by pre-inverting the overlay in Layer::Create. | ||
| 339 | // chroma=false (blend-toward-neutral luma) and float fall back to C templates. | ||
| 340 | |||
| 341 | 5 | static void layer_planarrgb_add_avx2_3plane( | |
| 342 | BYTE** dstp8, const BYTE** ovrp8, const BYTE* maskp8, | ||
| 343 | int dst_pitch, int overlay_pitch, int mask_pitch, | ||
| 344 | int width, int height, int opacity_i, int bits_per_pixel) | ||
| 345 | { | ||
| 346 |
2/2✓ Branch 5 → 3 taken 15 times.
✓ Branch 5 → 6 taken 5 times.
|
20 | for (int i = 0; i < 3; i++) |
| 347 | 15 | masked_merge_avx2_impl<MASK444>( | |
| 348 | 15 | dstp8[i], ovrp8[i], maskp8, | |
| 349 | dst_pitch, overlay_pitch, mask_pitch, | ||
| 350 | width, height, opacity_i, bits_per_pixel); | ||
| 351 | 5 | } | |
| 352 | |||
| 353 | 5 | static void layer_planarrgb_add_avx2_4plane( | |
| 354 | BYTE** dstp8, const BYTE** ovrp8, const BYTE* maskp8, | ||
| 355 | int dst_pitch, int overlay_pitch, int mask_pitch, | ||
| 356 | int width, int height, int opacity_i, int bits_per_pixel) | ||
| 357 | { | ||
| 358 |
2/2✓ Branch 5 → 3 taken 20 times.
✓ Branch 5 → 6 taken 5 times.
|
25 | for (int i = 0; i < 4; i++) |
| 359 | 20 | masked_merge_avx2_impl<MASK444>( | |
| 360 | 20 | dstp8[i], ovrp8[i], maskp8, | |
| 361 | dst_pitch, overlay_pitch, mask_pitch, | ||
| 362 | width, height, opacity_i, bits_per_pixel); | ||
| 363 | 5 | } | |
| 364 | |||
| 365 | // In layer_avx2.cpp — subtract handled by pre-inverted overlay in Layer::Create. | ||
| 366 | 1820 | void get_layer_planarrgb_add_functions_avx2( | |
| 367 | bool chroma, bool hasAlpha, bool blendAlpha, int bits_per_pixel, | ||
| 368 | /*out*/layer_planarrgb_add_c_t** layer_fn, | ||
| 369 | /*out*/layer_planarrgb_add_f_c_t** layer_f_fn) | ||
| 370 | { | ||
| 371 | // chroma is true: Layer can use the unified masked and weighted blend routines | ||
| 372 | // chroma is false: Layer-specific extension | ||
| 373 | // Integer + hasAlpha + chroma=true: dispatch per-plane to masked_merge_avx2_impl (MASK444). | ||
| 374 | // chroma=false (blend toward luma) has a different formula — keep C template. | ||
| 375 | // float: keep C template (float perf is usually fine; could add later). | ||
| 376 |
2/4✓ Branch 2 → 3 taken 1820 times.
✗ Branch 2 → 9 not taken.
✓ Branch 3 → 4 taken 1820 times.
✗ Branch 3 → 9 not taken.
|
1820 | if (chroma && bits_per_pixel != 32) { |
| 377 |
1/2✓ Branch 4 → 5 taken 1820 times.
✗ Branch 4 → 9 not taken.
|
1820 | if (hasAlpha) { |
| 378 |
2/2✓ Branch 5 → 6 taken 910 times.
✓ Branch 5 → 7 taken 910 times.
|
1820 | *layer_fn = blendAlpha ? layer_planarrgb_add_avx2_4plane : layer_planarrgb_add_avx2_3plane; |
| 379 | 1820 | return; | |
| 380 | } | ||
| 381 | // no alpha: standard weighted merge, to be added later. | ||
| 382 | } | ||
| 383 | ✗ | get_layer_planarrgb_add_functions(chroma, hasAlpha, blendAlpha, bits_per_pixel, layer_fn, layer_f_fn); | |
| 384 | } | ||
| 385 | |||
| 386 | |||
| 387 | 1 | void get_layer_planarrgb_mul_functions_avx2( | |
| 388 | bool chroma, bool hasAlpha, bool blendAlpha, int bits_per_pixel, | ||
| 389 | /*out*/layer_planarrgb_mul_c_t** layer_fn, | ||
| 390 | /*out*/layer_planarrgb_mul_f_c_t** layer_f_fn) | ||
| 391 | { | ||
| 392 | 1 | get_layer_planarrgb_mul_functions(chroma, hasAlpha, blendAlpha, bits_per_pixel, layer_fn, layer_f_fn); | |
| 393 | 1 | } | |
| 394 | |||
| 395 | // --------------------------------------------------------------------------- | ||
| 396 | // Packed RGBA (RGB32) magic-div blend — AVX2, 8-bit only. | ||
| 397 | // | ||
| 398 | // Processes 8 BGRA pixels (32 bytes) per iteration. | ||
| 399 | // | ||
| 400 | // Per-pixel blend weight source (compile-time): | ||
| 401 | // has_separate_mask=false → alpha from ovr[x*4+3] (Add: overlay's own alpha) | ||
| 402 | // has_separate_mask=true → alpha from maskp8[x] (Subtract: original alpha | ||
| 403 | // extracted before pre-inverting overlay in Create) | ||
| 404 | // | ||
| 405 | // Per-channel result: (dst * (255-aeff) + ovr * aeff + 127) / 255 | ||
| 406 | // For Subtract, ovr is already pre-inverted (max - original) by Create(). | ||
| 407 | // | ||
| 408 | // ÷255 is implemented as mulhi_epu16(x, 0x8081) >> 7 — the standard | ||
| 409 | // "magic multiply" for dividing 16-bit values by 255 (exact for [0..65280]). | ||
| 410 | // All intermediate sums stay within uint16 because: | ||
| 411 | // alpha_eff in [0..255], inv = 255-alpha_eff | ||
| 412 | // dst*inv + ovr*alpha ≤ 255*(inv+alpha) = 255*255 = 65025 < 65536 | ||
| 413 | // + half(127) → max 65152 < 65536 | ||
| 414 | // | ||
| 415 | // 16-bit pixels (RGB64) fall through to the C reference via the dispatcher. | ||
| 416 | // --------------------------------------------------------------------------- | ||
| 417 | template<bool has_separate_mask> | ||
| 418 | 7 | static void masked_blend_packedrgba_avx2_u8( | |
| 419 | BYTE* dstp8, const BYTE* ovrp8, const BYTE* maskp8, | ||
| 420 | int dst_pitch, int ovr_pitch, int mask_pitch, | ||
| 421 | int width, int height, int opacity_i) | ||
| 422 | { | ||
| 423 | // Shuffle mask: replicate byte 3 (A) of each BGRA pixel across all 4 bytes. | ||
| 424 | // AVX2 _mm256_shuffle_epi8 operates independently in each 128-bit lane, | ||
| 425 | // so lanes 0 and 1 use identical 16-byte shuffle patterns. | ||
| 426 | 7 | const __m256i shuf_alpha_bgra = _mm256_set_epi8( | |
| 427 | 15,15,15,15, 11,11,11,11, 7, 7, 7, 7, 3, 3, 3, 3, // lane 1 (pixels 4-7) | ||
| 428 | 15,15,15,15, 11,11,11,11, 7, 7, 7, 7, 3, 3, 3, 3); // lane 0 (pixels 0-3) | ||
| 429 | // Shuffle for separate mask: 8 bytes m0..m7 → each byte broadcast to 4 positions. | ||
| 430 | // Applies within each 128-bit lane independently: | ||
| 431 | // lane 0: bytes 0-7 of mask → pixels 0-3 of lane (m0,m0,m0,m0, m1,m1,m1,m1, ...) | ||
| 432 | // lane 1: bytes 0-7 of mask → pixels 4-7 of lane (m4,m4,m4,m4, m5,m5,m5,m5, ...) | ||
| 433 | 7 | const __m256i shuf_alpha_mask = _mm256_set_epi8( | |
| 434 | 3, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, // lane 1: mask bytes 4-7 | ||
| 435 | 3, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0); // lane 0: mask bytes 0-3 | ||
| 436 | |||
| 437 | 14 | const __m256i v_opacity = _mm256_set1_epi16((short)opacity_i); | |
| 438 | 7 | const __m256i v_half = _mm256_set1_epi16(127); | |
| 439 | 7 | const __m256i v_max = _mm256_set1_epi16(255); | |
| 440 | 7 | const __m256i v_magic = _mm256_set1_epi16((short)0x8081u); // magic for ÷255 | |
| 441 | |||
| 442 | // magic ÷255: mulhi_epu16(x, 0x8081) >> 7 ≡ (x * 32897) >> 23 | ||
| 443 | 111 | auto div255 = [&](__m256i x) -> __m256i { | |
| 444 | 312 | return _mm256_srli_epi16(_mm256_mulhi_epu16(x, v_magic), 7); | |
| 445 | }; | ||
| 446 | |||
| 447 | 7 | const int mod8_width = width / 8 * 8; | |
| 448 | |||
| 449 |
4/4void masked_blend_packedrgba_avx2_u8<false>(unsigned char*, unsigned char const*, unsigned char const*, int, int, int, int, int, int):
✓ Branch 96 → 23 taken 15 times.
✓ Branch 96 → 97 taken 4 times.
void masked_blend_packedrgba_avx2_u8<true>(unsigned char*, unsigned char const*, unsigned char const*, int, int, int, int, int, int):
✓ Branch 104 → 23 taken 13 times.
✓ Branch 104 → 105 taken 3 times.
|
35 | for (int y = 0; y < height; ++y) { |
| 450 | 28 | int x = 0; | |
| 451 |
4/4void masked_blend_packedrgba_avx2_u8<false>(unsigned char*, unsigned char const*, unsigned char const*, int, int, int, int, int, int):
✓ Branch 84 → 24 taken 13 times.
✓ Branch 84 → 85 taken 15 times.
void masked_blend_packedrgba_avx2_u8<true>(unsigned char*, unsigned char const*, unsigned char const*, int, int, int, int, int, int):
✓ Branch 92 → 24 taken 13 times.
✓ Branch 92 → 93 taken 13 times.
|
54 | for (; x < mod8_width; x += 8) { |
| 452 | 26 | __m256i dst8 = _mm256_loadu_si256((const __m256i*)(dstp8 + x * 4)); | |
| 453 | 39 | __m256i ovr8 = _mm256_loadu_si256((const __m256i*)(ovrp8 + x * 4)); | |
| 454 | |||
| 455 | // Broadcast per-pixel alpha to all 4 channel bytes within each pixel. | ||
| 456 | __m256i alpha_bcast; | ||
| 457 | if constexpr (has_separate_mask) { | ||
| 458 | // Load 8 mask bytes (one per pixel), distribute across two 128-bit lanes. | ||
| 459 | 13 | __m128i mask8b = _mm_loadl_epi64((const __m128i*)(maskp8 + x)); // lo 8 bytes | |
| 460 | // Place bytes 0-3 in lane 0 and bytes 4-7 in lane 1 of a 256-bit register. | ||
| 461 | 26 | __m256i mask_wide = _mm256_inserti128_si256( | |
| 462 | _mm256_castsi128_si256(mask8b), | ||
| 463 | _mm_srli_si128(mask8b, 4), 1); | ||
| 464 | 13 | alpha_bcast = _mm256_shuffle_epi8(mask_wide, shuf_alpha_mask); | |
| 465 | } else { | ||
| 466 | 13 | alpha_bcast = _mm256_shuffle_epi8(ovr8, shuf_alpha_bgra); | |
| 467 | } | ||
| 468 | |||
| 469 | // Expand all channels to 16-bit (two 256-bit registers: lo=px0-3, hi=px4-7). | ||
| 470 | 26 | __m256i dst_lo = _mm256_cvtepu8_epi16(_mm256_castsi256_si128(dst8)); | |
| 471 | 52 | __m256i dst_hi = _mm256_cvtepu8_epi16(_mm256_extracti128_si256(dst8, 1)); | |
| 472 | 26 | __m256i ovr_lo = _mm256_cvtepu8_epi16(_mm256_castsi256_si128(ovr8)); | |
| 473 | 52 | __m256i ovr_hi = _mm256_cvtepu8_epi16(_mm256_extracti128_si256(ovr8, 1)); | |
| 474 | 26 | __m256i a_lo = _mm256_cvtepu8_epi16(_mm256_castsi256_si128(alpha_bcast)); | |
| 475 | 52 | __m256i a_hi = _mm256_cvtepu8_epi16(_mm256_extracti128_si256(alpha_bcast, 1)); | |
| 476 | |||
| 477 | // alpha_eff = (alpha * opacity_i + 127) / 255 | ||
| 478 | 52 | __m256i aeff_lo = div255(_mm256_add_epi16(_mm256_mullo_epi16(a_lo, v_opacity), v_half)); | |
| 479 | 52 | __m256i aeff_hi = div255(_mm256_add_epi16(_mm256_mullo_epi16(a_hi, v_opacity), v_half)); | |
| 480 | 26 | __m256i inv_lo = _mm256_sub_epi16(v_max, aeff_lo); | |
| 481 | 26 | __m256i inv_hi = _mm256_sub_epi16(v_max, aeff_hi); | |
| 482 | |||
| 483 | // b = ovr (overlay pre-inverted for Subtract; plain for Add) | ||
| 484 | 26 | const __m256i b_lo = ovr_lo; | |
| 485 | 26 | const __m256i b_hi = ovr_hi; | |
| 486 | |||
| 487 | // result = (dst * inv + b * aeff + 127) / 255 | ||
| 488 | 104 | __m256i res_lo = div255(_mm256_add_epi16( | |
| 489 | _mm256_add_epi16(_mm256_mullo_epi16(dst_lo, inv_lo), | ||
| 490 | _mm256_mullo_epi16(b_lo, aeff_lo)), v_half)); | ||
| 491 | 104 | __m256i res_hi = div255(_mm256_add_epi16( | |
| 492 | _mm256_add_epi16(_mm256_mullo_epi16(dst_hi, inv_hi), | ||
| 493 | _mm256_mullo_epi16(b_hi, aeff_hi)), v_half)); | ||
| 494 | |||
| 495 | // Pack 16→8-bit. _mm256_packus_epi16 interleaves lanes: [p0p1 p4p5 | p2p3 p6p7]. | ||
| 496 | // _mm256_permute4x64_epi64(..., 0xD8) reorders 64-bit groups [0,2,1,3] to [p0-p7]. | ||
| 497 | 26 | __m256i result = _mm256_permute4x64_epi64( | |
| 498 | _mm256_packus_epi16(res_lo, res_hi), 0xD8); | ||
| 499 | |||
| 500 | 26 | _mm256_storeu_si256((__m256i*)(dstp8 + x * 4), result); | |
| 501 | } | ||
| 502 | |||
| 503 | // Scalar tail (width not a multiple of 8). | ||
| 504 | 28 | constexpr MagicDiv magic8 = get_magic_div(8); | |
| 505 |
4/4void masked_blend_packedrgba_avx2_u8<false>(unsigned char*, unsigned char const*, unsigned char const*, int, int, int, int, int, int):
✓ Branch 94 → 86 taken 55 times.
✓ Branch 94 → 95 taken 15 times.
void masked_blend_packedrgba_avx2_u8<true>(unsigned char*, unsigned char const*, unsigned char const*, int, int, int, int, int, int):
✓ Branch 102 → 94 taken 41 times.
✓ Branch 102 → 103 taken 13 times.
|
124 | for (; x < width; ++x) { |
| 506 | 96 | const uint32_t alpha_src = has_separate_mask | |
| 507 | 41 | ? (uint32_t)maskp8[x] | |
| 508 | 55 | : (uint32_t)ovrp8[x * 4 + 3]; | |
| 509 | 192 | const uint32_t ae = (uint32_t)magic_div_rt<uint8_t>( | |
| 510 | 96 | alpha_src * (uint32_t)opacity_i + 127u, magic8); | |
| 511 | 96 | const uint32_t iv = 255u - ae; | |
| 512 |
4/4void masked_blend_packedrgba_avx2_u8<false>(unsigned char*, unsigned char const*, unsigned char const*, int, int, int, int, int, int):
✓ Branch 92 → 89 taken 220 times.
✓ Branch 92 → 93 taken 55 times.
void masked_blend_packedrgba_avx2_u8<true>(unsigned char*, unsigned char const*, unsigned char const*, int, int, int, int, int, int):
✓ Branch 100 → 97 taken 164 times.
✓ Branch 100 → 101 taken 41 times.
|
480 | for (int ch = 0; ch < 4; ++ch) { |
| 513 | 384 | dstp8[x * 4 + ch] = (BYTE)magic_div_rt<uint8_t>( | |
| 514 | 384 | (uint32_t)dstp8[x * 4 + ch] * iv + (uint32_t)ovrp8[x * 4 + ch] * ae + 127u, magic8); | |
| 515 | } | ||
| 516 | } | ||
| 517 | |||
| 518 | 28 | dstp8 += dst_pitch; | |
| 519 | 28 | ovrp8 += ovr_pitch; | |
| 520 | 13 | if constexpr (has_separate_mask) maskp8 += mask_pitch; | |
| 521 | } | ||
| 522 | 7 | } | |
| 523 | |||
| 524 | // Dispatcher: 8-bit only; 16-bit falls back to C reference. | ||
| 525 | // has_separate_mask=false → Add (alpha from ovrp8[x*4+3]) | ||
| 526 | // has_separate_mask=true → Subtract (alpha from maskp8[x], overlay pre-inverted) | ||
| 527 | 1094 | void get_layer_packedrgb_blend_functions_avx2( | |
| 528 | bool has_separate_mask, int bits_per_pixel, | ||
| 529 | layer_packedrgb_blend_c_t** fn) | ||
| 530 | { | ||
| 531 |
2/2✓ Branch 2 → 3 taken 1093 times.
✓ Branch 2 → 7 taken 1 time.
|
1094 | if (bits_per_pixel == 8) { |
| 532 |
2/2✓ Branch 3 → 4 taken 546 times.
✓ Branch 3 → 5 taken 547 times.
|
1093 | *fn = has_separate_mask ? masked_blend_packedrgba_avx2_u8<true> |
| 533 | : masked_blend_packedrgba_avx2_u8<false>; | ||
| 534 | 1093 | return; | |
| 535 | } | ||
| 536 | 1 | get_layer_packedrgb_blend_functions(has_separate_mask, bits_per_pixel, fn); | |
| 537 | } | ||
| 538 | |||
| 539 | |||
| 540 | |||
| 541 | |||
| 542 | |||
| 543 | |||
| 544 | // "fast" blend is simple averaging | ||
| 545 | template<typename pixel_t> | ||
| 546 | 4 | void layer_genericplane_fast_avx2(BYTE* dstp, const BYTE* ovrp, int dst_pitch, int overlay_pitch, int width, int height, int level) { | |
| 547 | AVS_UNUSED(level); | ||
| 548 | 4 | int width_bytes = width * sizeof(pixel_t); | |
| 549 | 4 | int width_mod32 = width_bytes / 32 * 32; | |
| 550 | |||
| 551 |
4/4void layer_genericplane_fast_avx2<unsigned char>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 17 → 3 taken 10 times.
✓ Branch 17 → 18 taken 2 times.
void layer_genericplane_fast_avx2<unsigned short>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 17 → 3 taken 10 times.
✓ Branch 17 → 18 taken 2 times.
|
24 | for (int y = 0; y < height; ++y) { |
| 552 |
4/4void layer_genericplane_fast_avx2<unsigned char>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 12 → 4 taken 10 times.
✓ Branch 12 → 13 taken 10 times.
void layer_genericplane_fast_avx2<unsigned short>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 12 → 4 taken 10 times.
✓ Branch 12 → 13 taken 10 times.
|
40 | for (int x = 0; x < width_mod32; x += 32) { |
| 553 | 20 | __m256i src = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(dstp + x)); | |
| 554 | 40 | __m256i ovr = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(ovrp + x)); | |
| 555 | if constexpr (sizeof(pixel_t) == 1) | ||
| 556 | 10 | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + x), _mm256_avg_epu8(src, ovr)); | |
| 557 | else | ||
| 558 | 10 | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + x), _mm256_avg_epu16(src, ovr)); | |
| 559 | } | ||
| 560 | |||
| 561 |
4/4void layer_genericplane_fast_avx2<unsigned char>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 15 → 14 taken 114 times.
✓ Branch 15 → 16 taken 10 times.
void layer_genericplane_fast_avx2<unsigned short>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 15 → 14 taken 52 times.
✓ Branch 15 → 16 taken 10 times.
|
186 | for (int x = width_mod32 / sizeof(pixel_t); x < width; ++x) { |
| 562 | 166 | reinterpret_cast<pixel_t*>(dstp)[x] = (reinterpret_cast<pixel_t*>(dstp)[x] + reinterpret_cast<const pixel_t*>(ovrp)[x] + 1) / 2; | |
| 563 | } | ||
| 564 | |||
| 565 | 20 | dstp += dst_pitch; | |
| 566 | 20 | ovrp += overlay_pitch; | |
| 567 | } | ||
| 568 | 4 | } | |
| 569 | |||
| 570 | // instantiate | ||
| 571 | template void layer_genericplane_fast_avx2<uint8_t>(BYTE* dstp, const BYTE* ovrp, int dst_pitch, int overlay_pitch, int width, int height, int level); | ||
| 572 | template void layer_genericplane_fast_avx2<uint16_t>(BYTE* dstp, const BYTE* ovrp, int dst_pitch, int overlay_pitch, int width, int height, int level); | ||
| 573 | |||
| 574 | /* RGB32 */ | ||
| 575 | |||
| 576 | //src format: xx xx xx xx | xx xx xx xx | a1 xx xx xx | a0 xx xx xx | ||
| 577 | //level_vector and one should be vectors of 32bit packed integers | ||
| 578 | static AVS_FORCEINLINE __m128i calculate_monochrome_alpha_avx2(const __m128i& src, const __m128i& level_vector, const __m128i& one) { | ||
| 579 | 608 | __m128i alpha = _mm_srli_epi32(src, 24); | |
| 580 | 304 | alpha = _mm_mullo_epi16(alpha, level_vector); | |
| 581 | 608 | alpha = _mm_add_epi32(alpha, one); | |
| 582 | 304 | alpha = _mm_srli_epi32(alpha, 8); | |
| 583 | 304 | alpha = _mm_shufflelo_epi16(alpha, _MM_SHUFFLE(2, 2, 0, 0)); | |
| 584 | 304 | return _mm_shuffle_epi32(alpha, _MM_SHUFFLE(1, 1, 0, 0)); | |
| 585 | } | ||
| 586 | |||
| 587 | static AVS_FORCEINLINE __m128i calculate_luma_avx2(const __m128i& src, const __m128i& rgb_coeffs, const __m128i& zero) { | ||
| 588 | AVS_UNUSED(zero); | ||
| 589 | 96 | __m128i temp = _mm_madd_epi16(src, rgb_coeffs); | |
| 590 | 364 | __m128i low = _mm_shuffle_epi32(temp, _MM_SHUFFLE(3, 3, 1, 1)); | |
| 591 | 364 | temp = _mm_add_epi32(low, temp); | |
| 592 | 364 | temp = _mm_srli_epi32(temp, 15); | |
| 593 | 364 | __m128i result = _mm_shufflelo_epi16(temp, _MM_SHUFFLE(0, 0, 0, 0)); | |
| 594 | 364 | return _mm_shufflehi_epi16(result, _MM_SHUFFLE(0, 0, 0, 0)); | |
| 595 | } | ||
| 596 | |||
| 597 | // must be unaligned load/store | ||
| 598 | template<bool use_chroma> | ||
| 599 | 6 | void layer_rgb32_mul_avx2(BYTE* dstp, const BYTE* ovrp, int dst_pitch, int overlay_pitch, int width, int height, int level) { | |
| 600 | 6 | int mod2_width = width / 2 * 2; | |
| 601 | |||
| 602 | 6 | __m128i zero = _mm_setzero_si128(); | |
| 603 | 6 | __m128i level_vector = _mm_set1_epi32(level); | |
| 604 | 6 | __m128i one = _mm_set1_epi32(1); | |
| 605 | 6 | __m128i rgb_coeffs = _mm_set_epi16(0, cyr, cyg, cyb, 0, cyr, cyg, cyb); | |
| 606 | |||
| 607 |
4/4void layer_rgb32_mul_avx2<false>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 72 → 15 taken 11 times.
✓ Branch 72 → 73 taken 3 times.
void layer_rgb32_mul_avx2<true>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 65 → 15 taken 11 times.
✓ Branch 65 → 66 taken 3 times.
|
28 | for (int y = 0; y < height; ++y) { |
| 608 |
4/4void layer_rgb32_mul_avx2<false>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 67 → 16 taken 48 times.
✓ Branch 67 → 68 taken 11 times.
void layer_rgb32_mul_avx2<true>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 60 → 16 taken 48 times.
✓ Branch 60 → 61 taken 11 times.
|
118 | for (int x = 0; x < mod2_width; x += 2) { |
| 609 | 96 | __m128i src = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(dstp + x * 4)); | |
| 610 | 192 | __m128i ovr = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(ovrp + x * 4)); | |
| 611 | |||
| 612 | 96 | __m128i alpha = calculate_monochrome_alpha_avx2(ovr, level_vector, one); | |
| 613 | |||
| 614 | 96 | src = _mm_unpacklo_epi8(src, zero); | |
| 615 | 192 | ovr = _mm_unpacklo_epi8(ovr, zero); | |
| 616 | |||
| 617 | __m128i luma; | ||
| 618 | if (use_chroma) { | ||
| 619 | 48 | luma = ovr; | |
| 620 | } | ||
| 621 | else { | ||
| 622 | 48 | luma = calculate_luma_avx2(ovr, rgb_coeffs, zero); | |
| 623 | } | ||
| 624 | |||
| 625 | 96 | __m128i dst = _mm_mullo_epi16(luma, src); | |
| 626 | 96 | dst = _mm_srli_epi16(dst, 8); | |
| 627 | 96 | dst = _mm_subs_epi16(dst, src); | |
| 628 | 96 | dst = _mm_mullo_epi16(dst, alpha); | |
| 629 | 96 | dst = _mm_srli_epi16(dst, 8); | |
| 630 | 96 | dst = _mm_add_epi8(src, dst); | |
| 631 | |||
| 632 | 96 | dst = _mm_packus_epi16(dst, zero); | |
| 633 | |||
| 634 | 96 | _mm_storel_epi64(reinterpret_cast<__m128i*>(dstp + x * 4), dst); | |
| 635 | } | ||
| 636 | |||
| 637 |
2/4void layer_rgb32_mul_avx2<false>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 68 → 69 taken 11 times.
✗ Branch 68 → 71 not taken.
void layer_rgb32_mul_avx2<true>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 61 → 62 taken 11 times.
✗ Branch 61 → 64 not taken.
|
22 | if (width != mod2_width) { |
| 638 | 22 | int x = mod2_width; | |
| 639 | 22 | int alpha = (ovrp[x * 4 + 3] * level + 1) >> 8; | |
| 640 | |||
| 641 | if (use_chroma) { | ||
| 642 | 11 | dstp[x * 4] = dstp[x * 4] + (((((ovrp[x * 4] * dstp[x * 4]) >> 8) - dstp[x * 4]) * alpha) >> 8); | |
| 643 | 11 | dstp[x * 4 + 1] = dstp[x * 4 + 1] + (((((ovrp[x * 4 + 1] * dstp[x * 4 + 1]) >> 8) - dstp[x * 4 + 1]) * alpha) >> 8); | |
| 644 | 11 | dstp[x * 4 + 2] = dstp[x * 4 + 2] + (((((ovrp[x * 4 + 2] * dstp[x * 4 + 2]) >> 8) - dstp[x * 4 + 2]) * alpha) >> 8); | |
| 645 | 11 | dstp[x * 4 + 3] = dstp[x * 4 + 3] + (((((ovrp[x * 4 + 3] * dstp[x * 4 + 3]) >> 8) - dstp[x * 4 + 3]) * alpha) >> 8); | |
| 646 | } | ||
| 647 | else { | ||
| 648 | 11 | int luma = (cyb * ovrp[x * 4] + cyg * ovrp[x * 4 + 1] + cyr * ovrp[x * 4 + 2]) >> 15; | |
| 649 | |||
| 650 | 11 | dstp[x * 4] = dstp[x * 4] + (((((luma * dstp[x * 4]) >> 8) - dstp[x * 4]) * alpha) >> 8); | |
| 651 | 11 | dstp[x * 4 + 1] = dstp[x * 4 + 1] + (((((luma * dstp[x * 4 + 1]) >> 8) - dstp[x * 4 + 1]) * alpha) >> 8); | |
| 652 | 11 | dstp[x * 4 + 2] = dstp[x * 4 + 2] + (((((luma * dstp[x * 4 + 2]) >> 8) - dstp[x * 4 + 2]) * alpha) >> 8); | |
| 653 | 11 | dstp[x * 4 + 3] = dstp[x * 4 + 3] + (((((luma * dstp[x * 4 + 3]) >> 8) - dstp[x * 4 + 3]) * alpha) >> 8); | |
| 654 | } | ||
| 655 | } | ||
| 656 | |||
| 657 | 22 | dstp += dst_pitch; | |
| 658 | 22 | ovrp += overlay_pitch; | |
| 659 | } | ||
| 660 | 6 | } | |
| 661 | |||
| 662 | // instantiate | ||
| 663 | template void layer_rgb32_mul_avx2<false>(BYTE* dstp, const BYTE* ovrp, int dst_pitch, int overlay_pitch, int width, int height, int level); | ||
| 664 | template void layer_rgb32_mul_avx2<true>(BYTE* dstp, const BYTE* ovrp, int dst_pitch, int overlay_pitch, int width, int height, int level); | ||
| 665 | |||
| 666 | |||
| 667 | // must be unaligned load/store | ||
| 668 | template<bool use_chroma> | ||
| 669 | 3 | void layer_rgb32_add_avx2(BYTE* dstp, const BYTE* ovrp, int dst_pitch, int overlay_pitch, int width, int height, int level) { | |
| 670 | 3 | int mod2_width = width / 2 * 2; | |
| 671 | |||
| 672 | 3 | __m128i zero = _mm_setzero_si128(); | |
| 673 | 3 | __m128i level_vector = _mm_set1_epi32(level); | |
| 674 | 3 | __m128i one = _mm_set1_epi32(1); | |
| 675 | 3 | __m128i rgb_coeffs = _mm_set_epi16(0, cyr, cyg, cyb, 0, cyr, cyg, cyb); | |
| 676 | |||
| 677 | 3 | constexpr int rounder = 128; | |
| 678 | 3 | const __m128i rounder_simd = _mm_set1_epi16(rounder); | |
| 679 | |||
| 680 |
2/4void layer_rgb32_add_avx2<false>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 74 → 19 taken 11 times.
✓ Branch 74 → 75 taken 3 times.
void layer_rgb32_add_avx2<true>(unsigned char*, unsigned char const*, int, int, int, int, int):
✗ Branch 67 → 19 not taken.
✗ Branch 67 → 68 not taken.
|
14 | for (int y = 0; y < height; ++y) { |
| 681 |
2/4void layer_rgb32_add_avx2<false>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 69 → 20 taken 48 times.
✓ Branch 69 → 70 taken 11 times.
void layer_rgb32_add_avx2<true>(unsigned char*, unsigned char const*, int, int, int, int, int):
✗ Branch 62 → 20 not taken.
✗ Branch 62 → 63 not taken.
|
59 | for (int x = 0; x < mod2_width; x += 2) { |
| 682 | 48 | __m128i src = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(dstp + x * 4)); | |
| 683 | 96 | __m128i ovr = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(ovrp + x * 4)); | |
| 684 | |||
| 685 | 48 | __m128i alpha = calculate_monochrome_alpha_avx2(ovr, level_vector, one); | |
| 686 | |||
| 687 | 48 | src = _mm_unpacklo_epi8(src, zero); | |
| 688 | 96 | ovr = _mm_unpacklo_epi8(ovr, zero); | |
| 689 | |||
| 690 | __m128i luma; | ||
| 691 | if (use_chroma) { | ||
| 692 | ✗ | luma = ovr; | |
| 693 | } | ||
| 694 | else { | ||
| 695 | 48 | luma = calculate_luma_avx2(ovr, rgb_coeffs, zero); | |
| 696 | } | ||
| 697 | |||
| 698 | 48 | __m128i dst = _mm_subs_epi16(luma, src); | |
| 699 | 48 | dst = _mm_mullo_epi16(dst, alpha); | |
| 700 | 48 | dst = _mm_add_epi16(dst, rounder_simd); | |
| 701 | 48 | dst = _mm_srli_epi16(dst, 8); | |
| 702 | 48 | dst = _mm_add_epi8(src, dst); | |
| 703 | |||
| 704 | 48 | dst = _mm_packus_epi16(dst, zero); | |
| 705 | |||
| 706 | 48 | _mm_storel_epi64(reinterpret_cast<__m128i*>(dstp + x * 4), dst); | |
| 707 | } | ||
| 708 | |||
| 709 |
1/4void layer_rgb32_add_avx2<false>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 70 → 71 taken 11 times.
✗ Branch 70 → 73 not taken.
void layer_rgb32_add_avx2<true>(unsigned char*, unsigned char const*, int, int, int, int, int):
✗ Branch 63 → 64 not taken.
✗ Branch 63 → 66 not taken.
|
11 | if (width != mod2_width) { |
| 710 | 11 | int x = mod2_width; | |
| 711 | 11 | int alpha = (ovrp[x * 4 + 3] * level + 1) >> 8; | |
| 712 | |||
| 713 | if (use_chroma) { | ||
| 714 | ✗ | dstp[x * 4] = dstp[x * 4] + (((ovrp[x * 4] - dstp[x * 4]) * alpha + rounder) >> 8); | |
| 715 | ✗ | dstp[x * 4 + 1] = dstp[x * 4 + 1] + (((ovrp[x * 4 + 1] - dstp[x * 4 + 1]) * alpha + rounder) >> 8); | |
| 716 | ✗ | dstp[x * 4 + 2] = dstp[x * 4 + 2] + (((ovrp[x * 4 + 2] - dstp[x * 4 + 2]) * alpha + rounder) >> 8); | |
| 717 | ✗ | dstp[x * 4 + 3] = dstp[x * 4 + 3] + (((ovrp[x * 4 + 3] - dstp[x * 4 + 3]) * alpha + rounder) >> 8); | |
| 718 | } | ||
| 719 | else { | ||
| 720 | 11 | int luma = (cyb * ovrp[x * 4] + cyg * ovrp[x * 4 + 1] + cyr * ovrp[x * 4 + 2]) >> 15; | |
| 721 | |||
| 722 | 11 | dstp[x * 4] = dstp[x * 4] + (((luma - dstp[x * 4]) * alpha + rounder) >> 8); | |
| 723 | 11 | dstp[x * 4 + 1] = dstp[x * 4 + 1] + (((luma - dstp[x * 4 + 1]) * alpha + rounder) >> 8); | |
| 724 | 11 | dstp[x * 4 + 2] = dstp[x * 4 + 2] + (((luma - dstp[x * 4 + 2]) * alpha + rounder) >> 8); | |
| 725 | 11 | dstp[x * 4 + 3] = dstp[x * 4 + 3] + (((luma - dstp[x * 4 + 3]) * alpha + rounder) >> 8); | |
| 726 | } | ||
| 727 | } | ||
| 728 | |||
| 729 | 11 | dstp += dst_pitch; | |
| 730 | 11 | ovrp += overlay_pitch; | |
| 731 | } | ||
| 732 | 3 | } | |
| 733 | |||
| 734 | // instantiate | ||
| 735 | template void layer_rgb32_add_avx2<false>(BYTE* dstp, const BYTE* ovrp, int dst_pitch, int overlay_pitch, int width, int height, int level); | ||
| 736 | template void layer_rgb32_add_avx2<true>(BYTE* dstp, const BYTE* ovrp, int dst_pitch, int overlay_pitch, int width, int height, int level); | ||
| 737 | |||
| 738 | // unlike sse2 alignment is not required. avx2 has no such big penalty | ||
| 739 | 3 | void layer_yuy2_or_rgb32_fast_avx2(BYTE* dstp, const BYTE* ovrp, int dst_pitch, int overlay_pitch, int width, int height, int level) { | |
| 740 | AVS_UNUSED(level); | ||
| 741 | 3 | int width_bytes = width * 2; | |
| 742 | 3 | int width_mod32 = width_bytes / 32 * 32; | |
| 743 | |||
| 744 |
2/2✓ Branch 17 → 3 taken 13 times.
✓ Branch 17 → 18 taken 3 times.
|
16 | for (int y = 0; y < height; ++y) { |
| 745 |
2/2✓ Branch 12 → 4 taken 13 times.
✓ Branch 12 → 13 taken 13 times.
|
26 | for (int x = 0; x < width_mod32; x += 32) { |
| 746 | 13 | __m256i src = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(dstp + x)); | |
| 747 | 26 | __m256i ovr = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(ovrp + x)); | |
| 748 | |||
| 749 | 13 | _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp + x), _mm256_avg_epu8(src, ovr)); | |
| 750 | } | ||
| 751 | |||
| 752 |
2/2✓ Branch 15 → 14 taken 264 times.
✓ Branch 15 → 16 taken 13 times.
|
277 | for (int x = width_mod32; x < width_bytes; ++x) { |
| 753 | 264 | dstp[x] = (dstp[x] + ovrp[x] + 1) / 2; | |
| 754 | } | ||
| 755 | |||
| 756 | 13 | dstp += dst_pitch; | |
| 757 | 13 | ovrp += overlay_pitch; | |
| 758 | } | ||
| 759 | 3 | } | |
| 760 | |||
| 761 | // aligned ptr not required | ||
| 762 | 1 | void layer_rgb32_fast_avx2(BYTE* dstp, const BYTE* ovrp, int dst_pitch, int overlay_pitch, int width, int height, int level) { | |
| 763 | 1 | layer_yuy2_or_rgb32_fast_avx2(dstp, ovrp, dst_pitch, overlay_pitch, width * 2, height, level); | |
| 764 | 1 | } | |
| 765 | |||
| 766 | // unaligned addresses | ||
| 767 | template<bool use_chroma> | ||
| 768 | 4 | void layer_rgb32_subtract_avx2(BYTE* dstp, const BYTE* ovrp, int dst_pitch, int overlay_pitch, int width, int height, int level) { | |
| 769 | 4 | int mod2_width = width / 2 * 2; | |
| 770 | |||
| 771 | 4 | __m128i zero = _mm_setzero_si128(); | |
| 772 | 4 | __m128i level_vector = _mm_set1_epi32(level); | |
| 773 | 4 | __m128i one = _mm_set1_epi32(1); | |
| 774 | 4 | __m128i rgb_coeffs = _mm_set_epi16(0, cyr, cyg, cyb, 0, cyr, cyg, cyb); | |
| 775 | 4 | __m128i ff = _mm_set1_epi16(0x00FF); | |
| 776 | |||
| 777 | 4 | constexpr int rounder = 128; | |
| 778 | 4 | const __m128i rounder_simd = _mm_set1_epi16(rounder); | |
| 779 | |||
| 780 |
2/4void layer_rgb32_subtract_avx2<false>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 80 → 23 taken 13 times.
✓ Branch 80 → 81 taken 4 times.
void layer_rgb32_subtract_avx2<true>(unsigned char*, unsigned char const*, int, int, int, int, int):
✗ Branch 73 → 23 not taken.
✗ Branch 73 → 74 not taken.
|
17 | for (int y = 0; y < height; ++y) { |
| 781 |
2/4void layer_rgb32_subtract_avx2<false>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 75 → 24 taken 52 times.
✓ Branch 75 → 76 taken 13 times.
void layer_rgb32_subtract_avx2<true>(unsigned char*, unsigned char const*, int, int, int, int, int):
✗ Branch 68 → 24 not taken.
✗ Branch 68 → 69 not taken.
|
65 | for (int x = 0; x < mod2_width; x += 2) { |
| 782 | 52 | __m128i src = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(dstp + x * 4)); | |
| 783 | 104 | __m128i ovr = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(ovrp + x * 4)); | |
| 784 | |||
| 785 | 52 | __m128i alpha = calculate_monochrome_alpha_avx2(ovr, level_vector, one); | |
| 786 | |||
| 787 | 52 | src = _mm_unpacklo_epi8(src, zero); | |
| 788 | 104 | ovr = _mm_unpacklo_epi8(ovr, zero); | |
| 789 | |||
| 790 | __m128i luma; | ||
| 791 | if (use_chroma) { | ||
| 792 | ✗ | luma = _mm_subs_epi16(ff, ovr); | |
| 793 | } | ||
| 794 | else { | ||
| 795 | 156 | luma = calculate_luma_avx2(_mm_andnot_si128(ovr, ff), rgb_coeffs, zero); | |
| 796 | } | ||
| 797 | |||
| 798 | 52 | __m128i dst = _mm_subs_epi16(luma, src); | |
| 799 | 52 | dst = _mm_mullo_epi16(dst, alpha); | |
| 800 | 52 | dst = _mm_add_epi16(dst, rounder_simd); | |
| 801 | 52 | dst = _mm_srli_epi16(dst, 8); | |
| 802 | 52 | dst = _mm_add_epi8(src, dst); | |
| 803 | |||
| 804 | 52 | dst = _mm_packus_epi16(dst, zero); | |
| 805 | |||
| 806 | 52 | _mm_storel_epi64(reinterpret_cast<__m128i*>(dstp + x * 4), dst); | |
| 807 | } | ||
| 808 | |||
| 809 |
1/4void layer_rgb32_subtract_avx2<false>(unsigned char*, unsigned char const*, int, int, int, int, int):
✓ Branch 76 → 77 taken 13 times.
✗ Branch 76 → 79 not taken.
void layer_rgb32_subtract_avx2<true>(unsigned char*, unsigned char const*, int, int, int, int, int):
✗ Branch 69 → 70 not taken.
✗ Branch 69 → 72 not taken.
|
13 | if (width != mod2_width) { |
| 810 | 13 | int x = mod2_width; | |
| 811 | 13 | int alpha = (ovrp[x * 4 + 3] * level + 1) >> 8; | |
| 812 | |||
| 813 | if (use_chroma) { | ||
| 814 | ✗ | dstp[x * 4] = dstp[x * 4] + (((255 - ovrp[x * 4] - dstp[x * 4]) * alpha + rounder) >> 8); | |
| 815 | ✗ | dstp[x * 4 + 1] = dstp[x * 4 + 1] + (((255 - ovrp[x * 4 + 1] - dstp[x * 4 + 1]) * alpha + rounder) >> 8); | |
| 816 | ✗ | dstp[x * 4 + 2] = dstp[x * 4 + 2] + (((255 - ovrp[x * 4 + 2] - dstp[x * 4 + 2]) * alpha + rounder) >> 8); | |
| 817 | ✗ | dstp[x * 4 + 3] = dstp[x * 4 + 3] + (((255 - ovrp[x * 4 + 3] - dstp[x * 4 + 3]) * alpha + rounder) >> 8); | |
| 818 | } | ||
| 819 | else { | ||
| 820 | 13 | int luma = (cyb * (255 - ovrp[x * 4]) + cyg * (255 - ovrp[x * 4 + 1]) + cyr * (255 - ovrp[x * 4 + 2])) >> 15; | |
| 821 | |||
| 822 | 13 | dstp[x * 4] = dstp[x * 4] + (((luma - dstp[x * 4]) * alpha + rounder) >> 8); | |
| 823 | 13 | dstp[x * 4 + 1] = dstp[x * 4 + 1] + (((luma - dstp[x * 4 + 1]) * alpha + rounder) >> 8); | |
| 824 | 13 | dstp[x * 4 + 2] = dstp[x * 4 + 2] + (((luma - dstp[x * 4 + 2]) * alpha + rounder) >> 8); | |
| 825 | 13 | dstp[x * 4 + 3] = dstp[x * 4 + 3] + (((luma - dstp[x * 4 + 3]) * alpha + rounder) >> 8); | |
| 826 | } | ||
| 827 | } | ||
| 828 | |||
| 829 | 13 | dstp += dst_pitch; | |
| 830 | 13 | ovrp += overlay_pitch; | |
| 831 | } | ||
| 832 | 4 | } | |
| 833 | |||
| 834 | // instantiate | ||
| 835 | template void layer_rgb32_subtract_avx2<false>(BYTE* dstp, const BYTE* ovrp, int dst_pitch, int overlay_pitch, int width, int height, int level); | ||
| 836 | template void layer_rgb32_subtract_avx2<true>(BYTE* dstp, const BYTE* ovrp, int dst_pitch, int overlay_pitch, int width, int height, int level); | ||
| 837 | |||
| 838 | // unaligned adresses | ||
| 839 | template<int mode> | ||
| 840 | 8 | void layer_rgb32_lighten_darken_avx2(BYTE* dstp, const BYTE* ovrp, int dst_pitch, int overlay_pitch, int width, int height, int level, int thresh) { | |
| 841 | 8 | int mod2_width = width / 2 * 2; | |
| 842 | |||
| 843 | 8 | __m128i zero = _mm_setzero_si128(); | |
| 844 | 8 | __m128i level_vector = _mm_set1_epi32(level); | |
| 845 | 8 | __m128i one = _mm_set1_epi32(1); | |
| 846 | 8 | __m128i rgb_coeffs = _mm_set_epi16(0, cyr, cyg, cyb, 0, cyr, cyg, cyb); | |
| 847 | 8 | __m128i threshold = _mm_set1_epi16(thresh); | |
| 848 | |||
| 849 | 8 | constexpr int rounder = 128; | |
| 850 | 8 | const __m128i rounder_simd = _mm_set1_epi16(rounder); | |
| 851 | |||
| 852 |
4/4void layer_rgb32_lighten_darken_avx2<0>(unsigned char*, unsigned char const*, int, int, int, int, int, int):
✓ Branch 93 → 23 taken 13 times.
✓ Branch 93 → 94 taken 4 times.
void layer_rgb32_lighten_darken_avx2<1>(unsigned char*, unsigned char const*, int, int, int, int, int, int):
✓ Branch 93 → 23 taken 13 times.
✓ Branch 93 → 94 taken 4 times.
|
34 | for (int y = 0; y < height; ++y) { |
| 853 |
4/4void layer_rgb32_lighten_darken_avx2<0>(unsigned char*, unsigned char const*, int, int, int, int, int, int):
✓ Branch 86 → 24 taken 54 times.
✓ Branch 86 → 87 taken 13 times.
void layer_rgb32_lighten_darken_avx2<1>(unsigned char*, unsigned char const*, int, int, int, int, int, int):
✓ Branch 86 → 24 taken 54 times.
✓ Branch 86 → 87 taken 13 times.
|
134 | for (int x = 0; x < mod2_width; x += 2) { |
| 854 | 108 | __m128i src = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(dstp + x * 4)); | |
| 855 | 216 | __m128i ovr = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(ovrp + x * 4)); | |
| 856 | |||
| 857 | 108 | __m128i alpha = calculate_monochrome_alpha_avx2(ovr, level_vector, one); | |
| 858 | |||
| 859 | 108 | src = _mm_unpacklo_epi8(src, zero); | |
| 860 | 216 | ovr = _mm_unpacklo_epi8(ovr, zero); | |
| 861 | |||
| 862 | 108 | __m128i luma_ovr = calculate_luma_avx2(ovr, rgb_coeffs, zero); | |
| 863 | 108 | __m128i luma_src = calculate_luma_avx2(src, rgb_coeffs, zero); | |
| 864 | |||
| 865 | __m128i mask; | ||
| 866 | if constexpr (mode == LIGHTEN) { | ||
| 867 | 54 | __m128i tmp = _mm_add_epi16(luma_src, threshold); | |
| 868 | 54 | mask = _mm_cmpgt_epi16(luma_ovr, tmp); | |
| 869 | } | ||
| 870 | else { | ||
| 871 | 54 | __m128i tmp = _mm_sub_epi16(luma_src, threshold); | |
| 872 | 54 | mask = _mm_cmpgt_epi16(tmp, luma_ovr); | |
| 873 | } | ||
| 874 | |||
| 875 | 108 | alpha = _mm_and_si128(alpha, mask); | |
| 876 | |||
| 877 | 216 | __m128i dst = _mm_subs_epi16(ovr, src); | |
| 878 | 108 | dst = _mm_mullo_epi16(dst, alpha); | |
| 879 | 108 | dst = _mm_add_epi16(dst, rounder_simd); | |
| 880 | 108 | dst = _mm_srli_epi16(dst, 8); | |
| 881 | 108 | dst = _mm_add_epi8(src, dst); | |
| 882 | |||
| 883 | 108 | dst = _mm_packus_epi16(dst, zero); | |
| 884 | |||
| 885 | 108 | _mm_storel_epi64(reinterpret_cast<__m128i*>(dstp + x * 4), dst); | |
| 886 | } | ||
| 887 | |||
| 888 |
2/4void layer_rgb32_lighten_darken_avx2<0>(unsigned char*, unsigned char const*, int, int, int, int, int, int):
✓ Branch 87 → 88 taken 13 times.
✗ Branch 87 → 92 not taken.
void layer_rgb32_lighten_darken_avx2<1>(unsigned char*, unsigned char const*, int, int, int, int, int, int):
✓ Branch 87 → 88 taken 13 times.
✗ Branch 87 → 92 not taken.
|
26 | if (width != mod2_width) { |
| 889 | 26 | int x = mod2_width; | |
| 890 | 26 | int alpha = (ovrp[x * 4 + 3] * level + 1) >> 8; | |
| 891 | 26 | int luma_ovr = (cyb * ovrp[x * 4] + cyg * ovrp[x * 4 + 1] + cyr * ovrp[x * 4 + 2]) >> 15; | |
| 892 | 26 | int luma_src = (cyb * dstp[x * 4] + cyg * dstp[x * 4 + 1] + cyr * dstp[x * 4 + 2]) >> 15; | |
| 893 | |||
| 894 | if constexpr (mode == LIGHTEN) | ||
| 895 |
2/2✓ Branch 88 → 89 taken 1 time.
✓ Branch 88 → 90 taken 12 times.
|
13 | alpha = luma_ovr > luma_src + thresh ? alpha : 0; |
| 896 | else // DARKEN | ||
| 897 |
2/2✓ Branch 88 → 89 taken 7 times.
✓ Branch 88 → 90 taken 6 times.
|
13 | alpha = luma_ovr < luma_src - thresh ? alpha : 0; |
| 898 | |||
| 899 | 26 | dstp[x * 4] = dstp[x * 4] + (((ovrp[x * 4] - dstp[x * 4]) * alpha + rounder) >> 8); | |
| 900 | 26 | dstp[x * 4 + 1] = dstp[x * 4 + 1] + (((ovrp[x * 4 + 1] - dstp[x * 4 + 1]) * alpha + rounder) >> 8); | |
| 901 | 26 | dstp[x * 4 + 2] = dstp[x * 4 + 2] + (((ovrp[x * 4 + 2] - dstp[x * 4 + 2]) * alpha + rounder) >> 8); | |
| 902 | 26 | dstp[x * 4 + 3] = dstp[x * 4 + 3] + (((ovrp[x * 4 + 3] - dstp[x * 4 + 3]) * alpha + rounder) >> 8); | |
| 903 | } | ||
| 904 | |||
| 905 | 26 | dstp += dst_pitch; | |
| 906 | 26 | ovrp += overlay_pitch; | |
| 907 | } | ||
| 908 | 8 | } | |
| 909 | |||
| 910 | // instantiate | ||
| 911 | template void layer_rgb32_lighten_darken_avx2<LIGHTEN>(BYTE* dstp, const BYTE* ovrp, int dst_pitch, int overlay_pitch, int width, int height, int level, int thresh); | ||
| 912 | template void layer_rgb32_lighten_darken_avx2<DARKEN>(BYTE* dstp, const BYTE* ovrp, int dst_pitch, int overlay_pitch, int width, int height, int level, int thresh); | ||
| 913 | |||
| 914 |