GCC Code Coverage Report


Directory: avs_core/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 18.8% 138 / 0 / 734
Functions: 15.0% 6 / 0 / 40
Branches: 13.2% 128 / 0 / 969

filters/focus.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 #include "focus.h"
36 #ifdef INTEL_INTRINSICS
37 #include "intel/focus_sse.h"
38 #include "intel/focus_avx2.h"
39 #endif
40 #include <cmath>
41 #include <vector>
42 #include <avs/alignment.h>
43 #include <avs/minmax.h>
44 #include "../core/internal.h"
45 #include <stdint.h>
46
47
48 /********************************************************************
49 ***** Declare index of new filters for Avisynth's filter engine *****
50 ********************************************************************/
51
52 extern const AVSFunction Focus_filters[] = {
53 { "Blur", BUILTIN_FUNC_PREFIX, "cf[]f[mmx]b", Create_Blur }, // amount [-1.0 - 1.5849625] -- log2(3)
54 { "Sharpen", BUILTIN_FUNC_PREFIX, "cf[]f[mmx]b", Create_Sharpen }, // amount [-1.5849625 - 1.0]
55 { "TemporalSoften", BUILTIN_FUNC_PREFIX, "ciii[scenechange]i[mode]i", TemporalSoften::Create }, // radius, luma_threshold, chroma_threshold
56 { "SpatialSoften", BUILTIN_FUNC_PREFIX, "ciii", SpatialSoften::Create }, // radius, luma_threshold, chroma_threshold
57 { 0 }
58 };
59
60
61
62
63
64 /****************************************
65 *** AdjustFocus helper classes ***
66 *** Originally by Ben R.G. ***
67 *** MMX code by Marc FD ***
68 *** Adaptation and bugfixes sh0dan ***
69 *** Code actually requires ISSE! ***
70 *** Not anymore - pure MMX IanB ***
71 *** Implement boundary proc. IanB ***
72 *** Impl. full 8bit MMX proc. IanB ***
73 ***************************************/
74
75 AdjustFocusV::AdjustFocusV(double _amount, PClip _child)
76 : GenericVideoFilter(_child), amountd(pow(2.0, _amount)) {
77 half_amount = int(32768 * amountd + 0.5);
78 }
79
80 template<typename pixel_t>
81 static void af_vertical_c(BYTE* line_buf8, BYTE* dstp8, const int height, const int pitch8, const int width, const int half_amount, int bits_per_pixel) {
82 typedef typename std::conditional < sizeof(pixel_t) == 1, int, int64_t>::type weight_t;
83 // kernel:[(1-1/2^_amount)/2, 1/2^_amount, (1-1/2^_amount)/2]
84 weight_t center_weight = half_amount*2; // *2: 16 bit scaled arithmetic, but the converted amount parameter scaled is only 15 bits
85 weight_t outer_weight = 32768-half_amount; // (1-1/2^_amount)/2 32768 = 0.5
86 int max_pixel_value = (1 << bits_per_pixel) - 1;
87
88 pixel_t * dstp = reinterpret_cast<pixel_t *>(dstp8);
89 pixel_t * line_buf = reinterpret_cast<pixel_t *>(line_buf8);
90 int pitch = pitch8 / sizeof(pixel_t);
91
92 for (int y = height-1; y>0; --y) {
93 for (int x = 0; x < width; ++x) {
94 pixel_t a;
95 // Note: ScaledPixelClip is overloaded. With int64_t parameter and uint16_t result works for 16 bit
96 if constexpr(sizeof(pixel_t) == 1)
97 a = ScaledPixelClip((weight_t)(dstp[x] * center_weight + (line_buf[x] + dstp[x+pitch]) * outer_weight));
98 else
99 a = (pixel_t)ScaledPixelClipEx((weight_t)(dstp[x] * center_weight + (line_buf[x] + dstp[x+pitch]) * outer_weight), max_pixel_value);
100 line_buf[x] = dstp[x];
101 dstp[x] = a;
102 }
103 dstp += pitch;
104 }
105 for (int x = 0; x < width; ++x) { // Last row - map centre as lower
106 if constexpr(sizeof(pixel_t) == 1)
107 dstp[x] = ScaledPixelClip((weight_t)(dstp[x] * center_weight + (line_buf[x] + dstp[x]) * outer_weight));
108 else
109 dstp[x] = (pixel_t)ScaledPixelClipEx((weight_t)(dstp[x] * center_weight + (line_buf[x] + dstp[x]) * outer_weight), max_pixel_value);
110 }
111 }
112
113 static void af_vertical_c_float(BYTE* line_buf8, BYTE* dstp8, const int height, const int pitch8, const int width, const float amount) {
114 float *dstp = reinterpret_cast<float *>(dstp8);
115 float *line_buf = reinterpret_cast<float *>(line_buf8);
116 int pitch = pitch8 / sizeof(float);
117
118 const float center_weight = amount;
119 const float outer_weight = (1.0f - amount) / 2.0f;
120
121 for (int y = height-1; y>0; --y) {
122 for (int x = 0; x < width; ++x) {
123 float a = dstp[x] * center_weight + (line_buf[x] + dstp[x+pitch]) * outer_weight;
124 line_buf[x] = dstp[x];
125 dstp[x] = a;
126 }
127 dstp += pitch;
128 }
129 for (int x = 0; x < width; ++x) { // Last row - map centre as lower
130 dstp[x] = dstp[x] * center_weight + (line_buf[x] + dstp[x]) * outer_weight;
131 }
132 }
133
134 template<typename pixel_t>
135 static void af_vertical_process(BYTE* line_buf, BYTE* dstp, size_t height, size_t pitch, size_t row_size, int half_amount, int bits_per_pixel, IScriptEnvironment* env) {
136 size_t width = row_size / sizeof(pixel_t);
137 // only for 8/16 bit, float separated
138 #ifdef INTEL_INTRINSICS
139 if (sizeof(pixel_t) == 1 && (env->GetCPUFlags() & CPUF_AVX2) && width >= 32) {
140 //pitch of aligned frames is always >= 32 so we'll just process some garbage if width is not mod32
141 af_vertical_avx2(line_buf, dstp, (int)height, (int)pitch, (int)width, half_amount);
142 }
143 else
144 if (sizeof(pixel_t) == 1 && (env->GetCPUFlags() & CPUF_SSE2) && width >= 16) {
145 //pitch of aligned frames is always >= 16 so we'll just process some garbage if width is not mod16
146 af_vertical_sse2(line_buf, dstp, (int)height, (int)pitch, (int)width, half_amount);
147 }
148 else if (sizeof(pixel_t) == 2 && (env->GetCPUFlags() & CPUF_AVX2) && row_size >= 32) {
149 af_vertical_uint16_t_avx2(line_buf, dstp, (int)height, (int)pitch, (int)row_size, half_amount);
150 }
151 else if (sizeof(pixel_t) == 2 && (env->GetCPUFlags() & CPUF_SSE4_1) && row_size >= 16) {
152 af_vertical_uint16_t_sse41(line_buf, dstp, (int)height, (int)pitch, (int)row_size, half_amount);
153 }
154 else if (sizeof(pixel_t) == 2 && (env->GetCPUFlags() & CPUF_SSE2) && row_size >= 16) {
155 af_vertical_uint16_t_sse2(line_buf, dstp, (int)height, (int)pitch, (int)row_size, half_amount);
156 }
157 else
158 #ifdef X86_32
159 if (sizeof(pixel_t) == 1 && (env->GetCPUFlags() & CPUF_MMX) && width >= 8)
160 {
161 size_t mod8_width = width / 8 * 8;
162 af_vertical_mmx(line_buf, dstp, height, pitch, mod8_width, half_amount);
163 if (mod8_width != width) {
164 //yes, this is bad for caching. MMX shouldn't be used these days anyway
165 af_vertical_c<uint8_t>(line_buf, dstp + mod8_width, height, pitch, width - mod8_width, half_amount, bits_per_pixel);
166 }
167 } else
168 #endif
169 #endif
170 {
171 af_vertical_c<pixel_t>(line_buf, dstp, (int)height, (int)pitch, (int)width, half_amount, bits_per_pixel);
172 }
173 }
174
175 static void af_vertical_process_float(BYTE* line_buf, BYTE* dstp, size_t height, size_t pitch, size_t row_size, double amountd, IScriptEnvironment* env) {
176 size_t width = row_size / sizeof(float);
177 #ifdef INTEL_INTRINSICS
178 if ((env->GetCPUFlags() & CPUF_SSE2) && width >= 16) {
179 //pitch of aligned frames is always >= 16 so we'll just process some garbage if width is not mod16
180 af_vertical_sse2_float(line_buf, dstp, (int)height, (int)pitch, (int)row_size, (float)amountd);
181 }
182 else
183 #endif
184 {
185 af_vertical_c_float(line_buf, dstp, (int)height, (int)pitch, (int)width, (float)amountd);
186 }
187 }
188
189 // --------------------------------
190 // Vertical Blur/Sharpen
191 // --------------------------------
192
193 PVideoFrame __stdcall AdjustFocusV::GetFrame(int n, IScriptEnvironment* env)
194 {
195 PVideoFrame src = child->GetFrame(n, env);
196
197 env->MakeWritable(&src);
198
199 BYTE* line_buf = reinterpret_cast<BYTE*>(env->Allocate(AlignNumber(src->GetRowSize(), FRAME_ALIGN), FRAME_ALIGN, AVS_POOLED_ALLOC));
200 if (!line_buf) {
201 env->ThrowError("AdjustFocusV: Could not reserve memory.");
202 }
203
204 int pixelsize = vi.ComponentSize();
205 int bits_per_pixel = vi.BitsPerComponent();
206
207 if (vi.IsPlanar()) {
208 const int planesYUV[4] = { PLANAR_Y, PLANAR_U, PLANAR_V, PLANAR_A};
209 const int planesRGB[4] = { PLANAR_G, PLANAR_B, PLANAR_R, PLANAR_A};
210 const int *planes = vi.IsYUV() || vi.IsYUVA() ? planesYUV : planesRGB;
211
212 for (int cplane = 0; cplane < 3; cplane++) {
213 int plane = planes[cplane];
214 BYTE* dstp = src->GetWritePtr(plane);
215 int pitch = src->GetPitch(plane);
216 int row_size = src->GetRowSize(plane);
217 int height = src->GetHeight(plane);
218 memcpy(line_buf, dstp, row_size); // First row - map centre as upper
219
220 switch (pixelsize) {
221 case 1: af_vertical_process<uint8_t>(line_buf, dstp, height, pitch, row_size, half_amount, bits_per_pixel, env); break;
222 case 2: af_vertical_process<uint16_t>(line_buf, dstp, height, pitch, row_size, half_amount, bits_per_pixel, env); break;
223 default: // 4: float
224 af_vertical_process_float(line_buf, dstp, height, pitch, row_size, amountd, env); break;
225 }
226 }
227 }
228 else {
229 BYTE* dstp = src->GetWritePtr();
230 int pitch = src->GetPitch();
231 int row_size = vi.RowSize();
232 int height = vi.height;
233 memcpy(line_buf, dstp, row_size); // First row - map centre as upper
234 if (pixelsize == 1)
235 af_vertical_process<uint8_t>(line_buf, dstp, height, pitch, row_size, half_amount, bits_per_pixel, env);
236 else
237 af_vertical_process<uint16_t>(line_buf, dstp, height, pitch, row_size, half_amount, bits_per_pixel, env);
238 }
239
240 env->Free(line_buf);
241 return src;
242 }
243
244
245 AdjustFocusH::AdjustFocusH(double _amount, PClip _child)
246 : GenericVideoFilter(_child), amountd(pow(2.0, _amount)) {
247 half_amount = int(32768 * amountd + 0.5);
248 }
249
250
251 // --------------------------------------
252 // Blur/Sharpen Horizontal RGB32 C++ Code
253 // --------------------------------------
254
255 template<typename pixel_t, typename weight_t>
256 static AVS_FORCEINLINE void af_horizontal_rgb32_process_line_c(pixel_t b_left, pixel_t g_left, pixel_t r_left, pixel_t a_left, pixel_t *dstp, size_t width, weight_t center_weight, weight_t outer_weight) {
257 size_t x;
258 for (x = 0; x < width-1; ++x)
259 {
260 pixel_t b = ScaledPixelClip((weight_t)(dstp[x*4+0] * center_weight + (b_left + dstp[x*4+4]) * outer_weight));
261 b_left = dstp[x*4+0];
262 dstp[x*4+0] = b;
263 pixel_t g = ScaledPixelClip((weight_t)(dstp[x*4+1] * center_weight + (g_left + dstp[x*4+5]) * outer_weight));
264 g_left = dstp[x*4+1];
265 dstp[x*4+1] = g;
266 pixel_t r = ScaledPixelClip((weight_t)(dstp[x*4+2] * center_weight + (r_left + dstp[x*4+6]) * outer_weight));
267 r_left = dstp[x*4+2];
268 dstp[x*4+2] = r;
269 pixel_t a = ScaledPixelClip((weight_t)(dstp[x*4+3] * center_weight + (a_left + dstp[x*4+7]) * outer_weight));
270 a_left = dstp[x*4+3];
271 dstp[x*4+3] = a;
272 }
273 dstp[x*4+0] = ScaledPixelClip((weight_t)(dstp[x*4+0] * center_weight + (b_left + dstp[x*4+0]) * outer_weight));
274 dstp[x*4+1] = ScaledPixelClip((weight_t)(dstp[x*4+1] * center_weight + (g_left + dstp[x*4+1]) * outer_weight));
275 dstp[x*4+2] = ScaledPixelClip((weight_t)(dstp[x*4+2] * center_weight + (r_left + dstp[x*4+2]) * outer_weight));
276 dstp[x*4+3] = ScaledPixelClip((weight_t)(dstp[x*4+3] * center_weight + (a_left + dstp[x*4+3]) * outer_weight));
277 }
278
279 template<typename pixel_t>
280 static void af_horizontal_rgb32_64_c(BYTE* dstp8, size_t height, size_t pitch8, size_t width, int half_amount) {
281 typedef typename std::conditional < sizeof(pixel_t) == 1, int, int64_t>::type weight_t;
282 // kernel:[(1-1/2^_amount)/2, 1/2^_amount, (1-1/2^_amount)/2]
283 weight_t center_weight = half_amount*2; // *2: 16 bit scaled arithmetic, but the converted amount parameter scaled is only 15 bits
284 weight_t outer_weight = 32768-half_amount; // (1-1/2^_amount)/2 32768 = 0.5
285
286 pixel_t* dstp = reinterpret_cast<pixel_t *>(dstp8);
287 size_t pitch = pitch8 / sizeof(pixel_t);
288
289 for (size_t y = height; y>0; --y)
290 {
291 pixel_t b_left = dstp[0];
292 pixel_t g_left = dstp[1];
293 pixel_t r_left = dstp[2];
294 pixel_t a_left = dstp[3];
295 af_horizontal_rgb32_process_line_c<pixel_t, weight_t>(b_left, g_left, r_left, a_left, dstp, width, center_weight, outer_weight);
296 dstp += pitch;
297 }
298
299 }
300
301
302 // -------------------------------------
303 // Blur/Sharpen Horizontal YUY2 C++ Code
304 // -------------------------------------
305
306 static void af_horizontal_yuy2_c(BYTE* p, int height, int pitch, int width, int amount) {
307 const int center_weight = amount*2;
308 const int outer_weight = 32768-amount;
309 for (int y0 = height; y0>0; --y0)
310 {
311 BYTE yy = p[0];
312 BYTE uv = p[1];
313 BYTE vu = p[3];
314 int x;
315 for (x = 0; x < width-2; ++x)
316 {
317 BYTE y = ScaledPixelClip(p[x*2+0] * center_weight + (yy + p[x*2+2]) * outer_weight);
318 yy = p[x*2+0];
319 p[x*2+0] = y;
320 BYTE w = ScaledPixelClip(p[x*2+1] * center_weight + (uv + p[x*2+5]) * outer_weight);
321 uv = vu;
322 vu = p[x*2+1];
323 p[x*2+1] = w;
324 }
325 BYTE y = ScaledPixelClip(p[x*2+0] * center_weight + (yy + p[x*2+2]) * outer_weight);
326 yy = p[x*2+0];
327 p[x*2+0] = y;
328 p[x*2+1] = ScaledPixelClip(p[x*2+1] * center_weight + (uv + p[x*2+1]) * outer_weight);
329 p[x*2+2] = ScaledPixelClip(p[x*2+2] * center_weight + (yy + p[x*2+2]) * outer_weight);
330 p[x*2+3] = ScaledPixelClip(p[x*2+3] * center_weight + (vu + p[x*2+3]) * outer_weight);
331
332 p += pitch;
333 }
334 }
335
336
337 // --------------------------------------
338 // Blur/Sharpen Horizontal RGB24 C++ Code
339 // --------------------------------------
340
341 template<typename pixel_t>
342 static void af_horizontal_rgb24_48_c(BYTE* dstp8, int height, int pitch8, int width, int half_amount) {
343 typedef typename std::conditional < sizeof(pixel_t) == 1, int, int64_t>::type weight_t;
344 // kernel:[(1-1/2^_amount)/2, 1/2^_amount, (1-1/2^_amount)/2]
345 weight_t center_weight = half_amount*2; // *2: 16 bit scaled arithmetic, but the converted amount parameter scaled is only 15 bits
346 weight_t outer_weight = 32768-half_amount; // (1-1/2^_amount)/2 32768 = 0.5
347
348 pixel_t *dstp = reinterpret_cast<pixel_t *>(dstp8);
349 int pitch = pitch8 / sizeof(pixel_t);
350 for (int y = height; y>0; --y)
351 {
352 pixel_t bb = dstp[0];
353 pixel_t gg = dstp[1];
354 pixel_t rr = dstp[2];
355 int x;
356 for (x = 0; x < width-1; ++x)
357 {
358 // ScaledPixelClip has 2 overloads: BYTE/uint16_t (int/int64 i)
359 pixel_t b = ScaledPixelClip((weight_t)(dstp[x*3+0] * center_weight + (bb + dstp[x*3+3]) * outer_weight));
360 bb = dstp[x*3+0]; dstp[x*3+0] = b;
361 pixel_t g = ScaledPixelClip((weight_t)(dstp[x*3+1] * center_weight + (gg + dstp[x*3+4]) * outer_weight));
362 gg = dstp[x*3+1]; dstp[x*3+1] = g;
363 pixel_t r = ScaledPixelClip((weight_t)(dstp[x*3+2] * center_weight + (rr + dstp[x*3+5]) * outer_weight));
364 rr = dstp[x*3+2]; dstp[x*3+2] = r;
365 }
366 dstp[x*3+0] = ScaledPixelClip((weight_t)(dstp[x*3+0] * center_weight + (bb + dstp[x*3+0]) * outer_weight));
367 dstp[x*3+1] = ScaledPixelClip((weight_t)(dstp[x*3+1] * center_weight + (gg + dstp[x*3+1]) * outer_weight));
368 dstp[x*3+2] = ScaledPixelClip((weight_t)(dstp[x*3+2] * center_weight + (rr + dstp[x*3+2]) * outer_weight));
369 dstp += pitch;
370 }
371 }
372
373 // -------------------------------------
374 // Blur/Sharpen Horizontal YV12 C++ Code
375 // -------------------------------------
376
377 // for linker reasons these forceinlined functions appear in C, intel sse2 and avx2 source as well
378 template<typename pixel_t>
379 static AVS_FORCEINLINE void af_horizontal_planar_process_line_c(pixel_t left, BYTE *dstp8, size_t row_size, int center_weight, int outer_weight) {
380 size_t x;
381 pixel_t* dstp = reinterpret_cast<pixel_t *>(dstp8);
382 typedef typename std::conditional < sizeof(pixel_t) == 1, int, int64_t>::type weight_t; // for calling the right ScaledPixelClip()
383 size_t width = row_size / sizeof(pixel_t);
384 for (x = 0; x < width-1; ++x) {
385 pixel_t temp = ScaledPixelClip((weight_t)(dstp[x] * (weight_t)center_weight + (left + dstp[x+1]) * (weight_t)outer_weight));
386 left = dstp[x];
387 dstp[x] = temp;
388 }
389 // ScaledPixelClip has 2 overloads: BYTE/uint16_t (int/int64 i)
390 dstp[x] = ScaledPixelClip((weight_t)(dstp[x] * (weight_t)center_weight + (left + dstp[x]) * (weight_t)outer_weight));
391 }
392
393 static AVS_FORCEINLINE void af_horizontal_planar_process_line_uint16_c(uint16_t left, BYTE *dstp8, size_t row_size, int center_weight, int outer_weight, int bits_per_pixel) {
394 size_t x;
395 typedef uint16_t pixel_t;
396 pixel_t* dstp = reinterpret_cast<pixel_t *>(dstp8);
397 const int max_pixel_value = (1 << bits_per_pixel) - 1; // clamping on 10-12-14-16 bitdepth
398 typedef std::conditional < sizeof(pixel_t) == 1, int, int64_t>::type weight_t; // for calling the right ScaledPixelClip()
399 size_t width = row_size / sizeof(pixel_t);
400 for (x = 0; x < width-1; ++x) {
401 pixel_t temp = (pixel_t)ScaledPixelClipEx((weight_t)(dstp[x] * (weight_t)center_weight + (left + dstp[x+1]) * (weight_t)outer_weight), max_pixel_value);
402 left = dstp[x];
403 dstp[x] = temp;
404 }
405 // ScaledPixelClip has 2 overloads: BYTE/uint16_t (int/int64 i)
406 dstp[x] = ScaledPixelClipEx((weight_t)(dstp[x] * (weight_t)center_weight + (left + dstp[x]) * (weight_t)outer_weight), max_pixel_value);
407 }
408
409 static AVS_FORCEINLINE void af_horizontal_planar_process_line_float_c(float left, float* dstp, size_t row_size, float center_weight, float outer_weight) {
410 size_t x;
411 size_t width = row_size / sizeof(float);
412 for (x = 0; x < width - 1; ++x) {
413 float temp = dstp[x] * center_weight + (left + dstp[x + 1]) * outer_weight;
414 left = dstp[x];
415 dstp[x] = temp;
416 }
417 dstp[x] = dstp[x] * center_weight + (left + dstp[x]) * outer_weight;
418 }
419
420 template<typename pixel_t>
421 static void af_horizontal_planar_c(BYTE* dstp8, size_t height, size_t pitch8, size_t row_size, size_t half_amount, int bits_per_pixel)
422 {
423 pixel_t* dstp = reinterpret_cast<pixel_t *>(dstp8);
424 size_t pitch = pitch8 / sizeof(pixel_t);
425 int center_weight = int(half_amount*2);
426 int outer_weight = int(32768-half_amount);
427 pixel_t left;
428 for (size_t y = height; y>0; --y) {
429 left = dstp[0];
430 if constexpr(sizeof(pixel_t) == 1)
431 af_horizontal_planar_process_line_c<pixel_t>(left, (BYTE *)dstp, row_size, center_weight, outer_weight);
432 else
433 af_horizontal_planar_process_line_uint16_c(left, (BYTE *)dstp, row_size, center_weight, outer_weight, bits_per_pixel);
434 dstp += pitch;
435 }
436 }
437
438 static void af_horizontal_planar_float_c(BYTE* dstp8, size_t height, size_t pitch8, size_t row_size, float amount)
439 {
440 float* dstp = reinterpret_cast<float *>(dstp8);
441 size_t pitch = pitch8 / sizeof(float);
442 float center_weight = amount;
443 float outer_weight = (1.0f - amount) / 2.0f;
444 float left;
445 for (size_t y = height; y>0; --y) {
446 left = dstp[0];
447 af_horizontal_planar_process_line_float_c(left, dstp, row_size, center_weight, outer_weight);
448 dstp += pitch;
449 }
450 }
451
452 static void copy_frame(const PVideoFrame &src, PVideoFrame &dst, IScriptEnvironment *env, const int *planes, int plane_count) {
453 for (int p = 0; p < plane_count; p++) {
454 int plane = planes[p];
455 env->BitBlt(dst->GetWritePtr(plane), dst->GetPitch(plane), src->GetReadPtr(plane),
456 src->GetPitch(plane), src->GetRowSize(plane), src->GetHeight(plane));
457 }
458 }
459
460 // ----------------------------------
461 // Blur/Sharpen Horizontal GetFrame()
462 // ----------------------------------
463
464 PVideoFrame __stdcall AdjustFocusH::GetFrame(int n, IScriptEnvironment* env)
465 {
466 PVideoFrame src = child->GetFrame(n, env);
467 PVideoFrame dst = env->NewVideoFrameP(vi, &src);
468
469 const int planesYUV[4] = { PLANAR_Y, PLANAR_U, PLANAR_V, PLANAR_A};
470 const int planesRGB[4] = { PLANAR_G, PLANAR_B, PLANAR_R, PLANAR_A};
471 const int *planes = vi.IsYUV() || vi.IsYUVA() ? planesYUV : planesRGB;
472
473 int pixelsize = vi.ComponentSize();
474
475 if (vi.IsPlanar()) {
476 copy_frame(src, dst, env, planes, vi.NumComponents() ); //planar processing is always in-place
477 int bits_per_pixel = vi.BitsPerComponent();
478 for(int cplane=0;cplane<3;cplane++) {
479 int plane = planes[cplane];
480 int row_size = dst->GetRowSize(plane);
481 BYTE* q = dst->GetWritePtr(plane);
482 int pitch = dst->GetPitch(plane);
483 int height = dst->GetHeight(plane);
484 #ifdef INTEL_INTRINSICS
485 if (pixelsize == 1 && (env->GetCPUFlags() & CPUF_AVX2) && row_size > 32) {
486 af_horizontal_planar_avx2(q, height, pitch, row_size, half_amount);
487 }
488 else
489 if (pixelsize==1 && (env->GetCPUFlags() & CPUF_SSE2) && row_size > 16) {
490 af_horizontal_planar_sse2(q, height, pitch, row_size, half_amount);
491 } else
492 #ifdef X86_32
493 if (pixelsize == 1 && (env->GetCPUFlags() & CPUF_MMX) && row_size > 8) {
494 af_horizontal_planar_mmx(q,height,pitch,row_size,half_amount);
495 } else
496 #endif
497 if (pixelsize == 2 && (env->GetCPUFlags() & CPUF_AVX2) && row_size > 32) {
498 af_horizontal_planar_uint16_t_avx2(q, height, pitch, row_size, half_amount, bits_per_pixel);
499 }
500 else if (pixelsize == 2 && (env->GetCPUFlags() & CPUF_SSE4_1) && row_size > 16) {
501 af_horizontal_planar_uint16_t_sse41(q, height, pitch, row_size, half_amount, bits_per_pixel);
502 }
503 else if (pixelsize == 2 && (env->GetCPUFlags() & CPUF_SSE2) && row_size > 16) {
504 af_horizontal_planar_uint16_t_sse2(q, height, pitch, row_size, half_amount, bits_per_pixel);
505 }
506 else if (pixelsize == 4 && (env->GetCPUFlags() & CPUF_SSE2) && row_size > 16) {
507 af_horizontal_planar_float_sse2(q, height, pitch, row_size, (float)amountd);
508 }
509 else
510 #endif
511 {
512 switch (pixelsize) {
513 case 1: af_horizontal_planar_c<uint8_t>(q, height, pitch, row_size, half_amount, bits_per_pixel); break;
514 case 2: af_horizontal_planar_c<uint16_t>(q, height, pitch, row_size, half_amount, bits_per_pixel); break;
515 default: // 4: float
516 af_horizontal_planar_float_c(q, height, pitch, row_size, (float)amountd); break;
517 }
518 }
519 }
520 } else {
521 if (vi.IsYUY2()) {
522 BYTE* q = dst->GetWritePtr();
523 const int pitch = dst->GetPitch();
524 #ifdef INTEL_INTRINSICS
525 if ((env->GetCPUFlags() & CPUF_SSE2) && vi.width>8) {
526 af_horizontal_yuy2_sse2(dst->GetWritePtr(), src->GetReadPtr(), dst->GetPitch(), src->GetPitch(), vi.height, vi.width, half_amount);
527 } else
528 #ifdef X86_32
529 if ((env->GetCPUFlags() & CPUF_MMX) && vi.width>8) {
530 af_horizontal_yuy2_mmx(dst->GetWritePtr(), src->GetReadPtr(), dst->GetPitch(), src->GetPitch(), vi.height, vi.width, half_amount);
531 } else
532 #endif
533 #endif
534 {
535 copy_frame(src, dst, env, planesYUV, 1); //in-place
536 af_horizontal_yuy2_c(q,vi.height,pitch,vi.width,half_amount);
537 }
538 }
539 else if (vi.IsRGB32() || vi.IsRGB64()) {
540 #ifdef INTEL_INTRINSICS
541 if ((pixelsize==1) && (env->GetCPUFlags() & CPUF_SSE2) && vi.width>4) {
542 //this one is NOT in-place
543 af_horizontal_rgb32_sse2(dst->GetWritePtr(), src->GetReadPtr(), dst->GetPitch(), src->GetPitch(), vi.height, vi.width, half_amount);
544 }
545 else if ((pixelsize == 2) && (env->GetCPUFlags() & CPUF_SSE4_1) && vi.width > 2) {
546 //this one is NOT in-place
547 af_horizontal_rgb64_sse41(dst->GetWritePtr(), src->GetReadPtr(), dst->GetPitch(), src->GetPitch(), vi.height, vi.width, half_amount); // really width
548 }
549 else if ((pixelsize == 2) && (env->GetCPUFlags() & CPUF_SSE2) && vi.width > 2) {
550 //this one is NOT in-place
551 af_horizontal_rgb64_sse2(dst->GetWritePtr(), src->GetReadPtr(), dst->GetPitch(), src->GetPitch(), vi.height, vi.width, half_amount); // really width
552 }
553 else
554 #ifdef X86_32
555 if ((pixelsize==1) && (env->GetCPUFlags() & CPUF_MMX) && vi.width > 2)
556 { //so as this one
557 af_horizontal_rgb32_mmx(dst->GetWritePtr(), src->GetReadPtr(), dst->GetPitch(), src->GetPitch(), vi.height, vi.width, half_amount);
558 } else
559 #endif
560 #endif
561 {
562 copy_frame(src, dst, env, planesYUV, 1);
563 if(pixelsize==1)
564 af_horizontal_rgb32_64_c<uint8_t>(dst->GetWritePtr(), vi.height, dst->GetPitch(), vi.width, half_amount);
565 else
566 af_horizontal_rgb32_64_c<uint16_t>(dst->GetWritePtr(), vi.height, dst->GetPitch(), vi.width, half_amount);
567 }
568 } else if (vi.IsRGB24() || vi.IsRGB48()) {
569 copy_frame(src, dst, env, planesYUV, 1);
570 if(pixelsize==1)
571 af_horizontal_rgb24_48_c<uint8_t>(dst->GetWritePtr(), vi.height, dst->GetPitch(), vi.width, half_amount);
572 else
573 af_horizontal_rgb24_48_c<uint16_t>(dst->GetWritePtr(), vi.height, dst->GetPitch(), vi.width, half_amount);
574 }
575 }
576
577 return dst;
578 }
579
580
581 /************************************************
582 ******* Sharpen/Blur Factory Methods *******
583 ***********************************************/
584
585 AVSValue __cdecl Create_Sharpen(AVSValue args, void*, IScriptEnvironment* env)
586 {
587 const double amountH = args[1].AsFloat(), amountV = args[2].AsDblDef(amountH);
588
589 if (amountH < -1.5849625 || amountH > 1.0 || amountV < -1.5849625 || amountV > 1.0) // log2(3)
590 env->ThrowError("Sharpen: arguments must be in the range -1.58 to 1.0");
591
592 if (fabs(amountH) < 0.00002201361136) { // log2(1+1/65536)
593 if (fabs(amountV) < 0.00002201361136) {
594 return args[0].AsClip();
595 }
596 else {
597 return new AdjustFocusV(amountV, args[0].AsClip());
598 }
599 }
600 else {
601 if (fabs(amountV) < 0.00002201361136) {
602 return new AdjustFocusH(amountH, args[0].AsClip());
603 }
604 else {
605 return new AdjustFocusH(amountH, new AdjustFocusV(amountV, args[0].AsClip()));
606 }
607 }
608 }
609
610 AVSValue __cdecl Create_Blur(AVSValue args, void*, IScriptEnvironment* env)
611 {
612 const double amountH = args[1].AsFloat(), amountV = args[2].AsDblDef(amountH);
613
614 if (amountH < -1.0 || amountH > 1.5849625 || amountV < -1.0 || amountV > 1.5849625) // log2(3)
615 env->ThrowError("Blur: arguments must be in the range -1.0 to 1.58");
616
617 if (fabs(amountH) < 0.00002201361136) { // log2(1+1/65536)
618 if (fabs(amountV) < 0.00002201361136) {
619 return args[0].AsClip();
620 }
621 else {
622 return new AdjustFocusV(-amountV, args[0].AsClip());
623 }
624 }
625 else {
626 if (fabs(amountV) < 0.00002201361136) {
627 return new AdjustFocusH(-amountH, args[0].AsClip());
628 }
629 else {
630 return new AdjustFocusH(-amountH, new AdjustFocusV(-amountV, args[0].AsClip()));
631 }
632 }
633 }
634
635
636
637
638 /***************************
639 **** TemporalSoften *****
640 **************************/
641
642 4 TemporalSoften::TemporalSoften( PClip _child, unsigned radius, unsigned luma_thresh,
643 4 unsigned chroma_thresh, int _scenechange, IScriptEnvironment* env )
644 : GenericVideoFilter(_child),
645 4 scenechange(_scenechange),
646 8 luma_threshold(min(luma_thresh, 255u)),
647 4 chroma_threshold(min(chroma_thresh, 255u)),
648
2/4
✓ Branch 2 → 3 taken 4 times.
✗ Branch 2 → 77 not taken.
✓ Branch 3 → 4 taken 4 times.
✗ Branch 3 → 75 not taken.
8 kernel(2 * min(radius, (unsigned int)MAX_RADIUS) + 1)
649 {
650
651
1/2
✓ Branch 9 → 10 taken 4 times.
✗ Branch 9 → 78 not taken.
4 child->SetCacheHints(CACHE_WINDOW,kernel);
652
653
5/10
✓ Branch 10 → 11 taken 4 times.
✗ Branch 10 → 78 not taken.
✓ Branch 11 → 12 taken 4 times.
✗ Branch 11 → 14 not taken.
✓ Branch 12 → 13 taken 4 times.
✗ Branch 12 → 78 not taken.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 4 times.
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 18 taken 4 times.
4 if (vi.IsRGB24() || vi.IsRGB48()) {
654 env->ThrowError("TemporalSoften: RGB24/48 Not supported, use ConvertToRGB32/48().");
655 }
656
657
5/12
✓ Branch 18 → 19 taken 4 times.
✗ Branch 18 → 78 not taken.
✓ Branch 19 → 20 taken 4 times.
✗ Branch 19 → 22 not taken.
✓ Branch 20 → 21 taken 4 times.
✗ Branch 20 → 78 not taken.
✗ Branch 21 → 22 not taken.
✓ Branch 21 → 24 taken 4 times.
✗ Branch 22 → 23 not taken.
✗ Branch 22 → 24 not taken.
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 27 taken 4 times.
4 if ((vi.IsRGB32() || vi.IsRGB64()) && (vi.width&1)) {
658 env->ThrowError("TemporalSoften: RGB32/64 source must be multiple of 2 in width.");
659 }
660
661
3/8
✓ Branch 27 → 28 taken 4 times.
✗ Branch 27 → 78 not taken.
✗ Branch 28 → 29 not taken.
✓ Branch 28 → 31 taken 4 times.
✗ Branch 29 → 30 not taken.
✗ Branch 29 → 31 not taken.
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 34 taken 4 times.
4 if ((vi.IsYUY2()) && (vi.width&3)) {
662 env->ThrowError("TemporalSoften: YUY2 source must be multiple of 4 in width.");
663 }
664
665
1/2
✗ Branch 34 → 35 not taken.
✓ Branch 34 → 36 taken 4 times.
4 if (scenechange >= 255) {
666 scenechange = 0;
667 }
668
669
2/12
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 42 taken 4 times.
✗ Branch 37 → 38 not taken.
✗ Branch 37 → 78 not taken.
✗ Branch 38 → 39 not taken.
✗ Branch 38 → 41 not taken.
✗ Branch 39 → 40 not taken.
✗ Branch 39 → 78 not taken.
✗ Branch 40 → 41 not taken.
✗ Branch 40 → 42 not taken.
✗ Branch 43 → 44 not taken.
✓ Branch 43 → 45 taken 4 times.
4 if (scenechange>0 && (vi.IsRGB32() || vi.IsRGB64())) {
670 env->ThrowError("TemporalSoften: Scenechange not available on RGB32/64");
671 }
672
673
1/2
✓ Branch 45 → 46 taken 4 times.
✗ Branch 45 → 78 not taken.
4 pixelsize = vi.ComponentSize();
674
1/2
✓ Branch 46 → 47 taken 4 times.
✗ Branch 46 → 78 not taken.
4 bits_per_pixel = vi.BitsPerComponent();
675
676 // original scenechange parameter always 0-255
677 int factor;
678
2/4
✓ Branch 47 → 48 taken 4 times.
✗ Branch 47 → 78 not taken.
✓ Branch 48 → 49 taken 4 times.
✗ Branch 48 → 50 not taken.
4 if (vi.IsPlanar()) // Y/YUV, no Planar RGB here
679 4 factor = 1; // bitdepth independent. sad normalizes
680 else
681 factor = vi.BytesFromPixels(1) / pixelsize; // /pixelsize: correction for packed 16 bit rgb
682 4 scenechange *= ((vi.width/32)*32)*vi.height*factor; // why /*32?
683
684
685 4 int c = 0;
686
5/14
✓ Branch 52 → 53 taken 4 times.
✗ Branch 52 → 78 not taken.
✓ Branch 53 → 54 taken 4 times.
✗ Branch 53 → 59 not taken.
✓ Branch 54 → 55 taken 4 times.
✗ Branch 54 → 78 not taken.
✗ Branch 55 → 56 not taken.
✓ Branch 55 → 58 taken 4 times.
✗ Branch 56 → 57 not taken.
✗ Branch 56 → 78 not taken.
✗ Branch 57 → 58 not taken.
✗ Branch 57 → 59 not taken.
✓ Branch 60 → 61 taken 4 times.
✗ Branch 60 → 65 not taken.
4 if (vi.IsPlanar() && (vi.IsYUV() || vi.IsYUVA())) {
687
1/2
✓ Branch 61 → 62 taken 4 times.
✗ Branch 61 → 63 not taken.
4 if (luma_thresh>0) {
688 4 planes[c].planeId = PLANAR_Y;
689 4 planes[c++].threshold = luma_thresh;
690 }
691
2/2
✓ Branch 63 → 64 taken 3 times.
✓ Branch 63 → 74 taken 1 time.
4 if (chroma_thresh>0) {
692 3 planes[c].planeId = PLANAR_V;
693 3 planes[c++].threshold =chroma_thresh;
694 3 planes[c].planeId = PLANAR_U;
695 3 planes[c++].threshold = chroma_thresh;
696 }
697 } else if (vi.IsYUY2()) {
698 planes[c].planeId=0;
699 planes[c++].threshold=luma_thresh|(chroma_thresh<<8);
700 } else if (vi.IsRGB()) { // For RGB We use Luma.
701 if (vi.IsPlanar()) {
702 planes[c].planeId = PLANAR_G;
703 planes[c++].threshold = luma_thresh;
704 planes[c].planeId = PLANAR_B;
705 planes[c++].threshold = luma_thresh;
706 planes[c].planeId = PLANAR_R;
707 planes[c++].threshold = luma_thresh;
708 }
709 else { // packed RGB
710 planes[c].planeId = 0;
711 planes[c++].threshold = luma_thresh;
712 }
713 }
714 4 planes[c].planeId=0;
715 4 }
716
717 //offset is the initial value of x. Used when C routine processes only parts of frames after SSE/MMX paths do their job.
718 template<typename pixel_t, bool maxThreshold>
719 9 static void accumulate_line_c(BYTE* _c_plane, const BYTE** planeP, int planes, int offset, size_t rowsize, BYTE _threshold, int div, int bits_per_pixel) {
720 9 pixel_t *c_plane = reinterpret_cast<pixel_t *>(_c_plane);
721
722 typedef typename std::conditional < sizeof(pixel_t) <= 2, int, float>::type sum_t;
723 typedef typename std::conditional < sizeof(pixel_t) == 1, int, int64_t>::type bigsum_t;
724 typedef typename std::conditional < std::is_floating_point<pixel_t>::value, float, int>::type threshold_t;
725
726 9 size_t width = rowsize / sizeof(pixel_t);
727
728 9 threshold_t threshold = _threshold; // parameter 0..255
729 if (std::is_floating_point<pixel_t>::value)
730 9 threshold = (threshold_t)(threshold / 255.0f); // float
731 else if constexpr(sizeof(pixel_t) == 2) {
732 threshold = threshold * (uint16_t)(1 << (bits_per_pixel - 8)); // uint16_t, 10 bit: *4 16bit: *256
733 }
734
735
2/12
void accumulate_line_c<float, false>(unsigned char*, unsigned char const**, int, int, unsigned long, unsigned char, int, int):
✓ Branch 15 → 5 taken 144 times.
✓ Branch 15 → 16 taken 9 times.
void accumulate_line_c<float, true>(unsigned char*, unsigned char const**, int, int, unsigned long, unsigned char, int, int):
✗ Branch 12 → 5 not taken.
✗ Branch 12 → 13 not taken.
void accumulate_line_c<unsigned char, false>(unsigned char*, unsigned char const**, int, int, unsigned long, unsigned char, int, int):
✗ Branch 12 → 4 not taken.
✗ Branch 12 → 13 not taken.
void accumulate_line_c<unsigned char, true>(unsigned char*, unsigned char const**, int, int, unsigned long, unsigned char, int, int):
✗ Branch 10 → 4 not taken.
✗ Branch 10 → 11 not taken.
void accumulate_line_c<unsigned short, false>(unsigned char*, unsigned char const**, int, int, unsigned long, unsigned char, int, int):
✗ Branch 12 → 4 not taken.
✗ Branch 12 → 13 not taken.
void accumulate_line_c<unsigned short, true>(unsigned char*, unsigned char const**, int, int, unsigned long, unsigned char, int, int):
✗ Branch 10 → 4 not taken.
✗ Branch 10 → 11 not taken.
153 for (size_t x = offset; x < width; ++x) {
736 144 pixel_t current = c_plane[x];
737 144 sum_t sum = current;
738
739
2/12
void accumulate_line_c<float, false>(unsigned char*, unsigned char const**, int, int, unsigned long, unsigned char, int, int):
✓ Branch 12 → 6 taken 288 times.
✓ Branch 12 → 13 taken 144 times.
void accumulate_line_c<float, true>(unsigned char*, unsigned char const**, int, int, unsigned long, unsigned char, int, int):
✗ Branch 9 → 6 not taken.
✗ Branch 9 → 10 not taken.
void accumulate_line_c<unsigned char, false>(unsigned char*, unsigned char const**, int, int, unsigned long, unsigned char, int, int):
✗ Branch 10 → 5 not taken.
✗ Branch 10 → 11 not taken.
void accumulate_line_c<unsigned char, true>(unsigned char*, unsigned char const**, int, int, unsigned long, unsigned char, int, int):
✗ Branch 8 → 5 not taken.
✗ Branch 8 → 9 not taken.
void accumulate_line_c<unsigned short, false>(unsigned char*, unsigned char const**, int, int, unsigned long, unsigned char, int, int):
✗ Branch 10 → 5 not taken.
✗ Branch 10 → 11 not taken.
void accumulate_line_c<unsigned short, true>(unsigned char*, unsigned char const**, int, int, unsigned long, unsigned char, int, int):
✗ Branch 8 → 5 not taken.
✗ Branch 8 → 9 not taken.
432 for (int plane = planes - 1; plane >= 0; plane--) {
740 288 pixel_t p = reinterpret_cast<const pixel_t *>(planeP[plane])[x];
741 if (maxThreshold) {
742 sum += p; // simple frame average mode
743 }
744 else {
745 288 sum_t absdiff = std::abs(current - p);
746
747
2/6
void accumulate_line_c<float, false>(unsigned char*, unsigned char const**, int, int, unsigned long, unsigned char, int, int):
✓ Branch 8 → 9 taken 151 times.
✓ Branch 8 → 10 taken 137 times.
void accumulate_line_c<unsigned char, false>(unsigned char*, unsigned char const**, int, int, unsigned long, unsigned char, int, int):
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
void accumulate_line_c<unsigned short, false>(unsigned char*, unsigned char const**, int, int, unsigned long, unsigned char, int, int):
✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
288 if (absdiff <= threshold) {
748 151 sum += p;
749 }
750 else {
751 137 sum += current;
752 }
753 }
754 }
755 if (std::is_floating_point<pixel_t>::value)
756 144 c_plane[x] = (pixel_t)(sum / (planes + 1)); // float: simple average
757 else
758 c_plane[x] = (pixel_t)(((bigsum_t)sum * div + 16384) >> 15); // div = 32768/(planes+1) for integer arithmetic
759 }
760 9 }
761
762 static void accumulate_line_yuy2_c(BYTE* c_plane, const BYTE** planeP, int planes, size_t width, BYTE threshold_luma, BYTE threshold_chroma, int div) {
763 for (size_t x = 0; x < width; x+=2) {
764 BYTE current_y = c_plane[x];
765 BYTE current_c = c_plane[x+1];
766 size_t sum_y = current_y;
767 size_t sum_c = current_c;
768
769 for (int plane = planes - 1; plane >= 0; plane--) {
770 BYTE p_y = planeP[plane][x];
771 BYTE p_c = planeP[plane][x+1];
772 size_t absdiff_y = std::abs(current_y - p_y);
773 size_t absdiff_c = std::abs(current_c - p_c);
774
775 if (absdiff_y <= threshold_luma) {
776 sum_y += p_y;
777 } else {
778 sum_y += current_y;
779 }
780
781 if (absdiff_c <= threshold_chroma) {
782 sum_c += p_c;
783 } else {
784 sum_c += current_c;
785 }
786 }
787
788 c_plane[x] = (BYTE)((sum_y * div + 16384) >> 15);
789 c_plane[x+1] = (BYTE)((sum_c * div + 16384) >> 15);
790 }
791 }
792
793 static void accumulate_line_yuy2(BYTE* c_plane, const BYTE** planeP, int planes, size_t width, BYTE threshold_luma, BYTE threshold_chroma, int div, IScriptEnvironment* env)
794 {
795 #ifdef INTEL_INTRINSICS
796 if ((env->GetCPUFlags() & CPUF_SSE2) && width >= 16) {
797 accumulate_line_sse2<false>(c_plane, planeP, planes, width, threshold_luma | (threshold_chroma << 8), div);
798 } else
799 #ifdef X86_32
800 if ((env->GetCPUFlags() & CPUF_MMX) && width >= 8) {
801 accumulate_line_mmx(c_plane, planeP, planes, width, threshold_luma | (threshold_chroma << 8), div); //yuy2 is always at least mod8
802 } else
803 #endif
804 #endif
805 accumulate_line_yuy2_c(c_plane, planeP, planes, width, threshold_luma, threshold_chroma, div);
806 }
807
808 35 static void accumulate_line(BYTE* c_plane, const BYTE** planeP, int planes, size_t rowsize, BYTE threshold, int div, int pixelsize, int bits_per_pixel, IScriptEnvironment* env) {
809 // threshold == 255: simple average
810 35 bool maxThreshold = (threshold == 255);
811 #ifdef INTEL_INTRINSICS
812
6/8
✓ Branch 2 → 3 taken 15 times.
✓ Branch 2 → 7 taken 20 times.
✓ Branch 4 → 5 taken 15 times.
✗ Branch 4 → 7 not taken.
✓ Branch 5 → 6 taken 15 times.
✗ Branch 5 → 7 not taken.
✓ Branch 8 → 9 taken 15 times.
✓ Branch 8 → 16 taken 20 times.
35 if ((pixelsize == 2) && (env->GetCPUFlags() & CPUF_SSE4) && rowsize >= 16) {
813 // <maxThreshold, lessThan16bit>
814
1/2
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 13 taken 15 times.
15 if(maxThreshold) {
815 if(bits_per_pixel < 16)
816 accumulate_line_16_sse41<true, true>(c_plane, planeP, planes, rowsize, threshold << (bits_per_pixel - 8), div, bits_per_pixel);
817 else
818 accumulate_line_16_sse41<true, false>(c_plane, planeP, planes, rowsize, threshold << (bits_per_pixel - 8), div, bits_per_pixel);
819 }
820 else {
821
1/2
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 15 times.
15 if (bits_per_pixel < 16)
822 accumulate_line_16_sse41<false, true>(c_plane, planeP, planes, rowsize, threshold << (bits_per_pixel - 8), div, bits_per_pixel);
823 else
824 15 accumulate_line_16_sse41<false, false>(c_plane, planeP, planes, rowsize, threshold << (bits_per_pixel - 8), div, bits_per_pixel);
825 }
826
2/8
✗ Branch 16 → 17 not taken.
✓ Branch 16 → 21 taken 20 times.
✗ Branch 18 → 19 not taken.
✗ Branch 18 → 21 not taken.
✗ Branch 19 → 20 not taken.
✗ Branch 19 → 21 not taken.
✗ Branch 22 → 23 not taken.
✓ Branch 22 → 30 taken 20 times.
20 } else if ((pixelsize == 2) && (env->GetCPUFlags() & CPUF_SSE2) && rowsize >= 16) {
827 // <maxThreshold, lessThan16bit>
828 if(maxThreshold) {
829 if(bits_per_pixel < 16)
830 accumulate_line_16_sse2<true, true>(c_plane, planeP, planes, rowsize, threshold << (bits_per_pixel - 8), div, bits_per_pixel);
831 else
832 accumulate_line_16_sse2<true, false>(c_plane, planeP, planes, rowsize, threshold << (bits_per_pixel - 8), div, bits_per_pixel);
833 }
834 else {
835 if (bits_per_pixel < 16)
836 accumulate_line_16_sse2<false, true>(c_plane, planeP, planes, rowsize, threshold << (bits_per_pixel - 8), div, bits_per_pixel);
837 else
838 accumulate_line_16_sse2<false, false>(c_plane, planeP, planes, rowsize, threshold << (bits_per_pixel - 8), div, bits_per_pixel);
839 }
840 }
841
6/8
✓ Branch 30 → 31 taken 11 times.
✓ Branch 30 → 35 taken 9 times.
✓ Branch 32 → 33 taken 11 times.
✗ Branch 32 → 35 not taken.
✓ Branch 33 → 34 taken 11 times.
✗ Branch 33 → 35 not taken.
✓ Branch 36 → 37 taken 11 times.
✓ Branch 36 → 40 taken 9 times.
20 else if ((pixelsize == 1) && (env->GetCPUFlags() & CPUF_SSSE3) && rowsize >= 16) {
842
1/2
✗ Branch 37 → 38 not taken.
✓ Branch 37 → 39 taken 11 times.
11 if (maxThreshold) // <maxThreshold
843 accumulate_line_ssse3<true>(c_plane, planeP, planes, rowsize, threshold | (threshold << 8), div);
844 else
845 11 accumulate_line_ssse3<false>(c_plane, planeP, planes, rowsize, threshold | (threshold << 8), div);
846
2/8
✗ Branch 40 → 41 not taken.
✓ Branch 40 → 45 taken 9 times.
✗ Branch 42 → 43 not taken.
✗ Branch 42 → 45 not taken.
✗ Branch 43 → 44 not taken.
✗ Branch 43 → 45 not taken.
✗ Branch 46 → 47 not taken.
✓ Branch 46 → 50 taken 9 times.
9 } else if ((pixelsize == 1) && (env->GetCPUFlags() & CPUF_SSE2) && rowsize >= 16) {
847 if (maxThreshold)
848 accumulate_line_sse2<true>(c_plane, planeP, planes, rowsize, threshold | (threshold << 8), div);
849 else
850 accumulate_line_sse2<false>(c_plane, planeP, planes, rowsize, threshold | (threshold << 8), div);
851 }
852 else
853 #ifdef X86_32
854 if ((pixelsize == 1) && (env->GetCPUFlags() & CPUF_MMX) && rowsize >= 8) {
855 size_t mod8_width = rowsize / 8 * 8;
856 accumulate_line_mmx(c_plane, planeP, planes, rowsize, threshold | (threshold << 8), div);
857
858 if (mod8_width != rowsize) {
859 accumulate_line_c<uint8_t, false>(c_plane, planeP, planes, mod8_width, rowsize - mod8_width, threshold, div, bits_per_pixel);
860 }
861 } else
862 #endif
863 #endif
864
865 {
866
1/4
✗ Branch 50 → 51 not taken.
✗ Branch 50 → 55 not taken.
✓ Branch 50 → 59 taken 9 times.
✗ Branch 50 → 63 not taken.
9 switch(pixelsize) {
867 case 1:
868 if (maxThreshold)
869 accumulate_line_c<uint8_t, true>(c_plane, planeP, planes, 0, rowsize, threshold, div, bits_per_pixel);
870 else
871 accumulate_line_c<uint8_t, false>(c_plane, planeP, planes, 0, rowsize, threshold, div, bits_per_pixel);
872 break;
873 case 2:
874 if (maxThreshold)
875 accumulate_line_c<uint16_t, true>(c_plane, planeP, planes, 0, rowsize, threshold, div, bits_per_pixel);
876 else
877 accumulate_line_c<uint16_t, false>(c_plane, planeP, planes, 0, rowsize, threshold, div, bits_per_pixel);
878 break;
879 9 case 4:
880
1/2
✗ Branch 59 → 60 not taken.
✓ Branch 59 → 61 taken 9 times.
9 if (maxThreshold)
881 accumulate_line_c<float, true>(c_plane, planeP, planes, 0, rowsize, threshold, div, bits_per_pixel);
882 else
883 9 accumulate_line_c<float, false>(c_plane, planeP, planes, 0, rowsize, threshold, div, bits_per_pixel);
884 9 break;
885 }
886 }
887 35 }
888
889 template<typename pixel_t>
890 static int64_t calculate_sad_c(const BYTE* cur_ptr, const BYTE* other_ptr, int cur_pitch, int other_pitch, size_t rowsize, size_t height)
891 {
892 const pixel_t *ptr1 = reinterpret_cast<const pixel_t *>(cur_ptr);
893 const pixel_t *ptr2 = reinterpret_cast<const pixel_t *>(other_ptr);
894 size_t width = rowsize / sizeof(pixel_t);
895 cur_pitch /= sizeof(pixel_t);
896 other_pitch /= sizeof(pixel_t);
897
898 // for fullframe float may loose precision
899 typedef typename std::conditional < std::is_floating_point<pixel_t>::value, double, int64_t>::type sum_t;
900 // for one row int is enough and faster than int64
901 typedef typename std::conditional < std::is_floating_point<pixel_t>::value, float, int>::type sumrow_t;
902 sum_t sum = 0;
903
904 for (size_t y = 0; y < height; ++y) {
905 sumrow_t sumrow = 0;
906 for (size_t x = 0; x < width; ++x) {
907 sumrow += std::abs(ptr1[x] - ptr2[x]);
908 }
909 sum += sumrow;
910 ptr1 += cur_pitch;
911 ptr2 += other_pitch;
912 }
913 if (std::is_floating_point<pixel_t>::value)
914 return (int64_t)(sum * 255); // scale 0..1 based sum to 8 bit range
915 else
916 return (int64_t)sum; // for int, scaling to 8 bit range is done outside
917 }
918
919 // sum of byte-diffs.
920 static int64_t calculate_sad(const BYTE* cur_ptr, const BYTE* other_ptr, int cur_pitch, int other_pitch, size_t rowsize, size_t height, int pixelsize, int bits_per_pixel, IScriptEnvironment* env) {
921 #ifdef INTEL_INTRINSICS
922 // todo: sse for float
923 if ((pixelsize == 1) && (env->GetCPUFlags() & CPUF_SSE2) && rowsize >= 16) {
924 return (int64_t)calculate_sad_sse2<false>(cur_ptr, other_ptr, cur_pitch, other_pitch, rowsize, height);
925 }
926 #ifdef X86_32
927 if ((pixelsize ==1 ) && (env->GetCPUFlags() & CPUF_INTEGER_SSE) && rowsize >= 8) {
928 return (int64_t)calculate_sad_isse(cur_ptr, other_ptr, cur_pitch, other_pitch, rowsize, height);
929 }
930 #endif
931 // sse2 uint16_t
932 if ((pixelsize == 2) && (env->GetCPUFlags() & CPUF_SSE2) && rowsize >= 16) {
933 return calculate_sad_8_or_16_sse2<uint16_t, false>(cur_ptr, other_ptr, cur_pitch, other_pitch, rowsize, height) >> (bits_per_pixel-8);
934 }
935 #endif
936
937 switch(pixelsize) {
938 case 1: return calculate_sad_c<uint8_t>(cur_ptr, other_ptr, cur_pitch, other_pitch, rowsize, height);
939 case 2: return calculate_sad_c<uint16_t>(cur_ptr, other_ptr, cur_pitch, other_pitch, rowsize, height) >> (bits_per_pixel-8); // scale back to 8 bit range;
940 default: // case 4
941 return calculate_sad_c<float>(cur_ptr, other_ptr, cur_pitch, other_pitch, rowsize, height);
942 }
943 }
944
945 4 PVideoFrame TemporalSoften::GetFrame(int n, IScriptEnvironment* env)
946 {
947 4 int radius = (kernel-1) / 2;
948 4 int c = 0;
949
950 // Just skip if silly settings
951
952
2/6
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 4 times.
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 5 not taken.
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 10 taken 4 times.
4 if ((!luma_threshold && !chroma_threshold) || !radius)
953 {
954 PVideoFrame ret = child->GetFrame(n, env); // P.F.
955 return ret;
956 }
957
958 bool planeDisabled[16];
959
960
2/2
✓ Branch 12 → 11 taken 64 times.
✓ Branch 12 → 13 taken 4 times.
68 for (int p = 0; p<16; p++) {
961 64 planeDisabled[p] = false;
962 }
963
964 4 std::vector<PVideoFrame> frames;
965
1/2
✓ Branch 14 → 15 taken 4 times.
✗ Branch 14 → 119 not taken.
4 frames.reserve(kernel);
966
967
2/2
✓ Branch 22 → 16 taken 12 times.
✓ Branch 22 → 23 taken 4 times.
16 for (int p = n - radius; p <= n + radius; ++p) {
968
2/4
✓ Branch 18 → 19 taken 12 times.
✗ Branch 18 → 115 not taken.
✓ Branch 19 → 20 taken 12 times.
✗ Branch 19 → 113 not taken.
12 frames.emplace_back(child->GetFrame(clamp(p, 0, vi.num_frames - 1), env));
969 }
970
971 // P.F. 16.04.06 leak fix r1841 after 8 days of bug chasing:
972 // Reason #1 of the random QTGMC memory leaks (stuck frame refcounts)
973 // MakeWritable alters the pointer if it is not yet writeable, thus the original PVideoFrame won't be freed (refcount decremented)
974 // To fix this, we leave the frame[] array in its place and copy frame[radius] to CenterFrame and make further write operations on this new frame.
975 // env->MakeWritable(&frames[radius]); // old culprit line. if not yet writeable -> gives another pointer
976
1/2
✓ Branch 24 → 25 taken 4 times.
✗ Branch 24 → 119 not taken.
4 PVideoFrame CenterFrame = frames[radius];
977
1/2
✓ Branch 25 → 26 taken 4 times.
✗ Branch 25 → 117 not taken.
4 env->MakeWritable(&CenterFrame);
978
979 do {
980 const BYTE* planeP[16];
981 const BYTE* planeP2[16];
982 int planePitch[16];
983 int planePitch2[16];
984
985 10 int current_thresh = planes[c].threshold; // Threshold for current plane.
986 10 int d = 0;
987
2/2
✓ Branch 35 → 28 taken 10 times.
✓ Branch 35 → 36 taken 10 times.
20 for (int i = 0; i<radius; i++) { // Fetch all planes sequencially
988
1/2
✓ Branch 30 → 31 taken 10 times.
✗ Branch 30 → 116 not taken.
10 planePitch[d] = frames[i]->GetPitch(planes[c].planeId);
989
1/2
✓ Branch 33 → 34 taken 10 times.
✗ Branch 33 → 116 not taken.
10 planeP[d++] = frames[i]->GetReadPtr(planes[c].planeId);
990 }
991
992 // BYTE* c_plane = frames[radius]->GetWritePtr(planes[c]);
993
1/2
✓ Branch 37 → 38 taken 10 times.
✗ Branch 37 → 116 not taken.
10 BYTE* c_plane = CenterFrame->GetWritePtr(planes[c].planeId); // P.F. using CenterFrame for write access
994
995
2/2
✓ Branch 46 → 39 taken 10 times.
✓ Branch 46 → 47 taken 10 times.
20 for (int i = 1; i<=radius; i++) { // Fetch all planes sequencially
996
1/2
✓ Branch 41 → 42 taken 10 times.
✗ Branch 41 → 116 not taken.
10 planePitch[d] = frames[radius+i]->GetPitch(planes[c].planeId);
997
1/2
✓ Branch 44 → 45 taken 10 times.
✗ Branch 44 → 116 not taken.
10 planeP[d++] = frames[radius+i]->GetReadPtr(planes[c].planeId);
998 }
999
1000
1/2
✓ Branch 48 → 49 taken 10 times.
✗ Branch 48 → 116 not taken.
10 int rowsize = CenterFrame->GetRowSize(planes[c].planeId|PLANAR_ALIGNED);
1001
1/2
✓ Branch 50 → 51 taken 10 times.
✗ Branch 50 → 116 not taken.
10 int h = CenterFrame->GetHeight(planes[c].planeId);
1002
1/2
✓ Branch 52 → 53 taken 10 times.
✗ Branch 52 → 116 not taken.
10 int pitch = CenterFrame->GetPitch(planes[c].planeId);
1003
1004
1/2
✗ Branch 53 → 54 not taken.
✓ Branch 53 → 84 taken 10 times.
10 if (scenechange>0) {
1005 int d2 = 0;
1006 bool skiprest = false;
1007 for (int i = radius-1; i>=0; i--) { // Check frames backwards
1008 if ((!skiprest) && (!planeDisabled[i])) {
1009 int sad = (int)calculate_sad(c_plane, planeP[i], pitch, planePitch[i], CenterFrame->GetRowSize(planes[c].planeId), h, pixelsize, bits_per_pixel, env);
1010 if (sad < scenechange) {
1011 planePitch2[d2] = planePitch[i];
1012 planeP2[d2++] = planeP[i];
1013 } else {
1014 skiprest = true;
1015 }
1016 planeDisabled[i] = skiprest; // Disable this frame on next plane (so that Y can affect UV)
1017 } else {
1018 planeDisabled[i] = true;
1019 }
1020 }
1021 skiprest = false;
1022 for (int i = radius; i < 2*radius; i++) { // Check forward frames
1023 if ((!skiprest) && (!planeDisabled[i])) { // Disable this frame on next plane (so that Y can affect UV)
1024 int sad = (int)calculate_sad(c_plane, planeP[i], pitch, planePitch[i], CenterFrame->GetRowSize(planes[c].planeId), h, pixelsize, bits_per_pixel, env);
1025 if (sad < scenechange) {
1026 planePitch2[d2] = planePitch[i];
1027 planeP2[d2++] = planeP[i];
1028 } else {
1029 skiprest = true;
1030 }
1031 planeDisabled[i] = skiprest;
1032 } else {
1033 planeDisabled[i] = true;
1034 }
1035 }
1036
1037 //Copy back
1038 for (int i = 0; i<d2; i++) {
1039 planeP[i] = planeP2[i];
1040 planePitch[i] = planePitch2[i];
1041 }
1042 d = d2;
1043 }
1044
1045
1/2
✗ Branch 84 → 85 not taken.
✓ Branch 84 → 92 taken 10 times.
10 if (d < 1)
1046 {
1047 // Memory leak reason #2 r1841: this wasn't here before return
1048 for (int i = 0; i < kernel; ++i)
1049 frames[i] = nullptr;
1050 // return frames[radius];
1051 return CenterFrame; // return the modified frame
1052 }
1053
1054 10 int c_div = 32768/(d+1); // We also have the tetplane included, thus d+1.
1055
1/2
✓ Branch 92 → 93 taken 10 times.
✗ Branch 92 → 103 not taken.
10 if (current_thresh) {
1056 // for threshold==255 -> simple average
1057
2/2
✓ Branch 102 → 94 taken 35 times.
✓ Branch 102 → 103 taken 10 times.
45 for (int y = 0; y<h; y++) { // One line at the time
1058
2/4
✓ Branch 94 → 95 taken 35 times.
✗ Branch 94 → 116 not taken.
✗ Branch 95 → 96 not taken.
✓ Branch 95 → 97 taken 35 times.
35 if (vi.IsYUY2()) {
1059 accumulate_line_yuy2(c_plane, planeP, d, rowsize, luma_threshold, chroma_threshold, c_div, env);
1060 } else {
1061
1/2
✓ Branch 97 → 98 taken 35 times.
✗ Branch 97 → 116 not taken.
35 accumulate_line(c_plane, planeP, d, rowsize, current_thresh, c_div, pixelsize, bits_per_pixel, env);
1062 }
1063
2/2
✓ Branch 100 → 99 taken 70 times.
✓ Branch 100 → 101 taken 35 times.
105 for (int p = 0; p<d; p++)
1064 70 planeP[p] += planePitch[p];
1065 35 c_plane += pitch;
1066 }
1067 } else { // Just maintain the plane
1068 }
1069 10 c++;
1070
2/2
✓ Branch 105 → 106 taken 6 times.
✓ Branch 105 → 107 taken 4 times.
10 } while (planes[c].planeId);
1071
1072 // PVideoFrame result = frames[radius]; // we are using CenterFrame instead
1073 // return result;
1074
1/2
✓ Branch 107 → 108 taken 4 times.
✗ Branch 107 → 117 not taken.
4 return CenterFrame;
1075 4 }
1076
1077
1078 AVSValue __cdecl TemporalSoften::Create(AVSValue args, void*, IScriptEnvironment* env)
1079 {
1080 return new TemporalSoften( args[0].AsClip(), args[1].AsInt(), args[2].AsInt(),
1081 args[3].AsInt(), args[4].AsInt(0),/*args[5].AsInt(1),*/env ); //ignore mode parameter
1082 }
1083
1084
1085
1086 /****************************
1087 **** Spatial Soften *****
1088 ***************************/
1089
1090 2 SpatialSoften::SpatialSoften( PClip _child, int _radius, unsigned _luma_threshold,
1091 2 unsigned _chroma_threshold, IScriptEnvironment* env )
1092 : GenericVideoFilter(_child),
1093 2 luma_threshold(_luma_threshold), chroma_threshold(_chroma_threshold),
1094
2/4
✓ Branch 2 → 3 taken 2 times.
✗ Branch 2 → 11 not taken.
✓ Branch 3 → 4 taken 2 times.
✗ Branch 3 → 9 not taken.
2 diameter(_radius * 2 + 1)
1095 {
1096
2/4
✓ Branch 5 → 6 taken 2 times.
✗ Branch 5 → 12 not taken.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 2 times.
2 if (!vi.IsYUY2())
1097 env->ThrowError("SpatialSoften: requires YUY2 input");
1098 2 }
1099
1100
1101 1 PVideoFrame SpatialSoften::GetFrame(int n, IScriptEnvironment* env)
1102 {
1103
1/2
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 59 not taken.
1 PVideoFrame src = child->GetFrame(n, env);
1104
1/2
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 57 not taken.
1 PVideoFrame dst = env->NewVideoFrameP(vi, &src);
1105
1106
1/2
✓ Branch 6 → 7 taken 1 time.
✗ Branch 6 → 55 not taken.
1 const BYTE* srcp = src->GetReadPtr();
1107
1/2
✓ Branch 8 → 9 taken 1 time.
✗ Branch 8 → 55 not taken.
1 BYTE* dstp = dst->GetWritePtr();
1108
1/2
✓ Branch 10 → 11 taken 1 time.
✗ Branch 10 → 55 not taken.
1 int src_pitch = src->GetPitch();
1109
1/2
✓ Branch 12 → 13 taken 1 time.
✗ Branch 12 → 55 not taken.
1 int dst_pitch = dst->GetPitch();
1110
1/2
✓ Branch 14 → 15 taken 1 time.
✗ Branch 14 → 55 not taken.
1 int row_size = src->GetRowSize();
1111
1112
2/2
✓ Branch 51 → 16 taken 3 times.
✓ Branch 51 → 52 taken 1 time.
4 for (int y=0; y<vi.height; ++y)
1113 {
1114 const BYTE* line[65]; // better not make diameter bigger than this...
1115
2/2
✓ Branch 19 → 17 taken 9 times.
✓ Branch 19 → 20 taken 3 times.
12 for (int h=0; h<diameter; ++h)
1116 9 line[h] = &srcp[src_pitch * clamp(y+h-(diameter>>1), 0, vi.height-1)];
1117 int x;
1118
1119 3 int edge = (diameter+1) & -4;
1120
2/2
✓ Branch 22 → 21 taken 12 times.
✓ Branch 22 → 23 taken 3 times.
15 for (x=0; x<edge; ++x) // diameter-1 == (diameter>>1) * 2
1121 12 dstp[y*dst_pitch + x] = srcp[y*src_pitch + x];
1122
2/2
✓ Branch 46 → 24 taken 12 times.
✓ Branch 46 → 47 taken 3 times.
15 for (; x < row_size - edge; x+=2)
1123 {
1124 12 int cnt=0, _y=0, _u=0, _v=0;
1125 12 int xx = x | 3;
1126 12 int Y = srcp[y*src_pitch + x], U = srcp[y*src_pitch + xx - 2], V = srcp[y*src_pitch + xx];
1127
2/2
✓ Branch 42 → 25 taken 36 times.
✓ Branch 42 → 43 taken 12 times.
48 for (int h=0; h<diameter; ++h)
1128 {
1129
2/2
✓ Branch 40 → 26 taken 108 times.
✓ Branch 40 → 41 taken 36 times.
144 for (int w = -diameter+1; w < diameter; w += 2)
1130 {
1131 108 int xw = (x+w) | 3;
1132
1/2
✓ Branch 31 → 32 taken 36 times.
✗ Branch 31 → 36 not taken.
144 if (IsClose(line[h][x+w], Y, luma_threshold) && IsClose(line[h][xw-2], U,
1133
6/6
✓ Branch 28 → 29 taken 36 times.
✓ Branch 28 → 36 taken 72 times.
✓ Branch 34 → 35 taken 20 times.
✓ Branch 34 → 36 taken 16 times.
✓ Branch 37 → 38 taken 20 times.
✓ Branch 37 → 39 taken 88 times.
180 chroma_threshold) && IsClose(line[h][xw], V, chroma_threshold))
1134 {
1135 20 ++cnt; _y += line[h][x+w]; _u += line[h][xw-2]; _v += line[h][xw];
1136 }
1137 }
1138 }
1139 12 dstp[y*dst_pitch + x] = (_y + (cnt>>1)) / cnt;
1140
2/2
✓ Branch 43 → 44 taken 6 times.
✓ Branch 43 → 45 taken 6 times.
12 if (!(x&3)) {
1141 6 dstp[y*dst_pitch + x+1] = (_u + (cnt>>1)) / cnt;
1142 6 dstp[y*dst_pitch + x+3] = (_v + (cnt>>1)) / cnt;
1143 }
1144 }
1145
2/2
✓ Branch 49 → 48 taken 12 times.
✓ Branch 49 → 50 taken 3 times.
15 for (; x<row_size; ++x)
1146 12 dstp[y*dst_pitch + x] = srcp[y*src_pitch + x];
1147 }
1148
1149 1 return dst;
1150 1 }
1151
1152
1153 AVSValue __cdecl SpatialSoften::Create(AVSValue args, void*, IScriptEnvironment* env)
1154 {
1155 return new SpatialSoften( args[0].AsClip(), args[1].AsInt(), args[2].AsInt(),
1156 args[3].AsInt(), env );
1157 }
1158