GCC Code Coverage Report


Directory: avs_core/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 88.3% 545 / 0 / 617
Functions: 91.2% 31 / 0 / 34
Branches: 84.4% 130 / 0 / 154

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/4
void 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/4
void 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/4
void 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/4
void 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/4
void 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/4
void 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/4
void 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/4
void 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/4
void 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/4
void 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/4
void 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/4
void 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/4
void 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/4
void 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/4
void 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/4
void 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/4
void 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/4
void 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/4
void 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/4
void 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/4
void 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/4
void 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/4
void 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/4
void 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/4
void 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