GCC Code Coverage Report


Directory: avs_core/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 20.7% 114 / 0 / 551
Functions: 27.3% 6 / 0 / 22
Branches: 14.9% 77 / 0 / 516

filters/overlay/OF_blend.cpp
Line Branch Exec Source
1 // Avisynth v2.5. Copyright 2002 Ben Rudiak-Gould et al.
2 // http://avisynth.nl
3
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 // Overlay (c) 2003, 2004 by Klaus Post
36
37 #include "overlayfunctions.h"
38 #include "blend_common.h"
39 #ifdef INTEL_INTRINSICS
40 #include "intel/blend_common_sse.h"
41 #include "intel/blend_common_avx2.h"
42 #endif
43 #ifdef NEON_INTRINSICS
44 #include "aarch64/blend_common_neon.h"
45 #endif
46
47
48 #include <stdint.h>
49 #include <type_traits>
50
51 // ---------------------------------------------------------------------------
52 // Getters: select the best masked_merge_fn_t* / masked_merge_float_fn_t*
53 // for the current CPU and plane type (luma vs chroma).
54 // ---------------------------------------------------------------------------
55
56 // Float masked blend — placement-aware, parallel structure to the integer getter.
57 // is_lumamask_based_chroma=false → always MASK444 (luma). is_lumamask_based_chroma=true → placement-aware.
58 // Note that when we use an exactly calculated and sized chroma mask, which is actually
59 // in 1:1 relation with the chroma plane, then is_lumamask_based_chroma=false must be given here.
60 static masked_merge_float_fn_t* get_overlay_blend_masked_float_fn(
61 int cpuFlags, bool is_lumamask_based_chroma, int placement, const VideoInfo& vi_internal)
62 {
63 MaskMode maskMode = MASK444;
64 if (is_lumamask_based_chroma) {
65 if (vi_internal.IsYV411())
66 maskMode = MASK411;
67 else if (vi_internal.Is420())
68 maskMode = (placement == PLACEMENT_MPEG1) ? MASK420 : (placement == PLACEMENT_TOPLEFT) ? MASK420_TOPLEFT : MASK420_MPEG2;
69 else if (vi_internal.Is422())
70 maskMode = (placement == PLACEMENT_MPEG1) ? MASK422 : (placement == PLACEMENT_TOPLEFT) ? MASK422_TOPLEFT : MASK422_MPEG2;
71 }
72
73 #ifdef INTEL_INTRINSICS
74 if (cpuFlags & CPUF_AVX2) return get_overlay_blend_masked_float_fn_avx2(is_lumamask_based_chroma, maskMode);
75 if (cpuFlags & CPUF_SSE4_1) return get_overlay_blend_masked_float_fn_sse41(is_lumamask_based_chroma, maskMode);
76 if ((cpuFlags & CPUF_SSE2) && maskMode == MASK444) return &masked_merge_float_sse2;
77 #endif
78 #ifdef NEON_INTRINSICS
79 if (cpuFlags & CPUF_ARM_NEON) return get_overlay_blend_masked_float_fn_neon(is_lumamask_based_chroma, maskMode);
80 #endif
81 return get_overlay_blend_masked_float_fn_c(is_lumamask_based_chroma, maskMode);
82 }
83
84 // Integer masked blend: compute MaskMode from the internal VI + placement,
85 // then dispatch to the appropriate SIMD getter.
86 // is_lumamask_based_chroma=false → always MASK444 (luma plane is never sub-sampled).
87 // Note that when we use an exactly calculated and sized chroma mask, which is actually
88 // in 1:1 relation with the chroma plane, then is_lumamask_based_chroma=false must be given here.
89 9 static masked_merge_fn_t* get_overlay_blend_masked_fn(
90 int cpuFlags, bool is_lumamask_based_chroma, int placement, const VideoInfo& vi_internal)
91 {
92 9 MaskMode maskMode = MASK444;
93
2/2
✓ Branch 2 → 3 taken 3 times.
✓ Branch 2 → 20 taken 6 times.
9 if (is_lumamask_based_chroma) {
94
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 3 times.
3 if (vi_internal.IsYV411())
95 maskMode = MASK411;
96
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 13 taken 3 times.
3 else if (vi_internal.Is420())
97 maskMode = (placement == PLACEMENT_MPEG1) ? MASK420 : (placement == PLACEMENT_TOPLEFT) ? MASK420_TOPLEFT : MASK420_MPEG2;
98
1/2
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 20 taken 3 times.
3 else if (vi_internal.Is422())
99 maskMode = (placement == PLACEMENT_MPEG1) ? MASK422 : (placement == PLACEMENT_TOPLEFT) ? MASK422_TOPLEFT : MASK422_MPEG2;
100 }
101
102 #ifdef INTEL_INTRINSICS
103
1/2
✓ Branch 20 → 21 taken 9 times.
✗ Branch 20 → 23 not taken.
9 if (cpuFlags & CPUF_AVX2) return get_overlay_blend_masked_fn_avx2(is_lumamask_based_chroma, maskMode);
104 if (cpuFlags & CPUF_SSE4_1) return get_overlay_blend_masked_fn_sse41(is_lumamask_based_chroma, maskMode);
105 #endif
106 #ifdef NEON_INTRINSICS
107 if (cpuFlags & CPUF_ARM_NEON) return get_overlay_blend_masked_fn_neon(is_lumamask_based_chroma, maskMode);
108 #endif
109 return get_overlay_blend_masked_fn_c(is_lumamask_based_chroma, maskMode);
110 }
111
112 // Wrappers: adapt overlay_blend_plane_masked_opacity_t to weighted_merge SIMD functions.
113 // weight = round(opacity_f * 32768); invweight = 32768 - weight. mask ptr is ignored.
114 #ifdef NEON_INTRINSICS
115 static void overlay_blend_neon_weighted_wrap(
116 BYTE* p1, const BYTE* p2, const BYTE* /*mask*/,
117 const int p1_pitch, const int p2_pitch, const int /*mask_pitch*/,
118 const int width, const int height, const float opacity_f,
119 const int bits_per_pixel)
120 {
121 const int weight = static_cast<int>(opacity_f * 32768 + 0.5f);
122 if (weight == 0 || weight == 32768) {
123 // Avoid calling the full weighted_merge when one of the weights is zero, in case of no early out
124 weighted_merge_return_a_or_b(p1, p2, p1_pitch, p2_pitch, width, height, weight, 32768 - weight, bits_per_pixel);
125 return;
126 }
127 weighted_merge_neon(p1, p2, p1_pitch, p2_pitch, width, height, weight, 32768 - weight, bits_per_pixel);
128 }
129
130 static void overlay_blend_neon_weighted_float_wrap(
131 BYTE* p1, const BYTE* p2, const BYTE* /*mask*/,
132 const int p1_pitch, const int p2_pitch, const int /*mask_pitch*/,
133 const int width, const int height, const float opacity_f,
134 const int /*bits_per_pixel*/)
135 {
136 weighted_merge_float_neon(p1, p2, p1_pitch, p2_pitch, width, height, opacity_f);
137 }
138 #endif // NEON_INTRINSICS
139
140 #ifdef INTEL_INTRINSICS
141 18 static void overlay_blend_weighted_avx2(
142 BYTE* p1, const BYTE* p2, const BYTE* /*mask*/,
143 const int p1_pitch, const int p2_pitch, const int /*mask_pitch*/,
144 const int width, const int height, const float opacity_f,
145 const int bits_per_pixel)
146 {
147 18 const int weight = static_cast<int>(opacity_f * 32768 + 0.5f);
148
2/4
✓ Branch 2 → 3 taken 18 times.
✗ Branch 2 → 4 not taken.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 6 taken 18 times.
18 if (weight == 0 || weight == 32768) {
149 // Avoid calling the full weighted_merge when one of the weights is zero, in case of no early out
150 weighted_merge_return_a_or_b(p1, p2, p1_pitch, p2_pitch, width, height, weight, 32768 - weight, bits_per_pixel);
151 return;
152 }
153 18 weighted_merge_avx2(p1, p2, p1_pitch, p2_pitch, width, height, weight, 32768 - weight, bits_per_pixel);
154 }
155
156 static void overlay_blend_weighted_sse2(
157 BYTE* p1, const BYTE* p2, const BYTE* /*mask*/,
158 const int p1_pitch, const int p2_pitch, const int /*mask_pitch*/,
159 const int width, const int height, const float opacity_f,
160 const int bits_per_pixel)
161 {
162 const int weight = static_cast<int>(opacity_f * 32768 + 0.5f);
163 if (weight == 0 || weight == 32768) {
164 // Avoid calling the full weighted_merge when one of the weights is zero, in case of no early out
165 weighted_merge_return_a_or_b(p1, p2, p1_pitch, p2_pitch, width, height, weight, 32768 - weight, bits_per_pixel);
166 return;
167 }
168 weighted_merge_sse2(p1, p2, p1_pitch, p2_pitch, width, height, weight, 32768 - weight, bits_per_pixel);
169 }
170
171 static void overlay_blend_weighted_float_avx2(
172 BYTE* p1, const BYTE* p2, const BYTE* /*mask*/,
173 const int p1_pitch, const int p2_pitch, const int /*mask_pitch*/,
174 const int width, const int height, const float opacity_f,
175 const int /*bits_per_pixel*/)
176 {
177 weighted_merge_float_avx2(p1, p2, p1_pitch, p2_pitch, width, height, opacity_f);
178 }
179
180 static void overlay_blend_weighted_float_sse2(
181 BYTE* p1, const BYTE* p2, const BYTE* /*mask*/,
182 const int p1_pitch, const int p2_pitch, const int /*mask_pitch*/,
183 const int width, const int height, const float opacity_f,
184 const int /*bits_per_pixel*/)
185 {
186 weighted_merge_float_sse2(p1, p2, p1_pitch, p2_pitch, width, height, opacity_f);
187 }
188 #endif
189
190 // Wrapper: adapt overlay_blend_plane_masked_opacity_t to weighted_merge_c.
191 // weight = round(opacity_f * 32768); invweight = 32768 - weight.
192 static void overlay_blend_weighted_c(
193 BYTE* p1, const BYTE* p2, const BYTE* /*mask*/,
194 const int p1_pitch, const int p2_pitch, const int /*mask_pitch*/,
195 const int width, const int height, const float opacity_f,
196 const int bits_per_pixel)
197 {
198 const int weight = static_cast<int>(opacity_f * 32768 + 0.5f);
199 if (weight == 0 || weight == 32768) {
200 // Avoid calling the full weighted_merge_c when one of the weights is zero, in case of no early out
201 weighted_merge_return_a_or_b(p1, p2, p1_pitch, p2_pitch, width, height, weight, 32768 - weight, bits_per_pixel);
202 return;
203 }
204 weighted_merge_c(p1, p2, p1_pitch, p2_pitch, width, height, weight, 32768 - weight, bits_per_pixel);
205 }
206
207 static void overlay_blend_weighted_float_c(
208 BYTE* p1, const BYTE* p2, const BYTE* /*mask*/,
209 const int p1_pitch, const int p2_pitch, const int /*mask_pitch*/,
210 const int width, const int height, const float opacity_f,
211 const int /*bits_per_pixel*/)
212 {
213 weighted_merge_float_c(p1, p2, p1_pitch, p2_pitch, width, height, opacity_f);
214 }
215
216 // ---------------------------------------------------------------------------
217 // Per-row chroma scratch helpers — runtime-dispatch on MaskMode to call the
218 // compile-time prepare_effective_mask_for_row.
219 // The caller pre-sizes `buf` to chroma_w; mask_pitch is in elements, not bytes.
220 // ---------------------------------------------------------------------------
221 template<typename pixel_t, bool full_opacity = true>
222 static void do_fill_chroma_row(
223 std::vector<pixel_t>& buf, const pixel_t* luma_row,
224 int luma_pitch_pixels, int chroma_w, MaskMode mode,
225 int opacity_i = 0, int half = 0, MagicDiv magic = {})
226 {
227 switch (mode) {
228 case MASK411:
229 prepare_effective_mask_for_row<MASK411, pixel_t, full_opacity>(luma_row, luma_pitch_pixels, chroma_w, buf, opacity_i, half, magic); break;
230 case MASK420:
231 prepare_effective_mask_for_row<MASK420, pixel_t, full_opacity>(luma_row, luma_pitch_pixels, chroma_w, buf, opacity_i, half, magic); break;
232 case MASK420_MPEG2:
233 prepare_effective_mask_for_row<MASK420_MPEG2, pixel_t, full_opacity>(luma_row, luma_pitch_pixels, chroma_w, buf, opacity_i, half, magic); break;
234 case MASK420_TOPLEFT:
235 prepare_effective_mask_for_row<MASK420_TOPLEFT, pixel_t, full_opacity>(luma_row, luma_pitch_pixels, chroma_w, buf, opacity_i, half, magic); break;
236 case MASK422:
237 prepare_effective_mask_for_row<MASK422, pixel_t, full_opacity>(luma_row, luma_pitch_pixels, chroma_w, buf, opacity_i, half, magic); break;
238 case MASK422_MPEG2:
239 prepare_effective_mask_for_row<MASK422_MPEG2, pixel_t, full_opacity>(luma_row, luma_pitch_pixels, chroma_w, buf, opacity_i, half, magic); break;
240 case MASK422_TOPLEFT:
241 prepare_effective_mask_for_row<MASK422_TOPLEFT, pixel_t, full_opacity>(luma_row, luma_pitch_pixels, chroma_w, buf, opacity_i, half, magic); break;
242 default: break;
243 }
244 }
245
246 template<bool full_opacity = true>
247 static void do_fill_chroma_row_f(
248 std::vector<float>& buf, const float* luma_row,
249 int luma_pitch_floats, int chroma_w, MaskMode mode,
250 float opacity_f = 1.0f)
251 {
252 switch (mode) {
253 case MASK411:
254 prepare_effective_mask_for_row_float_c<MASK411, full_opacity>(luma_row, luma_pitch_floats, chroma_w, buf, opacity_f); break;
255 case MASK420:
256 prepare_effective_mask_for_row_float_c<MASK420, full_opacity>(luma_row, luma_pitch_floats, chroma_w, buf, opacity_f); break;
257 case MASK420_MPEG2:
258 prepare_effective_mask_for_row_float_c<MASK420_MPEG2, full_opacity>(luma_row, luma_pitch_floats, chroma_w, buf, opacity_f); break;
259 case MASK420_TOPLEFT:
260 prepare_effective_mask_for_row_float_c<MASK420_TOPLEFT, full_opacity>(luma_row, luma_pitch_floats, chroma_w, buf, opacity_f); break;
261 case MASK422:
262 prepare_effective_mask_for_row_float_c<MASK422, full_opacity>(luma_row, luma_pitch_floats, chroma_w, buf, opacity_f); break;
263 case MASK422_MPEG2:
264 prepare_effective_mask_for_row_float_c<MASK422_MPEG2, full_opacity>(luma_row, luma_pitch_floats, chroma_w, buf, opacity_f); break;
265 case MASK422_TOPLEFT:
266 prepare_effective_mask_for_row_float_c<MASK422_TOPLEFT, full_opacity>(luma_row, luma_pitch_floats, chroma_w, buf, opacity_f); break;
267 default: break;
268 }
269 }
270
271 // do_fill_chroma_row_sse41 and do_fill_chroma_row_avx2 are declared in
272 // intel/blend_common_sse.h and intel/blend_common_avx2.h respectively,
273 // and defined (with explicit instantiations) in their matching .cpp files.
274
275 6 void OL_BlendImage::DoBlendImageMask(ImageOverlayInternal* base, ImageOverlayInternal* overlay, ImageOverlayInternal* mask) {
276
1/2
✓ Branch 2 → 3 taken 6 times.
✗ Branch 2 → 4 not taken.
6 if (bits_per_pixel == 8)
277 6 BlendImageMask<uint8_t>(base, overlay, mask);
278 else if (bits_per_pixel <= 16)
279 BlendImageMask<uint16_t>(base, overlay, mask);
280 else if (bits_per_pixel == 32)
281 BlendImageMask<float>(base, overlay, mask);
282 6 }
283
284 7 void OL_BlendImage::DoBlendImage(ImageOverlayInternal* base, ImageOverlayInternal* overlay) {
285
1/2
✓ Branch 2 → 3 taken 7 times.
✗ Branch 2 → 4 not taken.
7 if (bits_per_pixel == 8)
286 7 BlendImage<uint8_t>(base, overlay);
287 else if (bits_per_pixel <= 16)
288 BlendImage<uint16_t>(base, overlay);
289 else if (bits_per_pixel == 32)
290 BlendImage<float>(base, overlay);
291 7 }
292
293
294 template<typename pixel_t>
295 6 void OL_BlendImage::BlendImageMask(ImageOverlayInternal* base, ImageOverlayInternal* overlay, ImageOverlayInternal* mask) {
296 6 int w = base->w();
297 6 int h = base->h();
298
299 6 int planeindex_from = 0;
300 6 int planeindex_to = 0;
301
302
1/6
void OL_BlendImage::BlendImageMask<float>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 4 → 5 not taken.
✗ Branch 4 → 8 not taken.
void OL_BlendImage::BlendImageMask<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 4 → 5 taken 6 times.
✗ Branch 4 → 8 not taken.
void OL_BlendImage::BlendImageMask<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 4 → 5 not taken.
✗ Branch 4 → 8 not taken.
6 if (of_mode == OF_Blend) {
303 6 planeindex_from = 0;
304
1/6
void OL_BlendImage::BlendImageMask<float>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 5 → 6 not taken.
✗ Branch 5 → 7 not taken.
void OL_BlendImage::BlendImageMask<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 6 times.
void OL_BlendImage::BlendImageMask<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 5 → 6 not taken.
✗ Branch 5 → 7 not taken.
6 planeindex_to = greyscale ? 0 : 2;
305 }
306 else if (of_mode == OF_Luma) {
307 planeindex_from = 0;
308 planeindex_to = 0;
309 }
310 else if (of_mode == OF_Chroma) {
311 if (greyscale)
312 return;
313 planeindex_from = 1;
314 planeindex_to = 2;
315 }
316
317 6 const int cpuFlags = env->GetCPUFlags();
318
319 // For RGB all planes are full-resolution (no subsampling), so G/B planes use the luma path.
320 // For YUV, planes 1/2 are chroma and use the placement-aware path.
321 6 const bool use_chroma_fn = !rgb;
322
323 // Per-row scratch path: imghelpers stores the luma mask pointer in planes 1/2 for
324 // subsampled greymask; BlendImageMask fills a per-row scratch buffer from luma rows,
325 // shared for both U and V.
326 // Active when use444=false keeps vi_internal at 420/422/411 AND greymask=true.
327 // Not active when use444=true or input is natively 444/RGB: vi_internal is then at full
328 // chroma resolution, Is420/Is422/IsYV411 == false, so is_subsampled = false.
329
5/18
void OL_BlendImage::BlendImageMask<float>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 16 → 17 not taken.
✗ Branch 16 → 21 not taken.
✗ Branch 18 → 19 not taken.
✗ Branch 18 → 21 not taken.
✗ Branch 20 → 21 not taken.
✗ Branch 20 → 22 not taken.
void OL_BlendImage::BlendImageMask<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 16 → 17 taken 4 times.
✓ Branch 16 → 21 taken 2 times.
✓ Branch 18 → 19 taken 3 times.
✓ Branch 18 → 21 taken 1 time.
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 3 times.
void OL_BlendImage::BlendImageMask<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 16 → 17 not taken.
✗ Branch 16 → 21 not taken.
✗ Branch 18 → 19 not taken.
✗ Branch 18 → 21 not taken.
✗ Branch 20 → 21 not taken.
✗ Branch 20 → 22 not taken.
6 const bool is_subsampled = vi_internal.Is420() || vi_internal.Is422() || vi_internal.IsYV411();
330
4/18
void OL_BlendImage::BlendImageMask<float>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 23 → 24 not taken.
✗ Branch 23 → 27 not taken.
✗ Branch 24 → 25 not taken.
✗ Branch 24 → 27 not taken.
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 27 not taken.
void OL_BlendImage::BlendImageMask<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 23 → 24 taken 6 times.
✗ Branch 23 → 27 not taken.
✓ Branch 24 → 25 taken 6 times.
✗ Branch 24 → 27 not taken.
✓ Branch 25 → 26 taken 3 times.
✓ Branch 25 → 27 taken 3 times.
void OL_BlendImage::BlendImageMask<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 23 → 24 not taken.
✗ Branch 23 → 27 not taken.
✗ Branch 24 → 25 not taken.
✗ Branch 24 → 27 not taken.
✗ Branch 25 → 26 not taken.
✗ Branch 25 → 27 not taken.
6 const bool use_scratch_path = greymask_mask && use_chroma_fn && is_subsampled;
331
332 if constexpr (std::is_same_v<pixel_t, float>) {
333 // Float path: separate luma/chroma function pointers.
334 // blend_fn_chroma uses is_lumamask_based_chroma=false (→ MASK444) in the normal path
335 // because the mask plane is already at chroma resolution (greymask=false or non-subsampled).
336 // The scratch path pre-fills chroma-resolution scratch and calls blend_fn_luma on it.
337 masked_merge_float_fn_t* blend_fn_luma = get_overlay_blend_masked_float_fn(cpuFlags, false, placement, vi_internal);
338 masked_merge_float_fn_t* blend_fn_chroma =
339 use_scratch_path ? blend_fn_luma
340 : get_overlay_blend_masked_float_fn(cpuFlags, true, placement, vi_internal);
341
342 if (use_scratch_path) {
343 // Luma plane (if applicable)
344 if (planeindex_from == 0) {
345 blend_fn_luma(
346 base->GetPtrByIndex(0), overlay->GetPtrByIndex(0), mask->GetPtrByIndex(0),
347 base->GetPitchByIndex(0), overlay->GetPitchByIndex(0), mask->GetPitchByIndex(0),
348 w, h, opacity_f);
349 }
350 // Chroma planes: outer loop = rows, inner = planes.
351 // Scratch filled once per row, reused for all active chroma planes (U and V).
352 if (planeindex_to >= 1) {
353 MaskMode chroma_maskMode = MASK444;
354 if (vi_internal.IsYV411())
355 chroma_maskMode = MASK411;
356 else if (vi_internal.Is420())
357 chroma_maskMode = (placement == PLACEMENT_MPEG1) ? MASK420 : (placement == PLACEMENT_TOPLEFT) ? MASK420_TOPLEFT : MASK420_MPEG2;
358 else if (vi_internal.Is422())
359 chroma_maskMode = (placement == PLACEMENT_MPEG1) ? MASK422 : (placement == PLACEMENT_TOPLEFT) ? MASK422_TOPLEFT : MASK422_MPEG2;
360
361 const int xws = base->xSubSamplingShifts[1];
362 const int yhs = base->ySubSamplingShifts[1];
363 const int chroma_w = (xws > 0) ? ((base->xAccum() & ((1 << xws) - 1)) + w + (1 << xws) - 1) >> xws : w;
364 const int chroma_h = (yhs > 0) ? ((base->yAccum() & ((1 << yhs) - 1)) + h + (1 << yhs) - 1) >> yhs : h;
365 const int luma_mask_pitch_floats = mask->GetPitchByIndex(0) / (int)sizeof(float);
366 const int luma_mask_advance = (chroma_maskMode == MASK420 || chroma_maskMode == MASK420_MPEG2 || chroma_maskMode == MASK420_TOPLEFT)
367 ? luma_mask_pitch_floats * 2 : luma_mask_pitch_floats;
368 const int scratch_pitch = chroma_w * (int)sizeof(float);
369 std::vector<float> scratch(chroma_w);
370
371 // Mutable row pointers for each active chroma plane (p=1, p=2)
372 BYTE* basep[2] = { base->GetPtrByIndex(1), base->GetPtrByIndex(2) };
373 const BYTE* ovlp [2] = { overlay->GetPtrByIndex(1), overlay->GetPtrByIndex(2) };
374 const int base_pitch[2] = { base->GetPitchByIndex(1), base->GetPitchByIndex(2) };
375 const int ovl_pitch [2] = { overlay->GetPitchByIndex(1), overlay->GetPitchByIndex(2) };
376 const int n_planes = planeindex_to; // 1 or 2 active chroma planes (indices 0..n_planes-1)
377
378 const float* lmask = reinterpret_cast<const float*>(mask->GetPtrByIndex(0));
379 const bool chroma_full_opacity = (opacity_f == 1.0f);
380 #ifdef INTEL_INTRINSICS
381 const bool use_avx2_rowprep_f = (cpuFlags & CPUF_AVX2) != 0;
382 const bool use_sse41_rowprep_f = (cpuFlags & CPUF_SSE4_1) != 0;
383 #endif
384 #ifdef NEON_INTRINSICS
385 const bool use_neon_rowprep_f = (cpuFlags & CPUF_ARM_NEON) != 0;
386 #endif
387 for (int y = 0; y < chroma_h; y++) {
388 // Fill scratch once for this luma-mask row pair, baking opacity in.
389 #ifdef INTEL_INTRINSICS
390 if (use_avx2_rowprep_f) {
391 if (chroma_full_opacity)
392 do_fill_chroma_row_float_avx2<true> (scratch, lmask, luma_mask_pitch_floats, chroma_w, chroma_maskMode);
393 else
394 do_fill_chroma_row_float_avx2<false>(scratch, lmask, luma_mask_pitch_floats, chroma_w, chroma_maskMode, opacity_f);
395 } else if (use_sse41_rowprep_f) {
396 if (chroma_full_opacity)
397 do_fill_chroma_row_float_sse41<true> (scratch, lmask, luma_mask_pitch_floats, chroma_w, chroma_maskMode);
398 else
399 do_fill_chroma_row_float_sse41<false>(scratch, lmask, luma_mask_pitch_floats, chroma_w, chroma_maskMode, opacity_f);
400 } else
401 #elif defined(NEON_INTRINSICS)
402 if (use_neon_rowprep_f) {
403 if (chroma_full_opacity)
404 do_fill_chroma_row_float_neon<true>(scratch, lmask, luma_mask_pitch_floats, chroma_w, chroma_maskMode);
405 else
406 do_fill_chroma_row_float_neon<false>(scratch, lmask, luma_mask_pitch_floats, chroma_w, chroma_maskMode, opacity_f);
407 } else
408 #endif
409 {
410 if (chroma_full_opacity)
411 do_fill_chroma_row_f<true> (scratch, lmask, luma_mask_pitch_floats, chroma_w, chroma_maskMode);
412 else
413 do_fill_chroma_row_f<false>(scratch, lmask, luma_mask_pitch_floats, chroma_w, chroma_maskMode, opacity_f);
414 }
415 // Apply to each active chroma plane; opacity already baked — pass 1.0f.
416 for (int i = 0; i < n_planes; i++) {
417 blend_fn_luma(basep[i], ovlp[i], (const BYTE*)scratch.data(),
418 base_pitch[i], ovl_pitch[i], scratch_pitch, chroma_w, 1, 1.0f);
419 basep[i] += base_pitch[i];
420 ovlp [i] += ovl_pitch[i];
421 }
422 lmask += luma_mask_advance;
423 }
424 }
425 return;
426 }
427
428 // Normal path: greymask=false or non-subsampled — mask already at plane resolution.
429 for (int p = planeindex_from; p <= planeindex_to; p++) {
430 masked_merge_float_fn_t* fn = (p == 0 || !use_chroma_fn) ? blend_fn_luma : blend_fn_chroma;
431 const int xws_p = base->xSubSamplingShifts[p];
432 const int yhs_p = base->ySubSamplingShifts[p];
433 const int plane_w = (xws_p > 0) ? ((base->xAccum() & ((1 << xws_p) - 1)) + w + (1 << xws_p) - 1) >> xws_p : w;
434 const int plane_h = (yhs_p > 0) ? ((base->yAccum() & ((1 << yhs_p) - 1)) + h + (1 << yhs_p) - 1) >> yhs_p : h;
435 fn(base->GetPtrByIndex(p), overlay->GetPtrByIndex(p), mask->GetPtrByIndex(p),
436 base->GetPitchByIndex(p), overlay->GetPitchByIndex(p), mask->GetPitchByIndex(p),
437 plane_w, plane_h, opacity_f);
438 }
439 }
440 else {
441 // Integer path: opacity pre-scaled to [0, max_pixel_value].
442 6 const int opacity_i = static_cast<int>(opacity_f * ((1 << bits_per_pixel) - 1) + 0.5f);
443 6 masked_merge_fn_t* blend_fn_luma = get_overlay_blend_masked_fn(cpuFlags, false, placement, vi_internal);
444 3 masked_merge_fn_t* blend_fn_chroma =
445
2/4
void OL_BlendImage::BlendImageMask<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 29 → 30 taken 3 times.
✓ Branch 29 → 31 taken 3 times.
void OL_BlendImage::BlendImageMask<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 29 → 30 not taken.
✗ Branch 29 → 31 not taken.
6 use_scratch_path ? blend_fn_luma
446 3 : get_overlay_blend_masked_fn(cpuFlags, true, placement, vi_internal);
447
448
2/4
void OL_BlendImage::BlendImageMask<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 33 → 34 taken 3 times.
✓ Branch 33 → 110 taken 3 times.
void OL_BlendImage::BlendImageMask<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 33 → 34 not taken.
✗ Branch 33 → 110 not taken.
6 if (use_scratch_path) {
449 // Luma plane (if applicable)
450
1/4
void OL_BlendImage::BlendImageMask<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 34 → 35 taken 3 times.
✗ Branch 34 → 42 not taken.
void OL_BlendImage::BlendImageMask<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 34 → 35 not taken.
✗ Branch 34 → 42 not taken.
3 if (planeindex_from == 0) {
451 6 blend_fn_luma(
452 3 base->GetPtrByIndex(0), overlay->GetPtrByIndex(0), mask->GetPtrByIndex(0),
453 base->GetPitchByIndex(0), overlay->GetPitchByIndex(0), mask->GetPitchByIndex(0),
454 w, h, opacity_i, bits_per_pixel);
455 }
456 // Chroma planes: outer loop = rows, inner = planes.
457 // Scratch filled once per row, reused for all active chroma planes (U and V).
458
1/4
void OL_BlendImage::BlendImageMask<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 42 → 43 taken 3 times.
✗ Branch 42 → 109 not taken.
void OL_BlendImage::BlendImageMask<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 42 → 43 not taken.
✗ Branch 42 → 109 not taken.
3 if (planeindex_to >= 1) {
459 3 MaskMode chroma_maskMode = MASK444;
460
2/8
void OL_BlendImage::BlendImageMask<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 43 → 44 taken 3 times.
✗ Branch 43 → 138 not taken.
✗ Branch 44 → 45 not taken.
✓ Branch 44 → 46 taken 3 times.
void OL_BlendImage::BlendImageMask<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 43 → 44 not taken.
✗ Branch 43 → 138 not taken.
✗ Branch 44 → 45 not taken.
✗ Branch 44 → 46 not taken.
3 if (vi_internal.IsYV411())
461 chroma_maskMode = MASK411;
462
3/8
void OL_BlendImage::BlendImageMask<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 46 → 47 taken 3 times.
✗ Branch 46 → 138 not taken.
✓ Branch 47 → 48 taken 2 times.
✓ Branch 47 → 53 taken 1 time.
void OL_BlendImage::BlendImageMask<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 46 → 47 not taken.
✗ Branch 46 → 138 not taken.
✗ Branch 47 → 48 not taken.
✗ Branch 47 → 53 not taken.
3 else if (vi_internal.Is420())
463
2/8
void OL_BlendImage::BlendImageMask<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 48 → 49 taken 2 times.
✗ Branch 48 → 52 not taken.
✗ Branch 49 → 50 not taken.
✓ Branch 49 → 51 taken 2 times.
void OL_BlendImage::BlendImageMask<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 48 → 49 not taken.
✗ Branch 48 → 52 not taken.
✗ Branch 49 → 50 not taken.
✗ Branch 49 → 51 not taken.
2 chroma_maskMode = (placement == PLACEMENT_MPEG1) ? MASK420 : (placement == PLACEMENT_TOPLEFT) ? MASK420_TOPLEFT : MASK420_MPEG2;
464
2/8
void OL_BlendImage::BlendImageMask<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 53 → 54 taken 1 time.
✗ Branch 53 → 138 not taken.
✓ Branch 54 → 55 taken 1 time.
✗ Branch 54 → 60 not taken.
void OL_BlendImage::BlendImageMask<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 53 → 54 not taken.
✗ Branch 53 → 138 not taken.
✗ Branch 54 → 55 not taken.
✗ Branch 54 → 60 not taken.
1 else if (vi_internal.Is422())
465
2/8
void OL_BlendImage::BlendImageMask<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 55 → 56 taken 1 time.
✗ Branch 55 → 59 not taken.
✗ Branch 56 → 57 not taken.
✓ Branch 56 → 58 taken 1 time.
void OL_BlendImage::BlendImageMask<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 55 → 56 not taken.
✗ Branch 55 → 59 not taken.
✗ Branch 56 → 57 not taken.
✗ Branch 56 → 58 not taken.
1 chroma_maskMode = (placement == PLACEMENT_MPEG1) ? MASK422 : (placement == PLACEMENT_TOPLEFT) ? MASK422_TOPLEFT : MASK422_MPEG2;
466
467 3 const int xws = base->xSubSamplingShifts[1];
468 3 const int yhs = base->ySubSamplingShifts[1];
469
1/4
void OL_BlendImage::BlendImageMask<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 60 → 61 taken 3 times.
✗ Branch 60 → 63 not taken.
void OL_BlendImage::BlendImageMask<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 60 → 61 not taken.
✗ Branch 60 → 63 not taken.
3 const int chroma_w = (xws > 0) ? ((base->xAccum() & ((1 << xws) - 1)) + w + (1 << xws) - 1) >> xws : w;
470
2/4
void OL_BlendImage::BlendImageMask<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 64 → 65 taken 2 times.
✓ Branch 64 → 67 taken 1 time.
void OL_BlendImage::BlendImageMask<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 64 → 65 not taken.
✗ Branch 64 → 67 not taken.
3 const int chroma_h = (yhs > 0) ? ((base->yAccum() & ((1 << yhs) - 1)) + h + (1 << yhs) - 1) >> yhs : h;
471 3 const int luma_mask_pitch_pixels = mask->GetPitchByIndex(0) / (int)sizeof(pixel_t);
472
3/8
void OL_BlendImage::BlendImageMask<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 70 → 71 taken 1 time.
✓ Branch 70 → 72 taken 2 times.
✗ Branch 71 → 72 not taken.
✓ Branch 71 → 73 taken 1 time.
void OL_BlendImage::BlendImageMask<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 70 → 71 not taken.
✗ Branch 70 → 72 not taken.
✗ Branch 71 → 72 not taken.
✗ Branch 71 → 73 not taken.
3 const int luma_mask_advance = (chroma_maskMode == MASK420 || chroma_maskMode == MASK420_MPEG2 || chroma_maskMode == MASK420_TOPLEFT)
473
1/4
void OL_BlendImage::BlendImageMask<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 69 → 70 taken 3 times.
✗ Branch 69 → 72 not taken.
void OL_BlendImage::BlendImageMask<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 69 → 70 not taken.
✗ Branch 69 → 72 not taken.
3 ? luma_mask_pitch_pixels * 2 : luma_mask_pitch_pixels;
474 3 const int scratch_pitch = chroma_w * (int)sizeof(pixel_t);
475
1/4
void OL_BlendImage::BlendImageMask<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 76 → 77 taken 3 times.
✗ Branch 76 → 133 not taken.
void OL_BlendImage::BlendImageMask<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 76 → 77 not taken.
✗ Branch 76 → 133 not taken.
3 std::vector<pixel_t> scratch(chroma_w);
476
477 // Mutable row pointers for each active chroma plane (p=1, p=2)
478 3 BYTE* basep[2] = { base->GetPtrByIndex(1), base->GetPtrByIndex(2) };
479 3 const BYTE* ovlp [2] = { overlay->GetPtrByIndex(1), overlay->GetPtrByIndex(2) };
480 3 const int base_pitch[2] = { base->GetPitchByIndex(1), base->GetPitchByIndex(2) };
481 3 const int ovl_pitch [2] = { overlay->GetPitchByIndex(1), overlay->GetPitchByIndex(2) };
482 3 const int n_planes = planeindex_to; // 1 or 2 active chroma planes (indices 0..n_planes-1)
483
484 3 const pixel_t* lmask = reinterpret_cast<const pixel_t*>(mask->GetPtrByIndex(0));
485 3 const int max_pixel_value = (1 << bits_per_pixel) - 1;
486 3 const MagicDiv mag = get_magic_div(bits_per_pixel);
487 3 const int half_val = max_pixel_value / 2;
488 3 const bool chroma_full_opacity = (opacity_i == max_pixel_value);
489 #ifdef INTEL_INTRINSICS
490 3 const bool use_avx2_rowprep = (cpuFlags & CPUF_AVX2) != 0;
491 3 const bool use_sse41_rowprep = (cpuFlags & CPUF_SSE4_1) != 0;
492 #endif
493 #ifdef NEON_INTRINSICS
494 const bool use_neon_rowprep = (cpuFlags & CPUF_ARM_NEON) != 0;
495 #endif
496
497
2/4
void OL_BlendImage::BlendImageMask<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 106 → 89 taken 9 times.
✓ Branch 106 → 107 taken 3 times.
void OL_BlendImage::BlendImageMask<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 106 → 89 not taken.
✗ Branch 106 → 107 not taken.
12 for (int y = 0; y < chroma_h; y++) {
498 // Fill scratch once for this luma-mask row pair, baking opacity in.
499 #ifdef INTEL_INTRINSICS
500
1/4
void OL_BlendImage::BlendImageMask<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 89 → 90 taken 9 times.
✗ Branch 89 → 93 not taken.
void OL_BlendImage::BlendImageMask<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 89 → 90 not taken.
✗ Branch 89 → 93 not taken.
9 if (use_avx2_rowprep) {
501
1/4
void OL_BlendImage::BlendImageMask<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 90 → 91 not taken.
✓ Branch 90 → 92 taken 9 times.
void OL_BlendImage::BlendImageMask<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 90 → 91 not taken.
✗ Branch 90 → 92 not taken.
9 if (chroma_full_opacity)
502 do_fill_chroma_row_avx2<pixel_t, true> (scratch, lmask, luma_mask_pitch_pixels, chroma_w, chroma_maskMode);
503 else
504
1/4
void OL_BlendImage::BlendImageMask<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 92 → 100 taken 9 times.
✗ Branch 92 → 136 not taken.
void OL_BlendImage::BlendImageMask<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 92 → 100 not taken.
✗ Branch 92 → 136 not taken.
9 do_fill_chroma_row_avx2<pixel_t, false>(scratch, lmask, luma_mask_pitch_pixels, chroma_w, chroma_maskMode, opacity_i, half_val, mag);
505 } else if (use_sse41_rowprep) {
506 if (chroma_full_opacity)
507 do_fill_chroma_row_sse41<pixel_t, true> (scratch, lmask, luma_mask_pitch_pixels, chroma_w, chroma_maskMode);
508 else
509 do_fill_chroma_row_sse41<pixel_t, false>(scratch, lmask, luma_mask_pitch_pixels, chroma_w, chroma_maskMode, opacity_i, half_val, mag);
510 } else
511 #elif defined(NEON_INTRINSICS)
512 if (use_neon_rowprep) {
513 if (chroma_full_opacity)
514 do_fill_chroma_row_neon<pixel_t, true>(scratch, lmask, luma_mask_pitch_pixels, chroma_w, chroma_maskMode);
515 else
516 do_fill_chroma_row_neon<pixel_t, false>(scratch, lmask, luma_mask_pitch_pixels, chroma_w, chroma_maskMode, opacity_i, half_val, mag);
517 }
518 else
519 #endif
520 {
521 if (chroma_full_opacity)
522 do_fill_chroma_row<pixel_t, true> (scratch, lmask, luma_mask_pitch_pixels, chroma_w, chroma_maskMode);
523 else
524 do_fill_chroma_row<pixel_t, false>(scratch, lmask, luma_mask_pitch_pixels, chroma_w, chroma_maskMode, opacity_i, half_val, mag);
525 }
526 // Apply to each active chroma plane; opacity already baked — pass max_pixel_value.
527
2/4
void OL_BlendImage::BlendImageMask<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 104 → 101 taken 18 times.
✓ Branch 104 → 105 taken 9 times.
void OL_BlendImage::BlendImageMask<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 104 → 101 not taken.
✗ Branch 104 → 105 not taken.
27 for (int i = 0; i < n_planes; i++) {
528
1/4
void OL_BlendImage::BlendImageMask<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 102 → 103 taken 18 times.
✗ Branch 102 → 136 not taken.
void OL_BlendImage::BlendImageMask<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 102 → 103 not taken.
✗ Branch 102 → 136 not taken.
18 blend_fn_luma(basep[i], ovlp[i], (const BYTE*)scratch.data(),
529 18 base_pitch[i], ovl_pitch[i], scratch_pitch, chroma_w, 1, max_pixel_value, bits_per_pixel);
530 18 basep[i] += base_pitch[i];
531 18 ovlp [i] += ovl_pitch[i];
532 }
533 9 lmask += luma_mask_advance;
534 }
535 3 }
536 3 return;
537 }
538
539 // Normal path: greymask=false or non-subsampled — mask already at plane resolution.
540
2/4
void OL_BlendImage::BlendImageMask<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 131 → 111 taken 9 times.
✓ Branch 131 → 132 taken 3 times.
void OL_BlendImage::BlendImageMask<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 131 → 111 not taken.
✗ Branch 131 → 132 not taken.
12 for (int p = planeindex_from; p <= planeindex_to; p++) {
541
3/8
void OL_BlendImage::BlendImageMask<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 111 → 112 taken 6 times.
✓ Branch 111 → 113 taken 3 times.
✗ Branch 112 → 113 not taken.
✓ Branch 112 → 114 taken 6 times.
void OL_BlendImage::BlendImageMask<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 111 → 112 not taken.
✗ Branch 111 → 113 not taken.
✗ Branch 112 → 113 not taken.
✗ Branch 112 → 114 not taken.
9 masked_merge_fn_t* fn = (p == 0 || !use_chroma_fn) ? blend_fn_luma : blend_fn_chroma;
542 9 const int xws_p = base->xSubSamplingShifts[p];
543 9 const int yhs_p = base->ySubSamplingShifts[p];
544
1/4
void OL_BlendImage::BlendImageMask<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 115 → 116 not taken.
✓ Branch 115 → 118 taken 9 times.
void OL_BlendImage::BlendImageMask<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 115 → 116 not taken.
✗ Branch 115 → 118 not taken.
9 const int plane_w = (xws_p > 0) ? ((base->xAccum() & ((1 << xws_p) - 1)) + w + (1 << xws_p) - 1) >> xws_p : w;
545
1/4
void OL_BlendImage::BlendImageMask<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 119 → 120 not taken.
✓ Branch 119 → 122 taken 9 times.
void OL_BlendImage::BlendImageMask<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 119 → 120 not taken.
✗ Branch 119 → 122 not taken.
9 const int plane_h = (yhs_p > 0) ? ((base->yAccum() & ((1 << yhs_p) - 1)) + h + (1 << yhs_p) - 1) >> yhs_p : h;
546 9 fn(base->GetPtrByIndex(p), overlay->GetPtrByIndex(p), mask->GetPtrByIndex(p),
547 base->GetPitchByIndex(p), overlay->GetPitchByIndex(p), mask->GetPitchByIndex(p),
548 plane_w, plane_h, opacity_i, bits_per_pixel);
549 }
550 }
551 }
552
553 template<typename pixel_t>
554 7 void OL_BlendImage::BlendImage(ImageOverlayInternal* base, ImageOverlayInternal* overlay) {
555 7 int w = base->w();
556 7 int h = base->h();
557
558 7 const int pixelsize = sizeof(pixel_t);
559
560 7 int planeindex_from = 0;
561 7 int planeindex_to = 0;
562
563
1/6
void OL_BlendImage::BlendImage<float>(ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 4 → 5 not taken.
✗ Branch 4 → 8 not taken.
void OL_BlendImage::BlendImage<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 4 → 5 taken 7 times.
✗ Branch 4 → 8 not taken.
void OL_BlendImage::BlendImage<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 4 → 5 not taken.
✗ Branch 4 → 8 not taken.
7 if (of_mode == OF_Blend) {
564 7 planeindex_from = 0;
565
1/6
void OL_BlendImage::BlendImage<float>(ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 5 → 6 not taken.
✗ Branch 5 → 7 not taken.
void OL_BlendImage::BlendImage<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 7 times.
void OL_BlendImage::BlendImage<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 5 → 6 not taken.
✗ Branch 5 → 7 not taken.
7 planeindex_to = greyscale ? 0 : 2;
566 }
567 else if (of_mode == OF_Luma) {
568 planeindex_from = 0;
569 planeindex_to = 0;
570 }
571 else if (of_mode == OF_Chroma) {
572 if (greyscale)
573 return;
574 planeindex_from = 1;
575 planeindex_to = 2;
576 }
577
578 // opacity == 0 was also an early-out case
579
2/6
void OL_BlendImage::BlendImage<float>(ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 31 not taken.
void OL_BlendImage::BlendImage<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 14 → 15 taken 1 time.
✓ Branch 14 → 31 taken 6 times.
void OL_BlendImage::BlendImage<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 31 not taken.
7 if (opacity == 256) {
580
2/6
void OL_BlendImage::BlendImage<float>(ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 30 → 16 not taken.
✗ Branch 30 → 64 not taken.
void OL_BlendImage::BlendImage<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 30 → 16 taken 3 times.
✓ Branch 30 → 59 taken 1 time.
void OL_BlendImage::BlendImage<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 30 → 16 not taken.
✗ Branch 30 → 59 not taken.
4 for (int p = planeindex_from; p <= planeindex_to; p++) {
581 3 const int xws_p = base->xSubSamplingShifts[p];
582 3 const int yhs_p = base->ySubSamplingShifts[p];
583
1/6
void OL_BlendImage::BlendImage<float>(ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 16 → 17 not taken.
✗ Branch 16 → 19 not taken.
void OL_BlendImage::BlendImage<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 19 taken 3 times.
void OL_BlendImage::BlendImage<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 16 → 17 not taken.
✗ Branch 16 → 19 not taken.
3 const int plane_w = (xws_p > 0) ? ((base->xAccum() & ((1 << xws_p) - 1)) + w + (1 << xws_p) - 1) >> xws_p : w;
584
1/6
void OL_BlendImage::BlendImage<float>(ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 20 → 21 not taken.
✗ Branch 20 → 23 not taken.
void OL_BlendImage::BlendImage<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 23 taken 3 times.
void OL_BlendImage::BlendImage<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 20 → 21 not taken.
✗ Branch 20 → 23 not taken.
3 const int plane_h = (yhs_p > 0) ? ((base->yAccum() & ((1 << yhs_p) - 1)) + h + (1 << yhs_p) - 1) >> yhs_p : h;
585 3 env->BitBlt(base->GetPtrByIndex(p), base->GetPitchByIndex(p), overlay->GetPtrByIndex(p), overlay->GetPitchByIndex(p), plane_w * pixelsize, plane_h);
586 }
587 }
588 else {
589 6 overlay_blend_plane_masked_opacity_t* blend_fn = nullptr;
590
591 #ifdef INTEL_INTRINSICS
592 if (pixelsize == 4 && (env->GetCPUFlags() & CPUF_AVX2)) {
593 blend_fn = overlay_blend_weighted_float_avx2;
594 }
595 else if (pixelsize == 4 && (env->GetCPUFlags() & CPUF_SSE2)) {
596 blend_fn = overlay_blend_weighted_float_sse2;
597 }
598
1/6
void OL_BlendImage::BlendImage<float>(ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 38 → 39 not taken.
✗ Branch 38 → 40 not taken.
void OL_BlendImage::BlendImage<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 33 → 34 taken 6 times.
✗ Branch 33 → 35 not taken.
void OL_BlendImage::BlendImage<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 33 → 34 not taken.
✗ Branch 33 → 35 not taken.
6 else if (env->GetCPUFlags() & CPUF_AVX2) {
599 6 blend_fn = overlay_blend_weighted_avx2; // handles all integer depths, width-exact
600 }
601 else if (env->GetCPUFlags() & CPUF_SSE2) {
602 blend_fn = overlay_blend_weighted_sse2; // handles all integer depths
603 }
604 else
605 #endif // INTEL_INTRINSICS
606 #ifdef NEON_INTRINSICS
607 if (pixelsize == 4 && (env->GetCPUFlags() & CPUF_ARM_NEON)) {
608 blend_fn = overlay_blend_neon_weighted_float_wrap;
609 }
610 else if (env->GetCPUFlags() & CPUF_ARM_NEON) {
611 blend_fn = overlay_blend_neon_weighted_wrap;
612 }
613 else
614 #endif // NEON_INTRINSICS
615 {
616 // pure C
617 if (bits_per_pixel == 32) blend_fn = overlay_blend_weighted_float_c;
618 else blend_fn = overlay_blend_weighted_c;
619 }
620 // end of new, float precision inside masked overlays
621
622
1/6
void OL_BlendImage::BlendImage<float>(ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 46 → 47 not taken.
✗ Branch 46 → 48 not taken.
void OL_BlendImage::BlendImage<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 41 → 42 not taken.
✓ Branch 41 → 43 taken 6 times.
void OL_BlendImage::BlendImage<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 41 → 42 not taken.
✗ Branch 41 → 43 not taken.
6 if (blend_fn == nullptr)
623 env->ThrowError("Blend: no valid internal function");
624
625
2/6
void OL_BlendImage::BlendImage<float>(ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 63 → 49 not taken.
✗ Branch 63 → 64 not taken.
void OL_BlendImage::BlendImage<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 58 → 44 taken 18 times.
✓ Branch 58 → 59 taken 6 times.
void OL_BlendImage::BlendImage<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 58 → 44 not taken.
✗ Branch 58 → 59 not taken.
24 for (int p = planeindex_from; p <= planeindex_to; p++) {
626 18 const int xws_p = base->xSubSamplingShifts[p];
627 18 const int yhs_p = base->ySubSamplingShifts[p];
628
2/6
void OL_BlendImage::BlendImage<float>(ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 49 → 50 not taken.
✗ Branch 49 → 52 not taken.
void OL_BlendImage::BlendImage<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 44 → 45 taken 6 times.
✓ Branch 44 → 47 taken 12 times.
void OL_BlendImage::BlendImage<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 44 → 45 not taken.
✗ Branch 44 → 47 not taken.
18 const int plane_w = (xws_p > 0) ? ((base->xAccum() & ((1 << xws_p) - 1)) + w + (1 << xws_p) - 1) >> xws_p : w;
629
2/6
void OL_BlendImage::BlendImage<float>(ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 53 → 54 not taken.
✗ Branch 53 → 56 not taken.
void OL_BlendImage::BlendImage<unsigned char>(ImageOverlayInternal*, ImageOverlayInternal*):
✓ Branch 48 → 49 taken 4 times.
✓ Branch 48 → 51 taken 14 times.
void OL_BlendImage::BlendImage<unsigned short>(ImageOverlayInternal*, ImageOverlayInternal*):
✗ Branch 48 → 49 not taken.
✗ Branch 48 → 51 not taken.
18 const int plane_h = (yhs_p > 0) ? ((base->yAccum() & ((1 << yhs_p) - 1)) + h + (1 << yhs_p) - 1) >> yhs_p : h;
630 // no mask ptr
631 36 blend_fn(
632 18 base->GetPtrByIndex(p), overlay->GetPtrByIndex(p), nullptr,
633 base->GetPitchByIndex(p), overlay->GetPitchByIndex(p), 0,
634 plane_w, plane_h, opacity_f,
635 bits_per_pixel);
636 }
637
638 }
639 }
640