filters/layer.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 | |||
| 36 | |||
| 37 | // Avisynth filter: Layer | ||
| 38 | // by "poptones" (poptones@myrealbox.com) | ||
| 39 | |||
| 40 | #include <avisynth.h> | ||
| 41 | #include "layer.h" | ||
| 42 | #include "merge.h" | ||
| 43 | #ifdef INTEL_INTRINSICS | ||
| 44 | #include "intel/layer_sse.h" | ||
| 45 | #include "intel/layer_avx2.h" | ||
| 46 | #include "intel/layer_sse41.h" | ||
| 47 | #endif | ||
| 48 | #ifdef NEON_INTRINSICS | ||
| 49 | #include "overlay/aarch64/layer_neon.h" | ||
| 50 | #endif | ||
| 51 | #ifdef AVS_WINDOWS | ||
| 52 | #include <avs/win.h> | ||
| 53 | #else | ||
| 54 | #include <avs/posix.h> | ||
| 55 | #endif | ||
| 56 | |||
| 57 | #include <avs/alignment.h> | ||
| 58 | #include "../core/internal.h" | ||
| 59 | #include "../convert/convert_planar.h" | ||
| 60 | #include <algorithm> | ||
| 61 | #include <vector> | ||
| 62 | |||
| 63 | ✗ | static int getPlacement(const AVSValue& _placement, IScriptEnvironment* env) { | |
| 64 | ✗ | const char* placement = _placement.AsString(0); | |
| 65 | |||
| 66 | ✗ | if (placement) { | |
| 67 | ✗ | if (!lstrcmpi(placement, "mpeg2")) | |
| 68 | ✗ | return PLACEMENT_MPEG2; | |
| 69 | |||
| 70 | ✗ | if (!lstrcmpi(placement, "mpeg1")) | |
| 71 | ✗ | return PLACEMENT_MPEG1; | |
| 72 | |||
| 73 | ✗ | if (!lstrcmpi(placement, "top_left")) | |
| 74 | ✗ | return PLACEMENT_TOPLEFT; | |
| 75 | |||
| 76 | ✗ | env->ThrowError("Layer: Unknown chroma placement"); | |
| 77 | } | ||
| 78 | ✗ | return PLACEMENT_MPEG2; | |
| 79 | } | ||
| 80 | |||
| 81 | /******************************************************************** | ||
| 82 | ***** Declare index of new filters for Avisynth's filter engine ***** | ||
| 83 | ********************************************************************/ | ||
| 84 | |||
| 85 | extern const AVSFunction Layer_filters[] = { | ||
| 86 | { "Mask", BUILTIN_FUNC_PREFIX, "cc", Mask::Create }, // clip, mask | ||
| 87 | { "ColorKeyMask", BUILTIN_FUNC_PREFIX, "ci[]i[]i[]i", ColorKeyMask::Create }, // clip, color, tolerance[B, toleranceG, toleranceR] | ||
| 88 | { "ResetMask", BUILTIN_FUNC_PREFIX, "c[mask]f[opacity]f", ResetMask::Create }, | ||
| 89 | { "Invert", BUILTIN_FUNC_PREFIX, "c[channels]s", Invert::Create }, | ||
| 90 | { "ShowAlpha", BUILTIN_FUNC_PREFIX, "c[pixel_type]s", ShowChannel::Create, (void*)3 }, // AVS+ also for YUVA, PRGBA | ||
| 91 | { "ShowRed", BUILTIN_FUNC_PREFIX, "c[pixel_type]s", ShowChannel::Create, (void*)2 }, | ||
| 92 | { "ShowGreen", BUILTIN_FUNC_PREFIX, "c[pixel_type]s", ShowChannel::Create, (void*)1 }, | ||
| 93 | { "ShowBlue", BUILTIN_FUNC_PREFIX, "c[pixel_type]s", ShowChannel::Create, (void*)0 }, | ||
| 94 | { "ShowY", BUILTIN_FUNC_PREFIX, "c[pixel_type]s", ShowChannel::Create, (void*)4 }, // AVS+ | ||
| 95 | { "ShowU", BUILTIN_FUNC_PREFIX, "c[pixel_type]s", ShowChannel::Create, (void*)5 }, // AVS+ | ||
| 96 | { "ShowV", BUILTIN_FUNC_PREFIX, "c[pixel_type]s", ShowChannel::Create, (void*)6 }, // AVS+ | ||
| 97 | { "MergeRGB", BUILTIN_FUNC_PREFIX, "ccc[pixel_type]s", MergeRGB::Create, (void*)0 }, | ||
| 98 | { "MergeARGB", BUILTIN_FUNC_PREFIX, "cccc[pixel_type]s", MergeRGB::Create, (void*)1 }, | ||
| 99 | { "Layer", BUILTIN_FUNC_PREFIX, "cc[op]s[level]i[x]i[y]i[threshold]i[use_chroma]b[opacity]f[placement]s", Layer::Create }, | ||
| 100 | /** | ||
| 101 | * Layer(clip, overlayclip, operation, amount, xpos, ypos, [threshold=0], [use_chroma=true]) | ||
| 102 | **/ | ||
| 103 | { "Subtract", BUILTIN_FUNC_PREFIX, "cc", Subtract::Create }, | ||
| 104 | { NULL } | ||
| 105 | }; | ||
| 106 | |||
| 107 | |||
| 108 | /****************************** | ||
| 109 | ******* Mask Filter ****** | ||
| 110 | ******************************/ | ||
| 111 | |||
| 112 | 1 | Mask::Mask(PClip _child1, PClip _child2, IScriptEnvironment* env) | |
| 113 |
2/4✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 39 not taken.
✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 37 not taken.
|
1 | : child1(_child1), child2(_child2) |
| 114 | { | ||
| 115 |
1/2✓ Branch 6 → 7 taken 1 time.
✗ Branch 6 → 35 not taken.
|
1 | const VideoInfo& vi1 = child1->GetVideoInfo(); |
| 116 |
1/2✓ Branch 8 → 9 taken 1 time.
✗ Branch 8 → 35 not taken.
|
1 | const VideoInfo& vi2 = child2->GetVideoInfo(); |
| 117 |
2/4✓ Branch 9 → 10 taken 1 time.
✗ Branch 9 → 11 not taken.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 1 time.
|
1 | if (vi1.width != vi2.width || vi1.height != vi2.height) |
| 118 | ✗ | env->ThrowError("Mask error: image dimensions don't match"); | |
| 119 |
5/10✓ Branch 12 → 13 taken 1 time.
✗ Branch 12 → 35 not taken.
✓ Branch 13 → 14 taken 1 time.
✗ Branch 13 → 16 not taken.
✓ Branch 14 → 15 taken 1 time.
✗ Branch 14 → 35 not taken.
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 25 taken 1 time.
✗ Branch 26 → 27 not taken.
✓ Branch 26 → 28 taken 1 time.
|
1 | if (!((vi1.IsRGB32() && vi2.IsRGB32()) || |
| 120 | ✗ | (vi1.IsRGB64() && vi2.IsRGB64()) || | |
| 121 | ✗ | (vi1.IsPlanarRGBA() && vi2.IsPlanarRGBA())) | |
| 122 | ) | ||
| 123 | ✗ | env->ThrowError("Mask error: sources must be RGB32, RGB64 or Planar RGBA"); | |
| 124 | |||
| 125 |
3/6✓ Branch 28 → 29 taken 1 time.
✗ Branch 28 → 35 not taken.
✓ Branch 29 → 30 taken 1 time.
✗ Branch 29 → 35 not taken.
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 32 taken 1 time.
|
1 | if (vi1.BitsPerComponent() != vi2.BitsPerComponent()) |
| 126 | ✗ | env->ThrowError("Mask error: Components are not of the same bit depths"); | |
| 127 | |||
| 128 | 1 | vi = vi1; | |
| 129 | |||
| 130 |
1/2✓ Branch 32 → 33 taken 1 time.
✗ Branch 32 → 35 not taken.
|
1 | pixelsize = vi.ComponentSize(); |
| 131 |
1/2✓ Branch 33 → 34 taken 1 time.
✗ Branch 33 → 35 not taken.
|
1 | bits_per_pixel = vi.BitsPerComponent(); |
| 132 | |||
| 133 | 1 | mask_frames = vi2.num_frames; | |
| 134 | 1 | } | |
| 135 | |||
| 136 | |||
| 137 | template<typename pixel_t> | ||
| 138 | ✗ | static void mask_c(BYTE* srcp8, const BYTE* alphap8, int src_pitch, int alpha_pitch, size_t width, size_t height) { | |
| 139 | ✗ | pixel_t* srcp = reinterpret_cast<pixel_t*>(srcp8); | |
| 140 | ✗ | const pixel_t* alphap = reinterpret_cast<const pixel_t*>(alphap8); | |
| 141 | |||
| 142 | ✗ | src_pitch /= sizeof(pixel_t); | |
| 143 | ✗ | alpha_pitch /= sizeof(pixel_t); | |
| 144 | |||
| 145 | ✗ | for (size_t y = 0; y < height; ++y) { | |
| 146 | ✗ | for (size_t x = 0; x < width; ++x) { | |
| 147 | ✗ | srcp[x * 4 + 3] = (cyb * alphap[x * 4 + 0] + cyg * alphap[x * 4 + 1] + cyr * alphap[x * 4 + 2] + 16384) >> 15; | |
| 148 | } | ||
| 149 | ✗ | srcp += src_pitch; | |
| 150 | ✗ | alphap += alpha_pitch; | |
| 151 | } | ||
| 152 | ✗ | } | |
| 153 | |||
| 154 | template<typename pixel_t> | ||
| 155 | ✗ | static void mask_planar_rgb_c(BYTE* dstp8, const BYTE* srcp_r8, const BYTE* srcp_g8, const BYTE* srcp_b8, int dst_pitch, int src_pitch, size_t width, size_t height, int bits_per_pixel) { | |
| 156 | ✗ | pixel_t* dstp = reinterpret_cast<pixel_t*>(dstp8); | |
| 157 | ✗ | const pixel_t* srcp_r = reinterpret_cast<const pixel_t*>(srcp_r8); | |
| 158 | ✗ | const pixel_t* srcp_g = reinterpret_cast<const pixel_t*>(srcp_g8); | |
| 159 | ✗ | const pixel_t* srcp_b = reinterpret_cast<const pixel_t*>(srcp_b8); | |
| 160 | ✗ | src_pitch /= sizeof(pixel_t); | |
| 161 | ✗ | dst_pitch /= sizeof(pixel_t); | |
| 162 | |||
| 163 | ✗ | for (size_t y = 0; y < height; ++y) { | |
| 164 | ✗ | for (size_t x = 0; x < width; ++x) { | |
| 165 | ✗ | dstp[x] = ((cyb * srcp_b[x] + cyg * srcp_g[x] + cyr * srcp_r[x] + 16384) >> 15); | |
| 166 | } | ||
| 167 | ✗ | dstp += dst_pitch; | |
| 168 | ✗ | srcp_r += src_pitch; | |
| 169 | ✗ | srcp_g += src_pitch; | |
| 170 | ✗ | srcp_b += src_pitch; | |
| 171 | } | ||
| 172 | ✗ | } | |
| 173 | |||
| 174 | ✗ | static void mask_planar_rgb_float_c(BYTE* dstp8, const BYTE* srcp_r8, const BYTE* srcp_g8, const BYTE* srcp_b8, int dst_pitch, int src_pitch, size_t width, size_t height) { | |
| 175 | |||
| 176 | ✗ | float* dstp = reinterpret_cast<float*>(dstp8); | |
| 177 | ✗ | const float* srcp_r = reinterpret_cast<const float*>(srcp_r8); | |
| 178 | ✗ | const float* srcp_g = reinterpret_cast<const float*>(srcp_g8); | |
| 179 | ✗ | const float* srcp_b = reinterpret_cast<const float*>(srcp_b8); | |
| 180 | ✗ | src_pitch /= sizeof(float); | |
| 181 | ✗ | dst_pitch /= sizeof(float); | |
| 182 | |||
| 183 | ✗ | for (size_t y = 0; y < height; ++y) { | |
| 184 | ✗ | for (size_t x = 0; x < width; ++x) { | |
| 185 | ✗ | dstp[x] = cyb_f * srcp_b[x] + cyg_f * srcp_g[x] + cyr_f * srcp_r[x]; | |
| 186 | } | ||
| 187 | ✗ | dstp += dst_pitch; | |
| 188 | ✗ | srcp_r += src_pitch; | |
| 189 | ✗ | srcp_g += src_pitch; | |
| 190 | ✗ | srcp_b += src_pitch; | |
| 191 | } | ||
| 192 | ✗ | } | |
| 193 | |||
| 194 | 1 | PVideoFrame __stdcall Mask::GetFrame(int n, IScriptEnvironment* env) | |
| 195 | { | ||
| 196 |
1/2✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 60 not taken.
|
1 | PVideoFrame src1 = child1->GetFrame(n, env); |
| 197 |
1/2✓ Branch 6 → 7 taken 1 time.
✗ Branch 6 → 55 not taken.
|
1 | PVideoFrame src2 = child2->GetFrame(std::min(n, mask_frames - 1), env); |
| 198 | |||
| 199 |
1/2✓ Branch 7 → 8 taken 1 time.
✗ Branch 7 → 56 not taken.
|
1 | env->MakeWritable(&src1); |
| 200 | |||
| 201 |
2/4✓ Branch 8 → 9 taken 1 time.
✗ Branch 8 → 56 not taken.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 27 taken 1 time.
|
1 | if (vi.IsPlanar()) { |
| 202 | // planar RGB | ||
| 203 | ✗ | BYTE* dstp = src1->GetWritePtr(PLANAR_A); // destination Alpha plane | |
| 204 | |||
| 205 | ✗ | const BYTE* srcp_g = src2->GetReadPtr(PLANAR_G); | |
| 206 | ✗ | const BYTE* srcp_b = src2->GetReadPtr(PLANAR_B); | |
| 207 | ✗ | const BYTE* srcp_r = src2->GetReadPtr(PLANAR_R); | |
| 208 | |||
| 209 | ✗ | const int dst_pitch = src1->GetPitch(); | |
| 210 | ✗ | const int src_pitch = src2->GetPitch(); | |
| 211 | |||
| 212 | // clip1_alpha = greyscale(clip2) | ||
| 213 | ✗ | if (pixelsize == 1) | |
| 214 | ✗ | mask_planar_rgb_c<uint8_t>(dstp, srcp_r, srcp_g, srcp_b, dst_pitch, src_pitch, vi.width, vi.height, bits_per_pixel); | |
| 215 | ✗ | else if (pixelsize == 2) | |
| 216 | ✗ | mask_planar_rgb_c<uint16_t>(dstp, srcp_r, srcp_g, srcp_b, dst_pitch, src_pitch, vi.width, vi.height, bits_per_pixel); | |
| 217 | else | ||
| 218 | ✗ | mask_planar_rgb_float_c(dstp, srcp_r, srcp_g, srcp_b, dst_pitch, src_pitch, vi.width, vi.height); | |
| 219 | } | ||
| 220 | else { | ||
| 221 | // Packed RGB32/64 | ||
| 222 |
1/2✓ Branch 28 → 29 taken 1 time.
✗ Branch 28 → 56 not taken.
|
1 | BYTE* src1p = src1->GetWritePtr(); |
| 223 |
1/2✓ Branch 30 → 31 taken 1 time.
✗ Branch 30 → 56 not taken.
|
1 | const BYTE* src2p = src2->GetReadPtr(); |
| 224 | |||
| 225 |
1/2✓ Branch 32 → 33 taken 1 time.
✗ Branch 32 → 56 not taken.
|
1 | const int src1_pitch = src1->GetPitch(); |
| 226 |
1/2✓ Branch 34 → 35 taken 1 time.
✗ Branch 34 → 56 not taken.
|
1 | const int src2_pitch = src2->GetPitch(); |
| 227 | |||
| 228 | // clip1_alpha = greyscale(clip2) | ||
| 229 | #ifdef INTEL_INTRINSICS | ||
| 230 |
4/8✓ Branch 35 → 36 taken 1 time.
✗ Branch 35 → 39 not taken.
✓ Branch 36 → 37 taken 1 time.
✗ Branch 36 → 56 not taken.
✓ Branch 37 → 38 taken 1 time.
✗ Branch 37 → 39 not taken.
✓ Branch 40 → 41 taken 1 time.
✗ Branch 40 → 42 not taken.
|
1 | if ((pixelsize == 1) && (env->GetCPUFlags() & CPUF_AVX2)) |
| 231 | { | ||
| 232 |
1/2✓ Branch 41 → 52 taken 1 time.
✗ Branch 41 → 56 not taken.
|
1 | mask_avx2(src1p, src2p, src1_pitch, src2_pitch, vi.width, vi.height); |
| 233 | } | ||
| 234 | ✗ | else if ((pixelsize == 1) && (env->GetCPUFlags() & CPUF_SSE2)) | |
| 235 | { | ||
| 236 | ✗ | mask_sse2(src1p, src2p, src1_pitch, src2_pitch, vi.width, vi.height); | |
| 237 | } | ||
| 238 | else | ||
| 239 | #endif | ||
| 240 | { | ||
| 241 | ✗ | if (pixelsize == 1) { // RGB32 | |
| 242 | ✗ | mask_c<uint8_t>(src1p, src2p, src1_pitch, src2_pitch, vi.width, vi.height); | |
| 243 | } | ||
| 244 | else { // if (pixelsize == 2) RGB64 | ||
| 245 | ✗ | mask_c<uint16_t>(src1p, src2p, src1_pitch, src2_pitch, vi.width, vi.height); | |
| 246 | } | ||
| 247 | } | ||
| 248 | } | ||
| 249 | |||
| 250 | 1 | return src1; | |
| 251 | 1 | } | |
| 252 | |||
| 253 | ✗ | AVSValue __cdecl Mask::Create(AVSValue args, void*, IScriptEnvironment* env) | |
| 254 | { | ||
| 255 | ✗ | return new Mask(args[0].AsClip(), args[1].AsClip(), env); | |
| 256 | } | ||
| 257 | |||
| 258 | |||
| 259 | /************************************** | ||
| 260 | ******* ColorKeyMask Filter ****** | ||
| 261 | **************************************/ | ||
| 262 | |||
| 263 | |||
| 264 | 1 | ColorKeyMask::ColorKeyMask(PClip _child, int _color, int _tolB, int _tolG, int _tolR, IScriptEnvironment* env) | |
| 265 |
2/4✓ Branch 2 → 3 taken 1 time.
✗ Branch 2 → 27 not taken.
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 25 not taken.
|
1 | : GenericVideoFilter(_child), color(_color & 0xffffff), tolB(_tolB & 0xff), tolG(_tolG & 0xff), tolR(_tolR & 0xff) |
| 266 | { | ||
| 267 |
3/14✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 28 not taken.
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 12 taken 1 time.
✗ Branch 7 → 8 not taken.
✗ Branch 7 → 28 not taken.
✗ Branch 8 → 9 not taken.
✗ Branch 8 → 12 not taken.
✗ Branch 9 → 10 not taken.
✗ Branch 9 → 28 not taken.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 1 time.
|
1 | if (!vi.IsRGB32() && !vi.IsRGB64() && !vi.IsPlanarRGBA()) |
| 268 | ✗ | env->ThrowError("ColorKeyMask: requires RGB32, RGB64 or Planar RGBA input"); | |
| 269 |
1/2✓ Branch 15 → 16 taken 1 time.
✗ Branch 15 → 28 not taken.
|
1 | pixelsize = vi.ComponentSize(); |
| 270 |
1/2✓ Branch 16 → 17 taken 1 time.
✗ Branch 16 → 28 not taken.
|
1 | bits_per_pixel = vi.BitsPerComponent(); |
| 271 | 1 | max_pixel_value = (1 << bits_per_pixel) - 1; | |
| 272 | |||
| 273 | 7 | auto rgbcolor8to16 = [](uint8_t color8, int max_pixel_value) { return (uint16_t)(color8 * max_pixel_value / 255); }; | |
| 274 | |||
| 275 | 1 | uint64_t r = rgbcolor8to16((color >> 16) & 0xFF, max_pixel_value); | |
| 276 | 1 | uint64_t g = rgbcolor8to16((color >> 8) & 0xFF, max_pixel_value); | |
| 277 | 1 | uint64_t b = rgbcolor8to16((color) & 0xFF, max_pixel_value); | |
| 278 | 1 | uint64_t a = rgbcolor8to16((color >> 24) & 0xFF, max_pixel_value); | |
| 279 | 1 | color64 = (a << 48) + (r << 32) + (g << 16) + (b); | |
| 280 | 1 | tolR16 = rgbcolor8to16(tolR & 0xFF, max_pixel_value); // scale tolerance | |
| 281 | 1 | tolG16 = rgbcolor8to16(tolG & 0xFF, max_pixel_value); | |
| 282 | 1 | tolB16 = rgbcolor8to16(tolB & 0xFF, max_pixel_value); | |
| 283 | 1 | } | |
| 284 | |||
| 285 | |||
| 286 | template<typename pixel_t> | ||
| 287 | ✗ | static void colorkeymask_c(BYTE* pf8, int pitch, int R, int G, int B, int height, int rowsize, int tolB, int tolG, int tolR) { | |
| 288 | ✗ | pixel_t* pf = reinterpret_cast<pixel_t*>(pf8); | |
| 289 | ✗ | rowsize /= sizeof(pixel_t); | |
| 290 | ✗ | pitch /= sizeof(pixel_t); | |
| 291 | ✗ | for (int y = 0; y < height; y++) { | |
| 292 | ✗ | for (int x = 0; x < rowsize; x += 4) { | |
| 293 | ✗ | if (IsClose(pf[x], B, tolB) && IsClose(pf[x + 1], G, tolG) && IsClose(pf[x + 2], R, tolR)) | |
| 294 | ✗ | pf[x + 3] = 0; | |
| 295 | } | ||
| 296 | ✗ | pf += pitch; | |
| 297 | } | ||
| 298 | ✗ | } | |
| 299 | |||
| 300 | template<typename pixel_t> | ||
| 301 | ✗ | static void colorkeymask_planar_c(const BYTE* pfR8, const BYTE* pfG8, const BYTE* pfB8, BYTE* pfA8, int pitch, int R, int G, int B, int height, int width, int tolB, int tolG, int tolR) { | |
| 302 | ✗ | const pixel_t* pfR = reinterpret_cast<const pixel_t*>(pfR8); | |
| 303 | ✗ | const pixel_t* pfG = reinterpret_cast<const pixel_t*>(pfG8); | |
| 304 | ✗ | const pixel_t* pfB = reinterpret_cast<const pixel_t*>(pfB8); | |
| 305 | ✗ | pixel_t* pfA = reinterpret_cast<pixel_t*>(pfA8); | |
| 306 | ✗ | pitch /= sizeof(pixel_t); | |
| 307 | ✗ | for (int y = 0; y < height; y++) { | |
| 308 | ✗ | for (int x = 0; x < width; x++) { | |
| 309 | ✗ | if (IsClose(pfB[x], B, tolB) && IsClose(pfG[x], G, tolG) && IsClose(pfR[x], R, tolR)) | |
| 310 | ✗ | pfA[x] = 0; | |
| 311 | } | ||
| 312 | ✗ | pfR += pitch; | |
| 313 | ✗ | pfG += pitch; | |
| 314 | ✗ | pfB += pitch; | |
| 315 | ✗ | pfA += pitch; | |
| 316 | } | ||
| 317 | ✗ | } | |
| 318 | |||
| 319 | ✗ | static void colorkeymask_planar_float_c(const BYTE* pfR8, const BYTE* pfG8, const BYTE* pfB8, BYTE* pfA8, int pitch, float R, float G, float B, int height, int width, float tolB, float tolG, float tolR) { | |
| 320 | typedef float pixel_t; | ||
| 321 | ✗ | const pixel_t* pfR = reinterpret_cast<const pixel_t*>(pfR8); | |
| 322 | ✗ | const pixel_t* pfG = reinterpret_cast<const pixel_t*>(pfG8); | |
| 323 | ✗ | const pixel_t* pfB = reinterpret_cast<const pixel_t*>(pfB8); | |
| 324 | ✗ | pixel_t* pfA = reinterpret_cast<pixel_t*>(pfA8); | |
| 325 | ✗ | pitch /= sizeof(pixel_t); | |
| 326 | ✗ | for (int y = 0; y < height; y++) { | |
| 327 | ✗ | for (int x = 0; x < width; x++) { | |
| 328 | ✗ | if (IsCloseFloat(pfB[x], B, tolB) && IsCloseFloat(pfG[x], G, tolG) && IsCloseFloat(pfR[x], R, tolR)) | |
| 329 | ✗ | pfA[x] = 0; | |
| 330 | } | ||
| 331 | ✗ | pfR += pitch; | |
| 332 | ✗ | pfG += pitch; | |
| 333 | ✗ | pfB += pitch; | |
| 334 | ✗ | pfA += pitch; | |
| 335 | } | ||
| 336 | ✗ | } | |
| 337 | |||
| 338 | |||
| 339 | 1 | PVideoFrame __stdcall ColorKeyMask::GetFrame(int n, IScriptEnvironment* env) | |
| 340 | { | ||
| 341 | 1 | PVideoFrame frame = child->GetFrame(n, env); | |
| 342 |
1/2✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 47 not taken.
|
1 | env->MakeWritable(&frame); |
| 343 | |||
| 344 |
1/2✓ Branch 6 → 7 taken 1 time.
✗ Branch 6 → 47 not taken.
|
1 | BYTE* pf = frame->GetWritePtr(); |
| 345 |
1/2✓ Branch 8 → 9 taken 1 time.
✗ Branch 8 → 47 not taken.
|
1 | const int pitch = frame->GetPitch(); |
| 346 |
1/2✓ Branch 10 → 11 taken 1 time.
✗ Branch 10 → 47 not taken.
|
1 | const int rowsize = frame->GetRowSize(); |
| 347 | |||
| 348 |
2/4✓ Branch 11 → 12 taken 1 time.
✗ Branch 11 → 47 not taken.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 28 taken 1 time.
|
1 | if (vi.IsPlanarRGBA()) { |
| 349 | ✗ | const BYTE* pf_g = frame->GetReadPtr(PLANAR_G); | |
| 350 | ✗ | const BYTE* pf_b = frame->GetReadPtr(PLANAR_B); | |
| 351 | ✗ | const BYTE* pf_r = frame->GetReadPtr(PLANAR_R); | |
| 352 | ✗ | BYTE* pf_a = frame->GetWritePtr(PLANAR_A); | |
| 353 | |||
| 354 | ✗ | const int pitch = frame->GetPitch(); | |
| 355 | ✗ | const int width = vi.width; | |
| 356 | |||
| 357 | ✗ | if (pixelsize == 1) { | |
| 358 | ✗ | const int R = (color >> 16) & 0xff; | |
| 359 | ✗ | const int G = (color >> 8) & 0xff; | |
| 360 | ✗ | const int B = color & 0xff; | |
| 361 | ✗ | colorkeymask_planar_c<uint8_t>(pf_r, pf_g, pf_b, pf_a, pitch, R, G, B, vi.height, width, tolB, tolG, tolR); | |
| 362 | } | ||
| 363 | ✗ | else if (pixelsize == 2) { | |
| 364 | ✗ | const int R = (color64 >> 32) & 0xffff; | |
| 365 | ✗ | const int G = (color64 >> 16) & 0xffff; | |
| 366 | ✗ | const int B = color64 & 0xffff; | |
| 367 | ✗ | colorkeymask_planar_c<uint16_t>(pf_r, pf_g, pf_b, pf_a, pitch, R, G, B, vi.height, width, tolB16, tolG16, tolR16); | |
| 368 | } | ||
| 369 | else { // float | ||
| 370 | ✗ | const float R = ((color >> 16) & 0xff) / 255.0f; | |
| 371 | ✗ | const float G = ((color >> 8) & 0xff) / 255.0f; | |
| 372 | ✗ | const float B = (color & 0xff) / 255.0f; | |
| 373 | ✗ | colorkeymask_planar_float_c(pf_r, pf_g, pf_b, pf_a, pitch, R, G, B, vi.height, width, tolB / 255.0f, tolG / 255.0f, tolR / 255.0f); | |
| 374 | } | ||
| 375 | } | ||
| 376 | else { | ||
| 377 | // RGB32, RGB64 | ||
| 378 | #ifdef INTEL_INTRINSICS | ||
| 379 |
4/8✓ Branch 28 → 29 taken 1 time.
✗ Branch 28 → 32 not taken.
✓ Branch 29 → 30 taken 1 time.
✗ Branch 29 → 47 not taken.
✓ Branch 30 → 31 taken 1 time.
✗ Branch 30 → 32 not taken.
✓ Branch 33 → 34 taken 1 time.
✗ Branch 33 → 35 not taken.
|
1 | if ((pixelsize == 1) && (env->GetCPUFlags() & CPUF_AVX2)) |
| 380 | { | ||
| 381 |
1/2✓ Branch 34 → 45 taken 1 time.
✗ Branch 34 → 47 not taken.
|
1 | colorkeymask_avx2(pf, pitch, color, vi.height, rowsize, tolB, tolG, tolR); |
| 382 | } | ||
| 383 | ✗ | else if ((pixelsize == 1) && (env->GetCPUFlags() & CPUF_SSE2)) | |
| 384 | { | ||
| 385 | ✗ | colorkeymask_sse2(pf, pitch, color, vi.height, rowsize, tolB, tolG, tolR); | |
| 386 | } | ||
| 387 | else | ||
| 388 | #endif | ||
| 389 | { | ||
| 390 | ✗ | if (pixelsize == 1) { | |
| 391 | ✗ | const int R = (color >> 16) & 0xff; | |
| 392 | ✗ | const int G = (color >> 8) & 0xff; | |
| 393 | ✗ | const int B = color & 0xff; | |
| 394 | ✗ | colorkeymask_c<uint8_t>(pf, pitch, R, G, B, vi.height, rowsize, tolB, tolG, tolR); | |
| 395 | } | ||
| 396 | else { | ||
| 397 | ✗ | const int R = (color64 >> 32) & 0xffff; | |
| 398 | ✗ | const int G = (color64 >> 16) & 0xffff; | |
| 399 | ✗ | const int B = color64 & 0xffff; | |
| 400 | ✗ | colorkeymask_c<uint16_t>(pf, pitch, R, G, B, vi.height, rowsize, tolB16, tolG16, tolR16); | |
| 401 | } | ||
| 402 | } | ||
| 403 | } | ||
| 404 | |||
| 405 | 1 | return frame; | |
| 406 | ✗ | } | |
| 407 | |||
| 408 | ✗ | AVSValue __cdecl ColorKeyMask::Create(AVSValue args, void*, IScriptEnvironment* env) | |
| 409 | { | ||
| 410 | enum { CHILD, COLOR, TOLERANCE_B, TOLERANCE_G, TOLERANCE_R }; | ||
| 411 | ✗ | return new ColorKeyMask(args[CHILD].AsClip(), | |
| 412 | ✗ | args[COLOR].AsInt(0), | |
| 413 | ✗ | args[TOLERANCE_B].AsInt(10), | |
| 414 | ✗ | args[TOLERANCE_G].AsInt(args[TOLERANCE_B].AsInt(10)), | |
| 415 | ✗ | args[TOLERANCE_R].AsInt(args[TOLERANCE_B].AsInt(10)), env); | |
| 416 | } | ||
| 417 | |||
| 418 | |||
| 419 | /******************************** | ||
| 420 | ****** ResetMask filter ****** | ||
| 421 | ********************************/ | ||
| 422 | |||
| 423 | |||
| 424 | 7 | ResetMask::ResetMask(PClip _child, AVSValue _mask_f, AVSValue _opacity_f, IScriptEnvironment* env) | |
| 425 |
2/4✓ Branch 2 → 3 taken 7 times.
✗ Branch 2 → 47 not taken.
✓ Branch 3 → 4 taken 7 times.
✗ Branch 3 → 45 not taken.
|
7 | : GenericVideoFilter(_child) |
| 426 | { | ||
| 427 |
13/18✓ Branch 5 → 6 taken 7 times.
✗ Branch 5 → 48 not taken.
✓ Branch 6 → 7 taken 6 times.
✓ Branch 6 → 14 taken 1 time.
✓ Branch 7 → 8 taken 6 times.
✗ Branch 7 → 48 not taken.
✓ Branch 8 → 9 taken 6 times.
✗ Branch 8 → 14 not taken.
✓ Branch 9 → 10 taken 6 times.
✗ Branch 9 → 48 not taken.
✓ Branch 10 → 11 taken 3 times.
✓ Branch 10 → 14 taken 3 times.
✓ Branch 11 → 12 taken 3 times.
✗ Branch 11 → 48 not taken.
✓ Branch 12 → 13 taken 1 time.
✓ Branch 12 → 14 taken 2 times.
✓ Branch 15 → 16 taken 1 time.
✓ Branch 15 → 17 taken 6 times.
|
7 | if (!(vi.IsRGB32() || vi.IsRGB64() || vi.IsPlanarRGBA() || vi.IsYUVA())) |
| 428 |
1/2✗ Branch 16 → 17 not taken.
✓ Branch 16 → 48 taken 1 time.
|
1 | env->ThrowError("ResetMask: format has no alpha channel"); |
| 429 | |||
| 430 |
1/2✓ Branch 17 → 18 taken 6 times.
✗ Branch 17 → 48 not taken.
|
6 | mask_f = _mask_f.AsFloatf(-1.0f); |
| 431 |
1/2✓ Branch 18 → 19 taken 6 times.
✗ Branch 18 → 48 not taken.
|
6 | const bool mask_defined = _mask_f.Defined(); |
| 432 |
1/2✓ Branch 19 → 20 taken 6 times.
✗ Branch 19 → 48 not taken.
|
6 | const bool opacity_defined = _opacity_f.Defined(); |
| 433 |
4/4✓ Branch 20 → 21 taken 5 times.
✓ Branch 20 → 23 taken 1 time.
✓ Branch 21 → 22 taken 1 time.
✓ Branch 21 → 23 taken 4 times.
|
6 | if (mask_defined && mask_f < 0.0f) { |
| 434 |
1/2✗ Branch 22 → 23 not taken.
✓ Branch 22 → 48 taken 1 time.
|
1 | env->ThrowError("ResetMask: mask value must be non-negative"); |
| 435 | } | ||
| 436 | // only for integers | ||
| 437 |
4/6✓ Branch 23 → 24 taken 5 times.
✗ Branch 23 → 48 not taken.
✓ Branch 24 → 25 taken 2 times.
✓ Branch 24 → 27 taken 3 times.
✓ Branch 25 → 26 taken 2 times.
✗ Branch 25 → 48 not taken.
|
5 | int max_pixel_value = vi.BitsPerComponent() <= 16 ? (1 << vi.BitsPerComponent()) - 1 : /*n/a*/ 255; |
| 438 | // mask and mask_f are the exact unscaled values to put into A channel (integer/float) | ||
| 439 |
2/2✓ Branch 28 → 29 taken 1 time.
✓ Branch 28 → 30 taken 4 times.
|
5 | if (!mask_defined) { |
| 440 | 1 | mask_f = 1.0f; | |
| 441 | 1 | mask = max_pixel_value; | |
| 442 | } | ||
| 443 | else { | ||
| 444 | // mask is defined - convert to integer for non-float formats | ||
| 445 |
3/4✓ Branch 30 → 31 taken 4 times.
✗ Branch 30 → 48 not taken.
✓ Branch 31 → 32 taken 2 times.
✓ Branch 31 → 33 taken 2 times.
|
4 | if (vi.ComponentSize() < 4) { |
| 446 | // integer format - use mask value directly | ||
| 447 | 2 | mask = (int)(mask_f + 0.5f); | |
| 448 | } | ||
| 449 | // for float formats, mask_f is already set correctly | ||
| 450 | } | ||
| 451 | // no check, allow rounding errors | ||
| 452 | /* | ||
| 453 | if (opacity_defined && _opacity_f.AsFloat(1.0f) < 0.0f) { | ||
| 454 | env->ThrowError("ResetMask: opacity value must be non-negative"); | ||
| 455 | } | ||
| 456 | if (opacity_defined && _opacity_f.AsFloat(1.0f) > 1.0f) { | ||
| 457 | env->ThrowError("ResetMask: opacity value must be between 0.0 and 1.0"); | ||
| 458 | } | ||
| 459 | */ | ||
| 460 |
2/2✓ Branch 33 → 34 taken 2 times.
✓ Branch 33 → 40 taken 3 times.
|
5 | if (opacity_defined) { |
| 461 | // if opacity is defined, calculate mask_f from opacity | ||
| 462 |
1/2✓ Branch 34 → 35 taken 2 times.
✗ Branch 34 → 48 not taken.
|
2 | mask_f = _opacity_f.AsFloatf(1.0f); |
| 463 |
1/2✗ Branch 35 → 36 not taken.
✓ Branch 35 → 37 taken 2 times.
|
2 | if (mask_f < 0.0f) mask_f = 0.0f; |
| 464 |
1/2✗ Branch 37 → 38 not taken.
✓ Branch 37 → 39 taken 2 times.
|
2 | if (mask_f > 1.0f) mask_f = 1.0f; |
| 465 | 2 | mask = (int)(mask_f * max_pixel_value + 0.5f); | |
| 466 | } | ||
| 467 | |||
| 468 | // mask (bit depth dependent): unscaled value. If none -> max OPACITY (fully opaque) | ||
| 469 | // | ||
| 470 | // opacity parameter (0.0 to 1.0): | ||
| 471 | // opacity = 1.0 means OPAQUE (fully visible, mask value 255/1023/etc. : white) | ||
| 472 | // opacity = 0.0 means TRANSPARENT (invisible, mask value 0 : black) | ||
| 473 | // when opacity is set, it overrides mask | ||
| 474 | |||
| 475 | 5 | mask = std::min(std::max(mask, 0), max_pixel_value); | |
| 476 | 5 | mask_f = std::min(std::max(mask_f, 0.0f), 1.0f); | |
| 477 | 7 | } | |
| 478 | |||
| 479 | |||
| 480 | 4 | PVideoFrame ResetMask::GetFrame(int n, IScriptEnvironment* env) | |
| 481 | { | ||
| 482 | 4 | PVideoFrame f = child->GetFrame(n, env); | |
| 483 |
1/2✓ Branch 4 → 5 taken 4 times.
✗ Branch 4 → 55 not taken.
|
4 | env->MakeWritable(&f); |
| 484 | |||
| 485 |
8/10✓ Branch 5 → 6 taken 4 times.
✗ Branch 5 → 55 not taken.
✓ Branch 6 → 7 taken 3 times.
✓ Branch 6 → 9 taken 1 time.
✓ Branch 7 → 8 taken 3 times.
✗ Branch 7 → 55 not taken.
✓ Branch 8 → 9 taken 2 times.
✓ Branch 8 → 10 taken 1 time.
✓ Branch 11 → 12 taken 3 times.
✓ Branch 11 → 29 taken 1 time.
|
4 | if (vi.IsPlanarRGBA() || vi.IsYUVA()) { |
| 486 |
1/2✓ Branch 13 → 14 taken 3 times.
✗ Branch 13 → 55 not taken.
|
3 | const int dst_rowsizeA = f->GetRowSize(PLANAR_A); |
| 487 |
1/2✓ Branch 15 → 16 taken 3 times.
✗ Branch 15 → 55 not taken.
|
3 | const int dst_pitchA = f->GetPitch(PLANAR_A); |
| 488 |
1/2✓ Branch 17 → 18 taken 3 times.
✗ Branch 17 → 55 not taken.
|
3 | BYTE* dstp_a = f->GetWritePtr(PLANAR_A); |
| 489 |
1/2✓ Branch 19 → 20 taken 3 times.
✗ Branch 19 → 55 not taken.
|
3 | const int heightA = f->GetHeight(PLANAR_A); |
| 490 | |||
| 491 |
3/6✓ Branch 20 → 21 taken 3 times.
✗ Branch 20 → 55 not taken.
✓ Branch 21 → 22 taken 2 times.
✗ Branch 21 → 24 not taken.
✓ Branch 21 → 26 taken 1 time.
✗ Branch 21 → 28 not taken.
|
3 | switch (vi.ComponentSize()) |
| 492 | { | ||
| 493 | 2 | case 1: | |
| 494 |
1/2✓ Branch 22 → 23 taken 2 times.
✗ Branch 22 → 55 not taken.
|
2 | fill_plane<BYTE>(dstp_a, heightA, dst_rowsizeA, dst_pitchA, mask); |
| 495 | 2 | break; | |
| 496 | ✗ | case 2: | |
| 497 | ✗ | fill_plane<uint16_t>(dstp_a, heightA, dst_rowsizeA, dst_pitchA, mask); | |
| 498 | ✗ | break; | |
| 499 | 1 | case 4: | |
| 500 |
1/2✓ Branch 26 → 27 taken 1 time.
✗ Branch 26 → 55 not taken.
|
1 | fill_plane<float>(dstp_a, heightA, dst_rowsizeA, dst_pitchA, mask_f); |
| 501 | 1 | break; | |
| 502 | } | ||
| 503 | 3 | return f; | |
| 504 | } | ||
| 505 | // RGB32 and RGB64 | ||
| 506 | |||
| 507 |
1/2✓ Branch 30 → 31 taken 1 time.
✗ Branch 30 → 55 not taken.
|
1 | BYTE* pf = f->GetWritePtr(); |
| 508 |
1/2✓ Branch 32 → 33 taken 1 time.
✗ Branch 32 → 55 not taken.
|
1 | int pitch = f->GetPitch(); |
| 509 |
1/2✓ Branch 34 → 35 taken 1 time.
✗ Branch 34 → 55 not taken.
|
1 | int rowsize = f->GetRowSize(); |
| 510 |
1/2✓ Branch 36 → 37 taken 1 time.
✗ Branch 36 → 55 not taken.
|
1 | int height = f->GetHeight(); |
| 511 | |||
| 512 |
2/4✓ Branch 37 → 38 taken 1 time.
✗ Branch 37 → 55 not taken.
✓ Branch 38 → 39 taken 1 time.
✗ Branch 38 → 45 not taken.
|
1 | if (vi.IsRGB32()) { |
| 513 |
2/2✓ Branch 44 → 40 taken 2 times.
✓ Branch 44 → 53 taken 1 time.
|
3 | for (int y = 0; y < height; y++) { |
| 514 |
2/2✓ Branch 42 → 41 taken 10 times.
✓ Branch 42 → 43 taken 2 times.
|
12 | for (int x = 3; x < rowsize; x += 4) { |
| 515 | 10 | pf[x] = mask; | |
| 516 | } | ||
| 517 | 2 | pf += pitch; | |
| 518 | } | ||
| 519 | } | ||
| 520 | ✗ | else if (vi.IsRGB64()) { | |
| 521 | ✗ | rowsize /= sizeof(uint16_t); | |
| 522 | ✗ | for (int y = 0; y < height; y++) { | |
| 523 | ✗ | for (int x = 3; x < rowsize; x += 4) { | |
| 524 | ✗ | reinterpret_cast<uint16_t*>(pf)[x] = mask; | |
| 525 | } | ||
| 526 | ✗ | pf += pitch; | |
| 527 | } | ||
| 528 | } | ||
| 529 | |||
| 530 | 1 | return f; | |
| 531 | ✗ | } | |
| 532 | |||
| 533 | |||
| 534 | ✗ | AVSValue ResetMask::Create(AVSValue args, void*, IScriptEnvironment* env) | |
| 535 | { | ||
| 536 | ✗ | return new ResetMask(args[0].AsClip(), args[1], args[2], env); | |
| 537 | } | ||
| 538 | |||
| 539 | |||
| 540 | /******************************** | ||
| 541 | ****** Invert filter ****** | ||
| 542 | ********************************/ | ||
| 543 | |||
| 544 | |||
| 545 | 1 | Invert::Invert(PClip _child, const char* _channels, IScriptEnvironment* env) | |
| 546 |
2/4✓ Branch 2 → 3 taken 1 time.
✗ Branch 2 → 68 not taken.
✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 66 not taken.
|
1 | : GenericVideoFilter(_child) |
| 547 | { | ||
| 548 | 1 | doB = doG = doR = doA = doY = doU = doV = false; | |
| 549 | |||
| 550 |
2/2✓ Branch 19 → 6 taken 2 times.
✓ Branch 19 → 20 taken 1 time.
|
3 | for (int k = 0; _channels[k] != '\0'; ++k) { |
| 551 |
2/8✗ Branch 6 → 7 not taken.
✗ Branch 6 → 8 not taken.
✗ Branch 6 → 9 not taken.
✗ Branch 6 → 10 not taken.
✓ Branch 6 → 12 taken 1 time.
✓ Branch 6 → 13 taken 1 time.
✗ Branch 6 → 15 not taken.
✗ Branch 6 → 17 not taken.
|
2 | switch (_channels[k]) { |
| 552 | ✗ | case 'B': | |
| 553 | case 'b': | ||
| 554 | ✗ | doB = true; | |
| 555 | ✗ | break; | |
| 556 | ✗ | case 'G': | |
| 557 | case 'g': | ||
| 558 | ✗ | doG = true; | |
| 559 | ✗ | break; | |
| 560 | ✗ | case 'R': | |
| 561 | case 'r': | ||
| 562 | ✗ | doR = true; | |
| 563 | ✗ | break; | |
| 564 | ✗ | case 'A': | |
| 565 | case 'a': | ||
| 566 | ✗ | doA = (vi.NumComponents() > 3); | |
| 567 | ✗ | break; | |
| 568 | 1 | case 'Y': | |
| 569 | case 'y': | ||
| 570 | 1 | doY = true; | |
| 571 | 1 | break; | |
| 572 | 1 | case 'U': | |
| 573 | case 'u': | ||
| 574 |
1/2✓ Branch 13 → 14 taken 1 time.
✗ Branch 13 → 69 not taken.
|
1 | doU = (vi.NumComponents() > 1); |
| 575 | 1 | break; | |
| 576 | ✗ | case 'V': | |
| 577 | case 'v': | ||
| 578 | ✗ | doV = (vi.NumComponents() > 1); | |
| 579 | ✗ | break; | |
| 580 | ✗ | default: | |
| 581 | ✗ | break; | |
| 582 | } | ||
| 583 | } | ||
| 584 |
1/2✓ Branch 20 → 21 taken 1 time.
✗ Branch 20 → 69 not taken.
|
1 | pixelsize = vi.ComponentSize(); |
| 585 |
1/2✓ Branch 21 → 22 taken 1 time.
✗ Branch 21 → 69 not taken.
|
1 | bits_per_pixel = vi.BitsPerComponent(); |
| 586 |
2/4✓ Branch 22 → 23 taken 1 time.
✗ Branch 22 → 69 not taken.
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 34 taken 1 time.
|
1 | if (vi.IsYUY2()) { |
| 587 | ✗ | mask = doY ? 0x00ff00ff : 0; | |
| 588 | ✗ | mask |= doU ? 0x0000ff00 : 0; | |
| 589 | ✗ | mask |= doV ? 0xff000000 : 0; | |
| 590 | } | ||
| 591 |
2/4✓ Branch 34 → 35 taken 1 time.
✗ Branch 34 → 69 not taken.
✗ Branch 35 → 36 not taken.
✓ Branch 35 → 49 taken 1 time.
|
1 | else if (vi.IsRGB32()) { |
| 592 | ✗ | mask = doB ? 0x000000ff : 0; | |
| 593 | ✗ | mask |= doG ? 0x0000ff00 : 0; | |
| 594 | ✗ | mask |= doR ? 0x00ff0000 : 0; | |
| 595 | ✗ | mask |= doA ? 0xff000000 : 0; | |
| 596 | } | ||
| 597 |
2/4✓ Branch 49 → 50 taken 1 time.
✗ Branch 49 → 69 not taken.
✗ Branch 50 → 51 not taken.
✓ Branch 50 → 64 taken 1 time.
|
1 | else if (vi.IsRGB64()) { |
| 598 | ✗ | mask64 = doB ? 0x000000000000ffffull : 0; | |
| 599 | ✗ | mask64 |= (doG ? 0x00000000ffff0000ull : 0); | |
| 600 | ✗ | mask64 |= (doR ? 0x0000ffff00000000ull : 0); | |
| 601 | ✗ | mask64 |= (doA ? 0xffff000000000000ull : 0); | |
| 602 | } | ||
| 603 | else { | ||
| 604 | 1 | mask = 0xffffffff; | |
| 605 | 1 | mask64 = (1 << bits_per_pixel) - 1; | |
| 606 | 1 | mask64 |= (mask64 << 48) | (mask64 << 32) | (mask64 << 16); // works for 10 bit, too | |
| 607 | // RGB24/48 is special case no use of this mask | ||
| 608 | } | ||
| 609 | 1 | } | |
| 610 | |||
| 611 | |||
| 612 | //mod4 width is required | ||
| 613 | ✗ | static void invert_frame_inplace_c(BYTE* frame, int pitch, int width, int height, int mask) { | |
| 614 | ✗ | for (int y = 0; y < height; ++y) { | |
| 615 | ✗ | int* intptr = reinterpret_cast<int*>(frame); | |
| 616 | |||
| 617 | ✗ | for (int x = 0; x < width / 4; ++x) { | |
| 618 | ✗ | intptr[x] = intptr[x] ^ mask; | |
| 619 | } | ||
| 620 | ✗ | frame += pitch; | |
| 621 | } | ||
| 622 | ✗ | } | |
| 623 | |||
| 624 | ✗ | static void invert_frame_uint16_inplace_c(BYTE* frame, int pitch, int width, int height, uint64_t mask64) { | |
| 625 | ✗ | for (int y = 0; y < height; ++y) { | |
| 626 | ✗ | for (int x = 0; x < width / 8; ++x) { | |
| 627 | ✗ | reinterpret_cast<uint64_t*>(frame)[x] = reinterpret_cast<uint64_t*>(frame)[x] ^ mask64; | |
| 628 | } | ||
| 629 | ✗ | frame += pitch; | |
| 630 | } | ||
| 631 | ✗ | } | |
| 632 | |||
| 633 | // called for uint8_t, uint16_t and float planar, chroma planes are inverted differently than luma plane | ||
| 634 | // R G B are treated the same way as luma. | ||
| 635 | // We assume full-range. | ||
| 636 | // 3.7.6 minor change: chroma: uint8_t, uint16_t: pivot around half and not xor FF/FFFF for chroma | ||
| 637 | // Note: this filter is so simple that it is optimized from C to same speed as SIMD in release | ||
| 638 | // Also, it is memory-bound AVX2 is not quicker than SSE2. | ||
| 639 | // lessthan16bits helps optimizing the exact 16 bit case. | ||
| 640 | // We use this very same C source for AVX2, where it is optimized even with 2x256 bit paths | ||
| 641 | template<typename pixel_t, bool lessthan16bits, bool chroma> | ||
| 642 | ✗ | static void invert_plane_c(uint8_t* dstp, const uint8_t* srcp, int src_pitch, int dst_pitch, int width, int height, int bits_per_pixel) { | |
| 643 | if constexpr (std::is_same_v<pixel_t, float>) { | ||
| 644 | if constexpr (chroma) { | ||
| 645 | // For chroma planes, invert around 0.0 -> negate | ||
| 646 | ✗ | for (int y = 0; y < height; ++y) { | |
| 647 | ✗ | for (int x = 0; x < width; ++x) { | |
| 648 | ✗ | reinterpret_cast<float*>(dstp)[x] = -reinterpret_cast<const float*>(srcp)[x]; | |
| 649 | } | ||
| 650 | ✗ | srcp += src_pitch; | |
| 651 | ✗ | dstp += dst_pitch; | |
| 652 | } | ||
| 653 | } | ||
| 654 | else { | ||
| 655 | // For luma plane, invert around 1.0 -> 1.0 - value | ||
| 656 | ✗ | for (int y = 0; y < height; ++y) { | |
| 657 | ✗ | for (int x = 0; x < width; ++x) { | |
| 658 | ✗ | reinterpret_cast<float*>(dstp)[x] = 1.0f - reinterpret_cast<const float*>(srcp)[x]; | |
| 659 | } | ||
| 660 | ✗ | srcp += src_pitch; | |
| 661 | ✗ | dstp += dst_pitch; | |
| 662 | } | ||
| 663 | } | ||
| 664 | ✗ | return; | |
| 665 | } | ||
| 666 | // 8 bit | ||
| 667 | if constexpr (std::is_same_v<pixel_t, uint8_t>) { | ||
| 668 | ✗ | constexpr int max_pixel_value = 255; | |
| 669 | if constexpr (chroma) { | ||
| 670 | ✗ | constexpr int half = 128; | |
| 671 | // For chroma planes, invert around 128 -> negate | ||
| 672 | ✗ | for (int y = 0; y < height; ++y) { | |
| 673 | ✗ | for (int x = 0; x < width; ++x) { | |
| 674 | ✗ | reinterpret_cast<uint8_t*>(dstp)[x] = std::min(2 * half - reinterpret_cast<const uint8_t*>(srcp)[x], max_pixel_value); | |
| 675 | // chroma invert: -(srcp[x] - half) + half | ||
| 676 | // = 2*half - srcp[x] = (1 << bits_per_pixel) - srcp[x] | ||
| 677 | // Watch for src==0, must top at max_pixel_value | ||
| 678 | } | ||
| 679 | ✗ | srcp += src_pitch; | |
| 680 | ✗ | dstp += dst_pitch; | |
| 681 | } | ||
| 682 | } | ||
| 683 | else { | ||
| 684 | // For luma plane, 255-x which is xor 0xff | ||
| 685 | ✗ | for (int y = 0; y < height; ++y) { | |
| 686 | ✗ | for (int x = 0; x < width; ++x) { | |
| 687 | ✗ | reinterpret_cast<uint8_t*>(dstp)[x] = max_pixel_value - reinterpret_cast<const uint8_t*>(srcp)[x]; | |
| 688 | } | ||
| 689 | ✗ | srcp += src_pitch; | |
| 690 | ✗ | dstp += dst_pitch; | |
| 691 | } | ||
| 692 | } | ||
| 693 | ✗ | return; | |
| 694 | } | ||
| 695 | // 10-16 bit uint16_t, luma: max_pixel_value - x which is xor with max_pixel_value, chroma: half - x | ||
| 696 | if constexpr (std::is_same_v<pixel_t, uint16_t>) { | ||
| 697 | if constexpr (!lessthan16bits) | ||
| 698 | ✗ | bits_per_pixel = 16; // quasi constexpr for optimization | |
| 699 | ✗ | const int max_pixel_value = (1 << bits_per_pixel) - 1; | |
| 700 | if constexpr (chroma) { | ||
| 701 | ✗ | const int half = 1 << (bits_per_pixel - 1); | |
| 702 | // For chroma planes, invert around mid-point (2^(bits-1)) | ||
| 703 | ✗ | for (int y = 0; y < height; ++y) { | |
| 704 | ✗ | for (int x = 0; x < width; ++x) { | |
| 705 | ✗ | reinterpret_cast<uint16_t*>(dstp)[x] = std::min(2 * half - reinterpret_cast<const uint16_t*>(srcp)[x], max_pixel_value); | |
| 706 | // chroma invert: -(srcp[x] - half) + half | ||
| 707 | // = 2*half - srcp[x] = (1 << bits_per_pixel) - srcp[x] | ||
| 708 | // Watch for src==0, must top at max_pixel_value | ||
| 709 | } | ||
| 710 | ✗ | srcp += src_pitch; | |
| 711 | ✗ | dstp += dst_pitch; | |
| 712 | } | ||
| 713 | } | ||
| 714 | else { | ||
| 715 | // For luma plane, max_pixel_value - x | ||
| 716 | ✗ | for (int y = 0; y < height; ++y) { | |
| 717 | ✗ | for (int x = 0; x < width; ++x) { | |
| 718 | ✗ | reinterpret_cast<uint16_t*>(dstp)[x] = max_pixel_value - reinterpret_cast<const uint16_t*>(srcp)[x]; | |
| 719 | } | ||
| 720 | ✗ | srcp += src_pitch; | |
| 721 | ✗ | dstp += dst_pitch; | |
| 722 | } | ||
| 723 | } | ||
| 724 | } | ||
| 725 | ✗ | } | |
| 726 | |||
| 727 | // YUY2 and packed RGB formats | ||
| 728 | ✗ | static void invert_frame_inplace(BYTE* frame, int pitch, int rowsize, int height, int mask, uint64_t mask64, int pixelsize, IScriptEnvironment* env) { | |
| 729 | #ifdef INTEL_INTRINSICS | ||
| 730 | ✗ | if ((pixelsize == 1 || pixelsize == 2) && (env->GetCPUFlags() & CPUF_AVX2)) | |
| 731 | { | ||
| 732 | ✗ | if (pixelsize == 1) | |
| 733 | ✗ | invert_frame_inplace_avx2(frame, pitch, rowsize, height, mask); | |
| 734 | else | ||
| 735 | ✗ | invert_frame_uint16_inplace_avx2(frame, pitch, rowsize, height, mask64); | |
| 736 | } | ||
| 737 | ✗ | else if ((pixelsize == 1 || pixelsize == 2) && (env->GetCPUFlags() & CPUF_SSE2)) | |
| 738 | { | ||
| 739 | ✗ | if (pixelsize == 1) | |
| 740 | ✗ | invert_frame_inplace_sse2(frame, pitch, rowsize, height, mask); | |
| 741 | else | ||
| 742 | ✗ | invert_frame_uint16_inplace_sse2(frame, pitch, rowsize, height, mask64); | |
| 743 | } | ||
| 744 | else | ||
| 745 | #endif | ||
| 746 | { | ||
| 747 | ✗ | if (pixelsize == 1) | |
| 748 | ✗ | invert_frame_inplace_c(frame, pitch, rowsize, height, mask); | |
| 749 | else | ||
| 750 | ✗ | invert_frame_uint16_inplace_c(frame, pitch, rowsize, height, mask64); | |
| 751 | } | ||
| 752 | ✗ | } | |
| 753 | |||
| 754 | // Function pointer type definition | ||
| 755 | using invert_plane_fn_t = void(*)(uint8_t*, const uint8_t*, int, int, int, int, int); | ||
| 756 | |||
| 757 | // width is in pixels, not bytes. | ||
| 758 | // AVX2: explicit SIMD kernels (invert_plane_avx2_u8/u16/f32). | ||
| 759 | // SSE2: explicit SIMD kernels (invert_plane_sse2_u8/u16/f32). | ||
| 760 | // Fallback: C templates (invert_plane_c). | ||
| 761 | 2 | static void invert_plane(uint8_t* dstp, const uint8_t* srcp, int src_pitch, int dst_pitch, int width, int height, int bits_per_pixel, bool chroma, IScriptEnvironment* env) { | |
| 762 |
1/4✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 2 times.
✗ Branch 3 → 4 not taken.
✗ Branch 3 → 5 not taken.
|
2 | const int pixelsize = bits_per_pixel == 8 ? 1 : bits_per_pixel <= 16 ? 2 : 4; // 8 bit = 1 byte, 10-16 bit = 2 bytes, float = 4 bytes |
| 763 | #ifdef INTEL_INTRINSICS | ||
| 764 | 2 | const bool avx2 = env->GetCPUFlags() & CPUF_AVX2; | |
| 765 | 2 | const bool sse2 = env->GetCPUFlags() & CPUF_SSE2; | |
| 766 | #endif | ||
| 767 | |||
| 768 | 2 | invert_plane_fn_t fn = nullptr; | |
| 769 | |||
| 770 |
1/4✓ Branch 9 → 10 taken 2 times.
✗ Branch 9 → 22 not taken.
✗ Branch 9 → 38 not taken.
✗ Branch 9 → 50 not taken.
|
2 | switch (pixelsize) { |
| 771 | 2 | case 1: | |
| 772 | #ifdef INTEL_INTRINSICS | ||
| 773 |
1/2✓ Branch 10 → 11 taken 2 times.
✗ Branch 10 → 14 not taken.
|
2 | if (avx2) |
| 774 |
2/2✓ Branch 11 → 12 taken 1 time.
✓ Branch 11 → 13 taken 1 time.
|
2 | fn = chroma ? invert_plane_avx2_u8<true> : invert_plane_avx2_u8<false>; |
| 775 | ✗ | else if (sse2) | |
| 776 | ✗ | fn = chroma ? invert_plane_sse2_u8<true> : invert_plane_sse2_u8<false>; | |
| 777 | else | ||
| 778 | #endif | ||
| 779 | ✗ | fn = chroma ? invert_plane_c<uint8_t, true /*lt16b* n/a */, true> : invert_plane_c<uint8_t, true /*lt16b* n/a */, false>; | |
| 780 | 2 | break; | |
| 781 | ✗ | case 2: | |
| 782 | #ifdef INTEL_INTRINSICS | ||
| 783 | ✗ | if (avx2) | |
| 784 | ✗ | fn = chroma ? invert_plane_avx2_u16<true> : invert_plane_avx2_u16<false>; | |
| 785 | ✗ | else if (sse2) | |
| 786 | ✗ | fn = chroma ? invert_plane_sse2_u16<true> : invert_plane_sse2_u16<false>; | |
| 787 | else | ||
| 788 | #endif | ||
| 789 | ✗ | if (bits_per_pixel < 16) | |
| 790 | ✗ | fn = chroma ? invert_plane_c<uint16_t, true /*lt16b**/, true> : invert_plane_c<uint16_t, true /*lt16b**/, false>; | |
| 791 | else | ||
| 792 | ✗ | fn = chroma ? invert_plane_c<uint16_t, false /*lt16b**/, true> : invert_plane_c<uint16_t, false /*lt16b**/, false>; | |
| 793 | ✗ | break; | |
| 794 | ✗ | case 4: | |
| 795 | #ifdef INTEL_INTRINSICS | ||
| 796 | ✗ | if (avx2) | |
| 797 | ✗ | fn = chroma ? invert_plane_avx2_f32<true> : invert_plane_avx2_f32<false>; | |
| 798 | ✗ | else if (sse2) | |
| 799 | ✗ | fn = chroma ? invert_plane_sse2_f32<true> : invert_plane_sse2_f32<false>; | |
| 800 | else | ||
| 801 | #endif | ||
| 802 | ✗ | fn = chroma ? invert_plane_c<float, false /*lt16b* n/a */, true> : invert_plane_c<float, false /*lt16b* n/a */, false>; | |
| 803 | ✗ | break; | |
| 804 | } | ||
| 805 | |||
| 806 | 2 | fn(dstp, srcp, src_pitch, dst_pitch, width, height, bits_per_pixel); | |
| 807 | 2 | } | |
| 808 | |||
| 809 | 1 | PVideoFrame Invert::GetFrame(int n, IScriptEnvironment* env) | |
| 810 | { | ||
| 811 |
1/2✓ Branch 3 → 4 taken 1 time.
✗ Branch 3 → 102 not taken.
|
1 | PVideoFrame src = child->GetFrame(n, env); |
| 812 | |||
| 813 |
2/4✓ Branch 4 → 5 taken 1 time.
✗ Branch 4 → 100 not taken.
✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 38 not taken.
|
1 | if (vi.IsPlanar()) { |
| 814 | |||
| 815 | // We do not use worst case MakeWritable, do it per plane. | ||
| 816 | // Read and Invert only those planes which are needed, BitBlt the rest. | ||
| 817 | |||
| 818 |
1/4✓ Branch 6 → 7 taken 1 time.
✗ Branch 6 → 99 not taken.
✗ Branch 96 → 97 not taken.
✗ Branch 96 → 98 not taken.
|
1 | PVideoFrame dst = env->NewVideoFrameP(vi, &src); |
| 819 | |||
| 820 | // Helper function to process or copy a plane | ||
| 821 | 3 | auto process_plane = [&](int plane, bool do_process, bool is_chroma) { | |
| 822 |
2/2✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 16 taken 1 time.
|
3 | if (do_process) { |
| 823 | 4 | invert_plane( | |
| 824 | dst->GetWritePtr(plane), | ||
| 825 | src->GetReadPtr(plane), | ||
| 826 | src->GetPitch(plane), | ||
| 827 | dst->GetPitch(plane), | ||
| 828 | 2 | dst->GetRowSize(plane) / pixelsize, | |
| 829 | dst->GetHeight(plane), | ||
| 830 | bits_per_pixel, | ||
| 831 | is_chroma, | ||
| 832 | env | ||
| 833 | ); | ||
| 834 | } | ||
| 835 | else { | ||
| 836 | 1 | env->BitBlt( | |
| 837 | dst->GetWritePtr(plane), | ||
| 838 | dst->GetPitch(plane), | ||
| 839 | src->GetReadPtr(plane), | ||
| 840 | src->GetPitch(plane), | ||
| 841 | src->GetRowSize(plane | PLANAR_ALIGNED), // Use aligned row size for BitBlt optimization | ||
| 842 | src->GetHeight(plane) | ||
| 843 | ); | ||
| 844 | } | ||
| 845 | 4 | }; | |
| 846 | |||
| 847 | // YUV/YUVA | ||
| 848 |
3/10✓ Branch 7 → 8 taken 1 time.
✗ Branch 7 → 96 not taken.
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 11 taken 1 time.
✗ Branch 9 → 10 not taken.
✗ Branch 9 → 96 not taken.
✗ Branch 10 → 11 not taken.
✗ Branch 10 → 12 not taken.
✓ Branch 13 → 14 taken 1 time.
✗ Branch 13 → 17 not taken.
|
1 | if (vi.IsYUV() || vi.IsYUVA()) { |
| 849 |
1/2✓ Branch 14 → 15 taken 1 time.
✗ Branch 14 → 96 not taken.
|
1 | process_plane(PLANAR_Y, doY, false); |
| 850 |
1/2✓ Branch 15 → 16 taken 1 time.
✗ Branch 15 → 96 not taken.
|
1 | process_plane(PLANAR_U, doU, true); |
| 851 |
1/2✓ Branch 16 → 17 taken 1 time.
✗ Branch 16 → 96 not taken.
|
1 | process_plane(PLANAR_V, doV, true); |
| 852 | } | ||
| 853 | |||
| 854 | // Planar RGB/RGBA | ||
| 855 |
5/10✓ Branch 17 → 18 taken 1 time.
✗ Branch 17 → 96 not taken.
✓ Branch 18 → 19 taken 1 time.
✗ Branch 18 → 21 not taken.
✓ Branch 19 → 20 taken 1 time.
✗ Branch 19 → 96 not taken.
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 22 taken 1 time.
✗ Branch 23 → 24 not taken.
✓ Branch 23 → 27 taken 1 time.
|
1 | if (vi.IsPlanarRGB() || vi.IsPlanarRGBA()) { |
| 856 | ✗ | process_plane(PLANAR_G, doG, false); | |
| 857 | ✗ | process_plane(PLANAR_B, doB, false); | |
| 858 | ✗ | process_plane(PLANAR_R, doR, false); | |
| 859 | } | ||
| 860 | |||
| 861 | // Alpha channel | ||
| 862 |
5/10✓ Branch 27 → 28 taken 1 time.
✗ Branch 27 → 96 not taken.
✓ Branch 28 → 29 taken 1 time.
✗ Branch 28 → 31 not taken.
✓ Branch 29 → 30 taken 1 time.
✗ Branch 29 → 96 not taken.
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 32 taken 1 time.
✗ Branch 33 → 34 not taken.
✓ Branch 33 → 35 taken 1 time.
|
1 | if (vi.IsPlanarRGBA() || vi.IsYUVA()) { |
| 863 | ✗ | process_plane(PLANAR_A, doA, false); | |
| 864 | } | ||
| 865 | |||
| 866 |
1/2✗ Branch 35 → 36 not taken.
✓ Branch 35 → 37 taken 1 time.
|
1 | return dst; |
| 867 | ✗ | } | |
| 868 | |||
| 869 | // packed formats, we do a full copy of the frame before inverting | ||
| 870 | ✗ | env->MakeWritable(&src); | |
| 871 | |||
| 872 | ✗ | BYTE* pf = src->GetWritePtr(); | |
| 873 | ✗ | int pitch = src->GetPitch(); | |
| 874 | ✗ | int rowsize = src->GetRowSize(); | |
| 875 | ✗ | int height = src->GetHeight(); | |
| 876 | |||
| 877 | ✗ | if (vi.IsYUY2() || vi.IsRGB32() || vi.IsRGB64()) { | |
| 878 | // packed pixels, 4x1 or 4x2 bytes, all can be treated the same way with a mask | ||
| 879 | // YUY2 is simply xored even in its chroma component. Not really correct, but YUY2 is a compability format. | ||
| 880 | // we won't convert it to and from YV16, keep its pre-3.7.6 behavior. | ||
| 881 | ✗ | invert_frame_inplace(pf, pitch, rowsize, height, mask, mask64, pixelsize, env); | |
| 882 | } | ||
| 883 | ✗ | else if (vi.IsRGB24()) { | |
| 884 | ✗ | int rMask = doR ? 0xff : 0; | |
| 885 | ✗ | int gMask = doG ? 0xff : 0; | |
| 886 | ✗ | int bMask = doB ? 0xff : 0; | |
| 887 | ✗ | for (int i = 0; i < height; i++) { | |
| 888 | |||
| 889 | ✗ | for (int j = 0; j < rowsize; j += 3) { | |
| 890 | ✗ | pf[j + 0] = pf[j + 0] ^ bMask; | |
| 891 | ✗ | pf[j + 1] = pf[j + 1] ^ gMask; | |
| 892 | ✗ | pf[j + 2] = pf[j + 2] ^ rMask; | |
| 893 | } | ||
| 894 | ✗ | pf += pitch; | |
| 895 | } | ||
| 896 | } | ||
| 897 | ✗ | else if (vi.IsRGB48()) { | |
| 898 | ✗ | int rMask = doR ? 0xffff : 0; | |
| 899 | ✗ | int gMask = doG ? 0xffff : 0; | |
| 900 | ✗ | int bMask = doB ? 0xffff : 0; | |
| 901 | ✗ | for (int i = 0; i < height; i++) { | |
| 902 | ✗ | for (int j = 0; j < rowsize / pixelsize; j += 3) { | |
| 903 | ✗ | reinterpret_cast<uint16_t*>(pf)[j + 0] ^= bMask; | |
| 904 | ✗ | reinterpret_cast<uint16_t*>(pf)[j + 1] ^= gMask; | |
| 905 | ✗ | reinterpret_cast<uint16_t*>(pf)[j + 2] ^= rMask; | |
| 906 | } | ||
| 907 | ✗ | pf += pitch; | |
| 908 | } | ||
| 909 | } | ||
| 910 | |||
| 911 | ✗ | return src; | |
| 912 | 1 | } | |
| 913 | |||
| 914 | |||
| 915 | ✗ | AVSValue Invert::Create(AVSValue args, void*, IScriptEnvironment* env) | |
| 916 | { | ||
| 917 | ✗ | return new Invert(args[0].AsClip(), args[0].AsClip()->GetVideoInfo().IsRGB() ? args[1].AsString("RGBA") : args[1].AsString("YUVA"), env); | |
| 918 | } | ||
| 919 | |||
| 920 | |||
| 921 | /********************************** | ||
| 922 | ****** ShowChannel filter ****** | ||
| 923 | **********************************/ | ||
| 924 | |||
| 925 | |||
| 926 | 3 | ShowChannel::ShowChannel(PClip _child, const char* pixel_type, int _channel, IScriptEnvironment* env) | |
| 927 |
1/2✓ Branch 6 → 7 taken 3 times.
✗ Branch 6 → 237 not taken.
|
3 | : GenericVideoFilter(_child), channel(_channel), input_type(_child->GetVideoInfo().pixel_type), |
| 928 |
6/12✓ Branch 2 → 3 taken 3 times.
✗ Branch 2 → 213 not taken.
✓ Branch 3 → 4 taken 3 times.
✗ Branch 3 → 211 not taken.
✓ Branch 8 → 9 taken 3 times.
✗ Branch 8 → 237 not taken.
✓ Branch 9 → 10 taken 3 times.
✗ Branch 9 → 237 not taken.
✓ Branch 11 → 12 taken 3 times.
✗ Branch 11 → 237 not taken.
✓ Branch 12 → 13 taken 3 times.
✗ Branch 12 → 237 not taken.
|
6 | pixelsize(_child->GetVideoInfo().ComponentSize()), bits_per_pixel(_child->GetVideoInfo().BitsPerComponent()) |
| 929 | { | ||
| 930 | static const char* const ShowText[7] = { "Blue", "Green", "Red", "Alpha", "Y", "U", "V" }; | ||
| 931 | |||
| 932 |
5/8✓ Branch 13 → 14 taken 3 times.
✗ Branch 13 → 237 not taken.
✓ Branch 14 → 15 taken 1 time.
✓ Branch 14 → 18 taken 2 times.
✓ Branch 15 → 16 taken 1 time.
✗ Branch 15 → 237 not taken.
✓ Branch 16 → 17 taken 1 time.
✗ Branch 16 → 18 not taken.
|
3 | input_type_is_packed_rgb = vi.IsRGB() && !vi.IsPlanar(); |
| 933 |
1/2✓ Branch 19 → 20 taken 3 times.
✗ Branch 19 → 237 not taken.
|
3 | input_type_is_planar_rgb = vi.IsPlanarRGB(); |
| 934 |
1/2✓ Branch 20 → 21 taken 3 times.
✗ Branch 20 → 237 not taken.
|
3 | input_type_is_planar_rgba = vi.IsPlanarRGBA(); |
| 935 |
1/2✓ Branch 21 → 22 taken 3 times.
✗ Branch 21 → 237 not taken.
|
3 | input_type_is_yuva = vi.IsYUVA(); |
| 936 |
5/8✓ Branch 22 → 23 taken 3 times.
✗ Branch 22 → 237 not taken.
✓ Branch 23 → 24 taken 2 times.
✓ Branch 23 → 27 taken 1 time.
✓ Branch 24 → 25 taken 2 times.
✗ Branch 24 → 237 not taken.
✓ Branch 25 → 26 taken 2 times.
✗ Branch 25 → 27 not taken.
|
3 | input_type_is_yuv = vi.IsYUV() && vi.IsPlanar(); |
| 937 |
1/2✓ Branch 28 → 29 taken 3 times.
✗ Branch 28 → 237 not taken.
|
3 | input_type_is_planar = vi.IsPlanar(); |
| 938 | |||
| 939 | 3 | int orig_channel = channel; | |
| 940 | |||
| 941 | // A channel | ||
| 942 |
2/20✗ Branch 29 → 30 not taken.
✓ Branch 29 → 39 taken 3 times.
✗ Branch 30 → 31 not taken.
✗ Branch 30 → 237 not taken.
✗ Branch 31 → 32 not taken.
✗ Branch 31 → 39 not taken.
✗ Branch 32 → 33 not taken.
✗ Branch 32 → 237 not taken.
✗ Branch 33 → 34 not taken.
✗ Branch 33 → 39 not taken.
✗ Branch 34 → 35 not taken.
✗ Branch 34 → 237 not taken.
✗ Branch 35 → 36 not taken.
✗ Branch 35 → 39 not taken.
✗ Branch 36 → 37 not taken.
✗ Branch 36 → 237 not taken.
✗ Branch 37 → 38 not taken.
✗ Branch 37 → 39 not taken.
✗ Branch 40 → 41 not taken.
✓ Branch 40 → 42 taken 3 times.
|
3 | if ((channel == 3) && !vi.IsRGB32() && !vi.IsRGB64() && !vi.IsPlanarRGBA() && !vi.IsYUVA()) |
| 943 | ✗ | env->ThrowError("ShowAlpha: RGB32, RGB64, Planar RGBA or YUVA data only"); | |
| 944 | |||
| 945 | // R, G, B channel | ||
| 946 |
8/10✓ Branch 42 → 43 taken 3 times.
✗ Branch 42 → 47 not taken.
✓ Branch 43 → 44 taken 2 times.
✓ Branch 43 → 47 taken 1 time.
✓ Branch 44 → 45 taken 2 times.
✗ Branch 44 → 237 not taken.
✓ Branch 45 → 46 taken 1 time.
✓ Branch 45 → 47 taken 1 time.
✓ Branch 48 → 49 taken 1 time.
✓ Branch 48 → 50 taken 2 times.
|
3 | if ((channel >= 0) && (channel <= 2) && !vi.IsRGB()) |
| 947 |
1/2✗ Branch 49 → 50 not taken.
✓ Branch 49 → 237 taken 1 time.
|
1 | env->ThrowError("Show%s: plane is valid only with RGB or planar RGB(A) source", ShowText[channel]); |
| 948 | |||
| 949 | // Y, U, V channel (4,5,6) | ||
| 950 |
3/4✓ Branch 50 → 51 taken 1 time.
✓ Branch 50 → 68 taken 1 time.
✓ Branch 51 → 52 taken 1 time.
✗ Branch 51 → 68 not taken.
|
2 | if ((channel >= 4) && (channel <= 6)) { |
| 951 |
3/10✓ Branch 52 → 53 taken 1 time.
✗ Branch 52 → 237 not taken.
✗ Branch 53 → 54 not taken.
✓ Branch 53 → 57 taken 1 time.
✗ Branch 54 → 55 not taken.
✗ Branch 54 → 237 not taken.
✗ Branch 55 → 56 not taken.
✗ Branch 55 → 57 not taken.
✗ Branch 58 → 59 not taken.
✓ Branch 58 → 60 taken 1 time.
|
1 | if (!vi.IsYUV() && !vi.IsYUVA()) |
| 952 | ✗ | env->ThrowError("Show%s: plane is valid only with YUV(A) source", ShowText[channel]); | |
| 953 |
2/8✗ Branch 60 → 61 not taken.
✓ Branch 60 → 64 taken 1 time.
✗ Branch 61 → 62 not taken.
✗ Branch 61 → 237 not taken.
✗ Branch 62 → 63 not taken.
✗ Branch 62 → 64 not taken.
✗ Branch 65 → 66 not taken.
✓ Branch 65 → 67 taken 1 time.
|
1 | if (channel != 4 && vi.IsY()) |
| 954 | ✗ | env->ThrowError("Show%s: invalid plane for greyscale source", ShowText[channel]); | |
| 955 | 1 | channel -= 4; // map to 0,1,2 | |
| 956 | } | ||
| 957 | |||
| 958 | int target_bits_per_pixel; | ||
| 959 | |||
| 960 | 2 | const int orig_width = vi.width; | |
| 961 | 2 | const int orig_height = vi.height; | |
| 962 | |||
| 963 |
3/4✓ Branch 68 → 69 taken 1 time.
✓ Branch 68 → 70 taken 1 time.
✗ Branch 69 → 70 not taken.
✓ Branch 69 → 75 taken 1 time.
|
2 | if (input_type_is_yuv || input_type_is_yuva) |
| 964 | { | ||
| 965 |
2/4✓ Branch 70 → 71 taken 1 time.
✗ Branch 70 → 72 not taken.
✗ Branch 71 → 72 not taken.
✓ Branch 71 → 75 taken 1 time.
|
1 | if (channel == 1 || channel == 2) // U or V: target can be smaller than Y |
| 966 | { | ||
| 967 | ✗ | vi.width >>= vi.GetPlaneWidthSubsampling(PLANAR_U); | |
| 968 | ✗ | vi.height >>= vi.GetPlaneHeightSubsampling(PLANAR_U); | |
| 969 | } | ||
| 970 | } | ||
| 971 | |||
| 972 |
2/4✓ Branch 75 → 76 taken 2 times.
✗ Branch 75 → 77 not taken.
✗ Branch 76 → 77 not taken.
✓ Branch 76 → 78 taken 2 times.
|
2 | const bool empty_pixel_type = pixel_type == nullptr || *pixel_type == 0; |
| 973 | |||
| 974 |
6/10✓ Branch 79 → 80 taken 2 times.
✗ Branch 79 → 83 not taken.
✓ Branch 80 → 81 taken 2 times.
✗ Branch 80 → 237 not taken.
✓ Branch 81 → 82 taken 1 time.
✓ Branch 81 → 84 taken 1 time.
✗ Branch 82 → 83 not taken.
✓ Branch 82 → 84 taken 1 time.
✗ Branch 85 → 86 not taken.
✓ Branch 85 → 105 taken 2 times.
|
2 | if (!lstrcmpi(pixel_type, "rgb") || (vi.IsRGB() && empty_pixel_type)) { |
| 975 | // target is RGB, rgb (packed) is adaptively 32 or 64 bits | ||
| 976 | // rgb (planar) is of any bit depths | ||
| 977 | ✗ | if (vi.IsPlanar()) { | |
| 978 | // YUV, planar RGB or Y | ||
| 979 | // always alphaless planar RGB output | ||
| 980 | ✗ | switch (bits_per_pixel) { | |
| 981 | ✗ | case 8: vi.pixel_type = VideoInfo::CS_RGBP8; break; | |
| 982 | ✗ | case 10: vi.pixel_type = VideoInfo::CS_RGBP10; break; | |
| 983 | ✗ | case 12: vi.pixel_type = VideoInfo::CS_RGBP12; break; | |
| 984 | ✗ | case 14: vi.pixel_type = VideoInfo::CS_RGBP14; break; | |
| 985 | ✗ | case 16: vi.pixel_type = VideoInfo::CS_RGBP16; break; | |
| 986 | ✗ | case 32: vi.pixel_type = VideoInfo::CS_RGBPS; break; | |
| 987 | } | ||
| 988 | } | ||
| 989 | ✗ | else if (vi.IsRGB()) { | |
| 990 | // packed RGB source | ||
| 991 | ✗ | switch (bits_per_pixel) { | |
| 992 | ✗ | case 8: vi.pixel_type = VideoInfo::CS_BGR32; break; // bit-depth adaptive | |
| 993 | ✗ | case 16: vi.pixel_type = VideoInfo::CS_BGR64; break; | |
| 994 | ✗ | default: env->ThrowError("Show%s: source must be 8 or 16 bits", ShowText[orig_channel]); | |
| 995 | } | ||
| 996 | } | ||
| 997 | else { | ||
| 998 | ✗ | env->ThrowError("Show%s: unsupported source format", ShowText[orig_channel]); | |
| 999 | } | ||
| 1000 | ✗ | target_bits_per_pixel = bits_per_pixel; | |
| 1001 | } | ||
| 1002 |
8/14✓ Branch 105 → 106 taken 2 times.
✗ Branch 105 → 111 not taken.
✓ Branch 106 → 107 taken 2 times.
✗ Branch 106 → 237 not taken.
✓ Branch 107 → 108 taken 1 time.
✓ Branch 107 → 110 taken 1 time.
✓ Branch 108 → 109 taken 1 time.
✗ Branch 108 → 237 not taken.
✗ Branch 109 → 110 not taken.
✓ Branch 109 → 112 taken 1 time.
✗ Branch 110 → 111 not taken.
✓ Branch 110 → 112 taken 1 time.
✗ Branch 113 → 114 not taken.
✓ Branch 113 → 122 taken 2 times.
|
2 | else if (!lstrcmpi(pixel_type, "yuv") || ((vi.IsYUV() || vi.IsYUVA()) && empty_pixel_type)) { |
| 1003 | // target is YUV, rgb (packed) is adaptively 32 or 64 bits | ||
| 1004 | // rgb (planar) is of any bit depths | ||
| 1005 | // YUV,Y: 420 | ||
| 1006 | // RGB source, when only 'yuv' is given, convert to 444 | ||
| 1007 | ✗ | switch (bits_per_pixel) { | |
| 1008 | ✗ | case 8: vi.pixel_type = VideoInfo::CS_YV24; break; | |
| 1009 | ✗ | case 10: vi.pixel_type = VideoInfo::CS_YUV444P10; break; | |
| 1010 | ✗ | case 12: vi.pixel_type = VideoInfo::CS_YUV444P12; break; | |
| 1011 | ✗ | case 14: vi.pixel_type = VideoInfo::CS_YUV444P14; break; | |
| 1012 | ✗ | case 16: vi.pixel_type = VideoInfo::CS_YUV444P16; break; | |
| 1013 | ✗ | case 32: vi.pixel_type = VideoInfo::CS_YUV444PS; break; | |
| 1014 | } | ||
| 1015 | ✗ | target_bits_per_pixel = bits_per_pixel; | |
| 1016 | } | ||
| 1017 | else { | ||
| 1018 | // explicitely given output pixel type | ||
| 1019 | |||
| 1020 | // first try | ||
| 1021 | // Append bit depth and check | ||
| 1022 |
1/2✓ Branch 124 → 125 taken 2 times.
✗ Branch 124 → 214 not taken.
|
2 | std::string format = pixel_type; |
| 1023 | // RGBP --> RGBPS, Y -> Y16, YUV420 -> YUV420P10 | ||
| 1024 |
1/2✗ Branch 126 → 127 not taken.
✓ Branch 126 → 133 taken 2 times.
|
2 | if (!lstrcmpi(pixel_type, "y")) { |
| 1025 | ✗ | format = format + std::to_string(bits_per_pixel); // Y8..Y16, also Y32 | |
| 1026 | } | ||
| 1027 |
2/4✓ Branch 133 → 134 taken 2 times.
✗ Branch 133 → 135 not taken.
✗ Branch 134 → 135 not taken.
✓ Branch 134 → 147 taken 2 times.
|
2 | else if (!lstrcmpi(pixel_type, "rgbp") || !lstrcmpi(pixel_type, "rgbap")) { |
| 1028 | ✗ | if (bits_per_pixel == 32) | |
| 1029 | ✗ | format = format + "S"; // RGBAPS | |
| 1030 | else | ||
| 1031 | ✗ | format = format + std::to_string(bits_per_pixel); // RGBP16 | |
| 1032 | } | ||
| 1033 | else { | ||
| 1034 | // hopefully like "yuv420" or "yuva444" | ||
| 1035 |
1/2✗ Branch 147 → 148 not taken.
✓ Branch 147 → 152 taken 2 times.
|
2 | if (bits_per_pixel == 32) |
| 1036 | ✗ | format = format + "PS"; // YUV420PS | |
| 1037 | else | ||
| 1038 |
2/4✓ Branch 153 → 154 taken 2 times.
✗ Branch 153 → 229 not taken.
✓ Branch 154 → 155 taken 2 times.
✗ Branch 154 → 227 not taken.
|
2 | format = format + "P" + std::to_string(bits_per_pixel); // YUV420P16 |
| 1039 | } | ||
| 1040 | |||
| 1041 |
1/2✓ Branch 161 → 162 taken 2 times.
✗ Branch 161 → 234 not taken.
|
2 | int new_pixel_type = GetPixelTypeFromName(format.c_str()); |
| 1042 |
1/2✗ Branch 162 → 163 not taken.
✓ Branch 162 → 166 taken 2 times.
|
2 | if (new_pixel_type == VideoInfo::CS_UNKNOWN) { |
| 1043 | ✗ | new_pixel_type = GetPixelTypeFromName(pixel_type); | |
| 1044 | ✗ | if (new_pixel_type == VideoInfo::CS_UNKNOWN) | |
| 1045 | ✗ | env->ThrowError("Show%s: invalid pixel_type!", ShowText[orig_channel]); | |
| 1046 | } | ||
| 1047 | // new output format | ||
| 1048 | 2 | vi.pixel_type = new_pixel_type; | |
| 1049 | |||
| 1050 |
2/4✓ Branch 166 → 167 taken 2 times.
✗ Branch 166 → 234 not taken.
✗ Branch 167 → 168 not taken.
✓ Branch 167 → 170 taken 2 times.
|
2 | if (vi.IsYUY2()) { |
| 1051 | ✗ | if (vi.width & 1) { | |
| 1052 | ✗ | env->ThrowError("Show%s: width must be mod 2 for yuy2", ShowText[orig_channel]); | |
| 1053 | } | ||
| 1054 | } | ||
| 1055 |
3/4✓ Branch 170 → 171 taken 2 times.
✗ Branch 170 → 234 not taken.
✓ Branch 171 → 172 taken 1 time.
✓ Branch 171 → 176 taken 1 time.
|
2 | if (vi.Is420()) { |
| 1056 |
1/2✓ Branch 172 → 173 taken 1 time.
✗ Branch 172 → 174 not taken.
|
1 | if (vi.width & 1) { |
| 1057 |
1/2✗ Branch 173 → 174 not taken.
✓ Branch 173 → 234 taken 1 time.
|
1 | env->ThrowError("Show%s: width must be mod 2 for 4:2:0 target", ShowText[orig_channel]); |
| 1058 | } | ||
| 1059 | ✗ | if (vi.height & 1) { | |
| 1060 | ✗ | env->ThrowError("Show%s: height must be mod 2 for 4:2:0 target", ShowText[orig_channel]); | |
| 1061 | } | ||
| 1062 | } | ||
| 1063 |
2/4✓ Branch 176 → 177 taken 1 time.
✗ Branch 176 → 234 not taken.
✗ Branch 177 → 178 not taken.
✓ Branch 177 → 180 taken 1 time.
|
1 | if (vi.Is422()) { |
| 1064 | ✗ | if (vi.width & 1) { | |
| 1065 | ✗ | env->ThrowError("Show%s: width must be mod 2 for 4:2:2 target", ShowText[orig_channel]); | |
| 1066 | } | ||
| 1067 | } | ||
| 1068 |
2/4✓ Branch 180 → 181 taken 1 time.
✗ Branch 180 → 234 not taken.
✗ Branch 181 → 182 not taken.
✓ Branch 181 → 184 taken 1 time.
|
1 | if (vi.IsYV411()) { |
| 1069 | ✗ | if (vi.width & 3) { | |
| 1070 | ✗ | env->ThrowError("Show%s: width must be mod 4 for 4:1:1 target", ShowText[orig_channel]); | |
| 1071 | } | ||
| 1072 | } | ||
| 1073 | |||
| 1074 |
1/2✓ Branch 184 → 185 taken 1 time.
✗ Branch 184 → 234 not taken.
|
1 | target_bits_per_pixel = vi.BitsPerComponent(); |
| 1075 | 2 | } | |
| 1076 | |||
| 1077 |
1/2✗ Branch 187 → 188 not taken.
✓ Branch 187 → 189 taken 1 time.
|
1 | if (target_bits_per_pixel != bits_per_pixel) |
| 1078 | ✗ | env->ThrowError("Show%s: source bit depth must be %d for %s", ShowText[orig_channel], target_bits_per_pixel, pixel_type); | |
| 1079 | |||
| 1080 |
8/16✓ Branch 189 → 190 taken 1 time.
✗ Branch 189 → 237 not taken.
✓ Branch 190 → 191 taken 1 time.
✗ Branch 190 → 197 not taken.
✓ Branch 191 → 192 taken 1 time.
✗ Branch 191 → 237 not taken.
✓ Branch 192 → 193 taken 1 time.
✗ Branch 192 → 197 not taken.
✓ Branch 193 → 194 taken 1 time.
✗ Branch 193 → 237 not taken.
✓ Branch 194 → 195 taken 1 time.
✗ Branch 194 → 197 not taken.
✓ Branch 195 → 196 taken 1 time.
✗ Branch 195 → 237 not taken.
✓ Branch 196 → 197 taken 1 time.
✗ Branch 196 → 198 not taken.
|
1 | target_hasalpha = vi.IsRGB32() || vi.IsRGB64() || vi.IsPlanarRGBA() || vi.IsYUVA(); |
| 1081 |
1/8✗ Branch 199 → 200 not taken.
✓ Branch 199 → 203 taken 1 time.
✗ Branch 200 → 201 not taken.
✗ Branch 200 → 203 not taken.
✗ Branch 201 → 202 not taken.
✗ Branch 201 → 203 not taken.
✗ Branch 202 → 203 not taken.
✗ Branch 202 → 204 not taken.
|
1 | source_hasalpha = input_type == VideoInfo::CS_BGR32 || input_type == VideoInfo::CS_BGR64 || input_type_is_planar_rgba || input_type_is_yuva; |
| 1082 | |||
| 1083 |
4/8✓ Branch 205 → 206 taken 1 time.
✗ Branch 205 → 210 not taken.
✓ Branch 206 → 207 taken 1 time.
✗ Branch 206 → 210 not taken.
✓ Branch 207 → 208 taken 1 time.
✗ Branch 207 → 209 not taken.
✗ Branch 208 → 209 not taken.
✓ Branch 208 → 210 taken 1 time.
|
1 | if (target_hasalpha && source_hasalpha && (vi.width != orig_width || vi.height != orig_height)) { |
| 1084 | ✗ | env->ThrowError("Show%s: subsampled source plane and alpha-aware source and destination format: alpha dimensions must be the same", ShowText[orig_channel]); | |
| 1085 | } | ||
| 1086 | |||
| 1087 | 3 | } | |
| 1088 | |||
| 1089 | |||
| 1090 | template<typename pixel_t, bool source_hasalpha, bool target_hasalpha> | ||
| 1091 | ✗ | static void planar_to_packedrgb(uint8_t* dstp, int dst_pitch, const uint8_t* srcp, const uint8_t* srcp_a, int src_pitch, int width, int height) | |
| 1092 | { | ||
| 1093 | // packed RGB is upside-down | ||
| 1094 | ✗ | dstp += (height - 1) * dst_pitch; | |
| 1095 | ✗ | constexpr int target_rgb_step = target_hasalpha ? 4 : 3; | |
| 1096 | ✗ | constexpr int max_pixel_value = sizeof(pixel_t) == 1 ? 255 : 65535; | |
| 1097 | |||
| 1098 | ✗ | for (int i = 0; i < height; ++i) { | |
| 1099 | ✗ | for (int j = 0; j < width; j++) { | |
| 1100 | ✗ | pixel_t* curr_dstp = reinterpret_cast<pixel_t*>(dstp); | |
| 1101 | ✗ | curr_dstp[j * target_rgb_step + 0] = curr_dstp[j * target_rgb_step + 1] = curr_dstp[j * target_rgb_step + 2] = reinterpret_cast<const pixel_t*>(srcp)[j]; | |
| 1102 | if constexpr(target_hasalpha) { | ||
| 1103 | ✗ | const int alpha = source_hasalpha ? reinterpret_cast<const pixel_t*>(srcp_a)[j] : max_pixel_value; | |
| 1104 | ✗ | curr_dstp[j * target_rgb_step + 3] = alpha; | |
| 1105 | } | ||
| 1106 | } | ||
| 1107 | ✗ | srcp += src_pitch; | |
| 1108 | if constexpr(source_hasalpha) | ||
| 1109 | ✗ | srcp_a += src_pitch; // alpha has the same pitch | |
| 1110 | ✗ | dstp -= dst_pitch; | |
| 1111 | } | ||
| 1112 | ✗ | } | |
| 1113 | |||
| 1114 | template<typename pixel_t, bool source_hasalpha, bool target_hasalpha> | ||
| 1115 | ✗ | static void packed_to_packedrgb(uint8_t* dstp, int dst_pitch, const uint8_t* srcp, int pitch, int width, int height, int channel) | |
| 1116 | { | ||
| 1117 | ✗ | constexpr int target_rgb_step = target_hasalpha ? 4 : 3; | |
| 1118 | ✗ | constexpr int source_rgb_step = source_hasalpha ? 4 : 3; | |
| 1119 | ✗ | constexpr int max_pixel_value = sizeof(pixel_t) == 1 ? 255 : 65535; | |
| 1120 | |||
| 1121 | ✗ | for (int i = 0; i < height; ++i) { | |
| 1122 | ✗ | for (int j = 0; j < width; j++) { | |
| 1123 | ✗ | pixel_t* curr_dstp = reinterpret_cast<pixel_t*>(dstp); | |
| 1124 | ✗ | const int pixel = reinterpret_cast<const pixel_t*>(srcp)[j * source_rgb_step + channel]; | |
| 1125 | ✗ | curr_dstp[j * target_rgb_step + 0] = curr_dstp[j * target_rgb_step + 1] = curr_dstp[j * target_rgb_step + 2] = pixel; | |
| 1126 | if constexpr(target_hasalpha) { | ||
| 1127 | ✗ | const int alpha = source_hasalpha ? reinterpret_cast<const pixel_t*>(srcp)[j * source_rgb_step + 3] : max_pixel_value; | |
| 1128 | ✗ | curr_dstp[j * target_rgb_step + 3] = alpha; // alpha | |
| 1129 | } | ||
| 1130 | } | ||
| 1131 | ✗ | srcp += pitch; | |
| 1132 | ✗ | dstp += dst_pitch; | |
| 1133 | } | ||
| 1134 | ✗ | } | |
| 1135 | |||
| 1136 | template<typename pixel_t, bool source_hasalpha, bool target_hasalpha> | ||
| 1137 | ✗ | static void packed_to_planarrgb(uint8_t* dstp_r, uint8_t* dstp_g, uint8_t* dstp_b, uint8_t* dstp_a, int dst_pitch, const uint8_t* srcp, int src_pitch, int width, int height, int channel) | |
| 1138 | { | ||
| 1139 | ✗ | constexpr int source_rgb_step = source_hasalpha ? 4 : 3; | |
| 1140 | ✗ | constexpr int max_pixel_value = sizeof(pixel_t) == 1 ? 255 : 65535; | |
| 1141 | |||
| 1142 | // packed RGB is upside-down | ||
| 1143 | ✗ | srcp += (height - 1) * src_pitch; | |
| 1144 | |||
| 1145 | ✗ | for (int i = 0; i < height; ++i) { | |
| 1146 | ✗ | for (int j = 0; j < width; ++j) { | |
| 1147 | ✗ | const int pixel = reinterpret_cast<const pixel_t*>(srcp)[j * source_rgb_step + channel]; | |
| 1148 | ✗ | reinterpret_cast<pixel_t*>(dstp_g)[j] = | |
| 1149 | ✗ | reinterpret_cast<pixel_t*>(dstp_b)[j] = | |
| 1150 | ✗ | reinterpret_cast<pixel_t*>(dstp_r)[j] = pixel; | |
| 1151 | if constexpr (target_hasalpha) { | ||
| 1152 | ✗ | const int alpha = source_hasalpha ? reinterpret_cast<const pixel_t*>(srcp)[j * source_rgb_step + 3] : max_pixel_value; | |
| 1153 | ✗ | reinterpret_cast<pixel_t*>(dstp_a)[j] = alpha; | |
| 1154 | } | ||
| 1155 | } | ||
| 1156 | ✗ | srcp -= src_pitch; | |
| 1157 | ✗ | dstp_g += dst_pitch; | |
| 1158 | ✗ | dstp_b += dst_pitch; | |
| 1159 | ✗ | dstp_r += dst_pitch; | |
| 1160 | if constexpr (target_hasalpha) | ||
| 1161 | ✗ | dstp_a += dst_pitch; | |
| 1162 | } | ||
| 1163 | ✗ | } | |
| 1164 | |||
| 1165 | template<typename pixel_t, bool source_hasalpha, bool target_hasalpha> | ||
| 1166 | 2 | static void packed_to_luma_alpha(uint8_t* dstp_y, uint8_t* dstp_a, int dst_pitch, const uint8_t* srcp, int src_pitch, int width, int height, int channel) | |
| 1167 | { | ||
| 1168 | 2 | constexpr int source_rgb_step = source_hasalpha ? 4 : 3; | |
| 1169 | 2 | constexpr int max_pixel_value = sizeof(pixel_t) == 1 ? 255 : 65535; | |
| 1170 | |||
| 1171 | // packed RGB is upside-down | ||
| 1172 | 2 | srcp += (height - 1) * src_pitch; | |
| 1173 | |||
| 1174 |
2/16void packed_to_luma_alpha<unsigned char, false, false>(unsigned char*, unsigned char*, int, unsigned char const*, int, int, int, int):
✗ Branch 7 → 3 not taken.
✗ Branch 7 → 8 not taken.
void packed_to_luma_alpha<unsigned char, false, true>(unsigned char*, unsigned char*, int, unsigned char const*, int, int, int, int):
✗ Branch 7 → 3 not taken.
✗ Branch 7 → 8 not taken.
void packed_to_luma_alpha<unsigned char, true, false>(unsigned char*, unsigned char*, int, unsigned char const*, int, int, int, int):
✗ Branch 7 → 3 not taken.
✗ Branch 7 → 8 not taken.
void packed_to_luma_alpha<unsigned char, true, true>(unsigned char*, unsigned char*, int, unsigned char const*, int, int, int, int):
✓ Branch 7 → 3 taken 6 times.
✓ Branch 7 → 8 taken 2 times.
void packed_to_luma_alpha<unsigned short, false, false>(unsigned char*, unsigned char*, int, unsigned char const*, int, int, int, int):
✗ Branch 7 → 3 not taken.
✗ Branch 7 → 8 not taken.
void packed_to_luma_alpha<unsigned short, false, true>(unsigned char*, unsigned char*, int, unsigned char const*, int, int, int, int):
✗ Branch 7 → 3 not taken.
✗ Branch 7 → 8 not taken.
void packed_to_luma_alpha<unsigned short, true, false>(unsigned char*, unsigned char*, int, unsigned char const*, int, int, int, int):
✗ Branch 7 → 3 not taken.
✗ Branch 7 → 8 not taken.
void packed_to_luma_alpha<unsigned short, true, true>(unsigned char*, unsigned char*, int, unsigned char const*, int, int, int, int):
✗ Branch 7 → 3 not taken.
✗ Branch 7 → 8 not taken.
|
8 | for (int i = 0; i < height; ++i) { |
| 1175 |
2/16void packed_to_luma_alpha<unsigned char, false, false>(unsigned char*, unsigned char*, int, unsigned char const*, int, int, int, int):
✗ Branch 5 → 4 not taken.
✗ Branch 5 → 6 not taken.
void packed_to_luma_alpha<unsigned char, false, true>(unsigned char*, unsigned char*, int, unsigned char const*, int, int, int, int):
✗ Branch 5 → 4 not taken.
✗ Branch 5 → 6 not taken.
void packed_to_luma_alpha<unsigned char, true, false>(unsigned char*, unsigned char*, int, unsigned char const*, int, int, int, int):
✗ Branch 5 → 4 not taken.
✗ Branch 5 → 6 not taken.
void packed_to_luma_alpha<unsigned char, true, true>(unsigned char*, unsigned char*, int, unsigned char const*, int, int, int, int):
✓ Branch 5 → 4 taken 30 times.
✓ Branch 5 → 6 taken 6 times.
void packed_to_luma_alpha<unsigned short, false, false>(unsigned char*, unsigned char*, int, unsigned char const*, int, int, int, int):
✗ Branch 5 → 4 not taken.
✗ Branch 5 → 6 not taken.
void packed_to_luma_alpha<unsigned short, false, true>(unsigned char*, unsigned char*, int, unsigned char const*, int, int, int, int):
✗ Branch 5 → 4 not taken.
✗ Branch 5 → 6 not taken.
void packed_to_luma_alpha<unsigned short, true, false>(unsigned char*, unsigned char*, int, unsigned char const*, int, int, int, int):
✗ Branch 5 → 4 not taken.
✗ Branch 5 → 6 not taken.
void packed_to_luma_alpha<unsigned short, true, true>(unsigned char*, unsigned char*, int, unsigned char const*, int, int, int, int):
✗ Branch 5 → 4 not taken.
✗ Branch 5 → 6 not taken.
|
36 | for (int j = 0; j < width; ++j) { |
| 1176 | 30 | const int pixel = reinterpret_cast<const pixel_t*>(srcp)[j * source_rgb_step + channel]; | |
| 1177 | 30 | reinterpret_cast<pixel_t*>(dstp_y)[j] = pixel; | |
| 1178 | if constexpr (target_hasalpha) { | ||
| 1179 | 30 | const int alpha = source_hasalpha ? reinterpret_cast<const pixel_t*>(srcp)[j * source_rgb_step + 3] : max_pixel_value; | |
| 1180 | 30 | reinterpret_cast<pixel_t*>(dstp_a)[j] = alpha; | |
| 1181 | } | ||
| 1182 | } | ||
| 1183 | 6 | srcp -= src_pitch; | |
| 1184 | 6 | dstp_y += dst_pitch; | |
| 1185 | if constexpr (target_hasalpha) | ||
| 1186 | 6 | dstp_a += dst_pitch; | |
| 1187 | } | ||
| 1188 | 2 | } | |
| 1189 | |||
| 1190 | |||
| 1191 | 2 | PVideoFrame ShowChannel::GetFrame(int n, IScriptEnvironment* env) | |
| 1192 | { | ||
| 1193 |
1/2✓ Branch 3 → 4 taken 2 times.
✗ Branch 3 → 383 not taken.
|
2 | PVideoFrame src = child->GetFrame(n, env); |
| 1194 | |||
| 1195 | // for planar these will be reread for proper plane | ||
| 1196 |
1/2✓ Branch 5 → 6 taken 2 times.
✗ Branch 5 → 381 not taken.
|
2 | const BYTE* srcp = src->GetReadPtr(); |
| 1197 |
1/2✓ Branch 7 → 8 taken 2 times.
✗ Branch 7 → 381 not taken.
|
2 | const int height = src->GetHeight(); |
| 1198 |
1/2✓ Branch 9 → 10 taken 2 times.
✗ Branch 9 → 381 not taken.
|
2 | const int pitch = src->GetPitch(); |
| 1199 |
1/2✓ Branch 11 → 12 taken 2 times.
✗ Branch 11 → 381 not taken.
|
2 | const int rowsize = src->GetRowSize(); |
| 1200 | |||
| 1201 | 2 | const float chroma_center_f = 0.0f; | |
| 1202 | |||
| 1203 |
1/2✓ Branch 12 → 13 taken 2 times.
✗ Branch 12 → 173 not taken.
|
2 | if (input_type_is_packed_rgb) { |
| 1204 |
1/4✓ Branch 13 → 14 taken 2 times.
✗ Branch 13 → 381 not taken.
✗ Branch 368 → 369 not taken.
✗ Branch 368 → 370 not taken.
|
2 | PVideoFrame dst = env->NewVideoFrameP(vi, &src); |
| 1205 | |||
| 1206 |
2/4✓ Branch 14 → 15 taken 2 times.
✗ Branch 14 → 368 not taken.
✓ Branch 15 → 16 taken 2 times.
✗ Branch 15 → 18 not taken.
|
2 | if (!vi.IsRGB()) { |
| 1207 | // delete _Matrix when target is not an RGB | ||
| 1208 |
1/2✓ Branch 16 → 17 taken 2 times.
✗ Branch 16 → 368 not taken.
|
2 | auto props = env->getFramePropsRW(dst); |
| 1209 |
1/2✓ Branch 17 → 18 taken 2 times.
✗ Branch 17 → 368 not taken.
|
2 | env->propDeleteKey(props, "_Matrix"); |
| 1210 | } | ||
| 1211 | |||
| 1212 |
1/2✓ Branch 18 → 19 taken 2 times.
✗ Branch 18 → 20 not taken.
|
2 | const int source_rgb_step = source_hasalpha ? 4 : 3; |
| 1213 | 2 | const int w = rowsize / pixelsize / source_rgb_step; | |
| 1214 | |||
| 1215 |
3/10✓ Branch 21 → 22 taken 2 times.
✗ Branch 21 → 368 not taken.
✗ Branch 22 → 23 not taken.
✓ Branch 22 → 26 taken 2 times.
✗ Branch 23 → 24 not taken.
✗ Branch 23 → 368 not taken.
✗ Branch 24 → 25 not taken.
✗ Branch 24 → 26 not taken.
✗ Branch 27 → 28 not taken.
✓ Branch 27 → 55 taken 2 times.
|
2 | if (vi.IsRGB() && !vi.IsPlanar()) |
| 1216 | { | ||
| 1217 | // packed RGB to packed RGB | ||
| 1218 | ✗ | BYTE* dstp = dst->GetWritePtr(); | |
| 1219 | ✗ | const int dstpitch = dst->GetPitch(); | |
| 1220 | |||
| 1221 | ✗ | if (pixelsize == 1) { | |
| 1222 | ✗ | if (!source_hasalpha && !target_hasalpha) | |
| 1223 | ✗ | packed_to_packedrgb<uint8_t, false, false>(dstp, dstpitch, srcp, pitch, w, height, channel); | |
| 1224 | ✗ | else if (!source_hasalpha && target_hasalpha) | |
| 1225 | ✗ | packed_to_packedrgb<uint8_t, false, true>(dstp, dstpitch, srcp, pitch, w, height, channel); | |
| 1226 | ✗ | else if (source_hasalpha && !target_hasalpha) | |
| 1227 | ✗ | packed_to_packedrgb<uint8_t, true, false>(dstp, dstpitch, srcp, pitch, w, height, channel); | |
| 1228 | else // if (source_hasalpha && target_hasalpha) | ||
| 1229 | ✗ | packed_to_packedrgb<uint8_t, true, true>(dstp, dstpitch, srcp, pitch, w, height, channel); | |
| 1230 | } | ||
| 1231 | else { | ||
| 1232 | ✗ | if (!source_hasalpha && !target_hasalpha) | |
| 1233 | ✗ | packed_to_packedrgb<uint16_t, false, false>(dstp, dstpitch, srcp, pitch, w, height, channel); | |
| 1234 | ✗ | else if (!source_hasalpha && target_hasalpha) | |
| 1235 | ✗ | packed_to_packedrgb<uint16_t, false, true>(dstp, dstpitch, srcp, pitch, w, height, channel); | |
| 1236 | ✗ | else if (source_hasalpha && !target_hasalpha) | |
| 1237 | ✗ | packed_to_packedrgb<uint16_t, true, false>(dstp, dstpitch, srcp, pitch, w, height, channel); | |
| 1238 | else // if (source_hasalpha && target_hasalpha) | ||
| 1239 | ✗ | packed_to_packedrgb<uint16_t, true, true>(dstp, dstpitch, srcp, pitch, w, height, channel); | |
| 1240 | } | ||
| 1241 | } | ||
| 1242 |
1/2✗ Branch 55 → 56 not taken.
✓ Branch 55 → 66 taken 2 times.
|
2 | else if (vi.pixel_type == VideoInfo::CS_YUY2) |
| 1243 | { | ||
| 1244 | // packed RGB to YUY2 | ||
| 1245 | ✗ | BYTE* dstp = dst->GetWritePtr(); | |
| 1246 | ✗ | const int dstpitch = dst->GetPitch(); | |
| 1247 | |||
| 1248 | // RGB is upside-down | ||
| 1249 | ✗ | srcp += (height - 1) * pitch; | |
| 1250 | |||
| 1251 | ✗ | for (int i = 0; i < height; ++i) { | |
| 1252 | ✗ | for (int j = 0; j < w; j++) { | |
| 1253 | ✗ | dstp[j * 2 + 0] = srcp[j * source_rgb_step + channel]; | |
| 1254 | ✗ | dstp[j * 2 + 1] = 128; | |
| 1255 | } | ||
| 1256 | ✗ | srcp -= pitch; | |
| 1257 | ✗ | dstp += dstpitch; | |
| 1258 | } | ||
| 1259 | } | ||
| 1260 |
5/14✓ Branch 66 → 67 taken 2 times.
✗ Branch 66 → 368 not taken.
✓ Branch 67 → 68 taken 2 times.
✗ Branch 67 → 72 not taken.
✓ Branch 68 → 69 taken 2 times.
✗ Branch 68 → 368 not taken.
✗ Branch 69 → 70 not taken.
✓ Branch 69 → 72 taken 2 times.
✗ Branch 70 → 71 not taken.
✗ Branch 70 → 368 not taken.
✗ Branch 71 → 72 not taken.
✗ Branch 71 → 73 not taken.
✓ Branch 74 → 75 taken 2 times.
✗ Branch 74 → 127 not taken.
|
2 | else if (vi.IsYUV() || vi.IsYUVA() || vi.IsY()) |
| 1261 | { | ||
| 1262 | // packed RGB -> Y, YUV(A) | ||
| 1263 |
1/2✓ Branch 76 → 77 taken 2 times.
✗ Branch 76 → 368 not taken.
|
2 | BYTE* dstp = dst->GetWritePtr(); |
| 1264 |
1/2✓ Branch 78 → 79 taken 2 times.
✗ Branch 78 → 368 not taken.
|
2 | int dstpitch = dst->GetPitch(); |
| 1265 | |||
| 1266 |
2/4✓ Branch 79 → 80 taken 2 times.
✗ Branch 79 → 83 not taken.
✓ Branch 81 → 82 taken 2 times.
✗ Branch 81 → 368 not taken.
|
2 | BYTE* dstp_a = target_hasalpha ? dst->GetWritePtr(PLANAR_A) : nullptr; |
| 1267 | |||
| 1268 |
1/2✓ Branch 84 → 85 taken 2 times.
✗ Branch 84 → 96 not taken.
|
2 | if (pixelsize == 1) { |
| 1269 |
1/4✗ Branch 85 → 86 not taken.
✓ Branch 85 → 88 taken 2 times.
✗ Branch 86 → 87 not taken.
✗ Branch 86 → 88 not taken.
|
2 | if (!source_hasalpha && !target_hasalpha) |
| 1270 | ✗ | packed_to_luma_alpha<uint8_t, false, false>(dstp, dstp_a, dstpitch, srcp, pitch, w, height, channel); | |
| 1271 |
1/4✗ Branch 88 → 89 not taken.
✓ Branch 88 → 91 taken 2 times.
✗ Branch 89 → 90 not taken.
✗ Branch 89 → 91 not taken.
|
2 | else if (!source_hasalpha && target_hasalpha) |
| 1272 | ✗ | packed_to_luma_alpha<uint8_t, false, true>(dstp, dstp_a, dstpitch, srcp, pitch, w, height, channel); | |
| 1273 |
2/4✓ Branch 91 → 92 taken 2 times.
✗ Branch 91 → 94 not taken.
✗ Branch 92 → 93 not taken.
✓ Branch 92 → 94 taken 2 times.
|
2 | else if (source_hasalpha && !target_hasalpha) |
| 1274 | ✗ | packed_to_luma_alpha<uint8_t, true, false>(dstp, dstp_a, dstpitch, srcp, pitch, w, height, channel); | |
| 1275 | else // if (source_hasalpha && target_hasalpha) | ||
| 1276 | 2 | packed_to_luma_alpha<uint8_t, true, true>(dstp, dstp_a, dstpitch, srcp, pitch, w, height, channel); | |
| 1277 | } | ||
| 1278 | else { | ||
| 1279 | // 16 bit | ||
| 1280 | ✗ | if (!source_hasalpha && !target_hasalpha) | |
| 1281 | ✗ | packed_to_luma_alpha<uint16_t, false, false>(dstp, dstp_a, dstpitch, srcp, pitch, w, height, channel); | |
| 1282 | ✗ | else if (!source_hasalpha && target_hasalpha) | |
| 1283 | ✗ | packed_to_luma_alpha<uint16_t, false, true>(dstp, dstp_a, dstpitch, srcp, pitch, w, height, channel); | |
| 1284 | ✗ | else if (source_hasalpha && !target_hasalpha) | |
| 1285 | ✗ | packed_to_luma_alpha<uint16_t, true, false>(dstp, dstp_a, dstpitch, srcp, pitch, w, height, channel); | |
| 1286 | else // if (source_hasalpha && target_hasalpha) | ||
| 1287 | ✗ | packed_to_luma_alpha<uint16_t, true, true>(dstp, dstp_a, dstpitch, srcp, pitch, w, height, channel); | |
| 1288 | } | ||
| 1289 | |||
| 1290 | // fill chroma neutral | ||
| 1291 |
2/4✓ Branch 106 → 107 taken 2 times.
✗ Branch 106 → 368 not taken.
✓ Branch 107 → 108 taken 2 times.
✗ Branch 107 → 170 not taken.
|
2 | if (!vi.IsY()) |
| 1292 | { | ||
| 1293 |
1/2✓ Branch 109 → 110 taken 2 times.
✗ Branch 109 → 368 not taken.
|
2 | int uvrowsize = dst->GetRowSize(PLANAR_U); |
| 1294 |
1/2✓ Branch 111 → 112 taken 2 times.
✗ Branch 111 → 368 not taken.
|
2 | int uvpitch = dst->GetPitch(PLANAR_U); |
| 1295 |
1/2✓ Branch 113 → 114 taken 2 times.
✗ Branch 113 → 368 not taken.
|
2 | int dstheight = dst->GetHeight(PLANAR_U); |
| 1296 |
1/2✓ Branch 115 → 116 taken 2 times.
✗ Branch 115 → 368 not taken.
|
2 | BYTE* dstp_u = dst->GetWritePtr(PLANAR_U); |
| 1297 |
1/2✓ Branch 117 → 118 taken 2 times.
✗ Branch 117 → 368 not taken.
|
2 | BYTE* dstp_v = dst->GetWritePtr(PLANAR_V); |
| 1298 |
1/4✓ Branch 118 → 119 taken 2 times.
✗ Branch 118 → 121 not taken.
✗ Branch 118 → 124 not taken.
✗ Branch 118 → 126 not taken.
|
2 | switch (pixelsize) { |
| 1299 |
1/2✓ Branch 119 → 120 taken 2 times.
✗ Branch 119 → 368 not taken.
|
2 | case 1: fill_chroma<BYTE>(dstp_u, dstp_v, dstheight, uvrowsize, uvpitch, (BYTE)0x80); break; |
| 1300 | ✗ | case 2: fill_chroma<uint16_t>(dstp_u, dstp_v, dstheight, uvrowsize, uvpitch, 1 << (vi.BitsPerComponent() - 1)); break; | |
| 1301 | ✗ | case 4: | |
| 1302 | ✗ | fill_chroma<float>(dstp_u, dstp_v, dstheight, uvrowsize, uvpitch, chroma_center_f); | |
| 1303 | ✗ | break; | |
| 1304 | } | ||
| 1305 | } | ||
| 1306 | } | ||
| 1307 | ✗ | else if (vi.IsPlanarRGB() || vi.IsPlanarRGBA()) | |
| 1308 | { // packed RGB -> Planar RGB 8/16 bit | ||
| 1309 | ✗ | BYTE* dstp_g = dst->GetWritePtr(PLANAR_G); | |
| 1310 | ✗ | BYTE* dstp_b = dst->GetWritePtr(PLANAR_B); | |
| 1311 | ✗ | BYTE* dstp_r = dst->GetWritePtr(PLANAR_R); | |
| 1312 | ✗ | int dstpitch = dst->GetPitch(); | |
| 1313 | |||
| 1314 | ✗ | BYTE* dstp_a = target_hasalpha ? dst->GetWritePtr(PLANAR_A) : nullptr; | |
| 1315 | |||
| 1316 | ✗ | if (pixelsize == 1) { | |
| 1317 | ✗ | if (!source_hasalpha && !target_hasalpha) | |
| 1318 | ✗ | packed_to_planarrgb<uint8_t, false, false>(dstp_r, dstp_g, dstp_b, dstp_a, dstpitch, srcp, pitch, w, height, channel); | |
| 1319 | ✗ | else if (!source_hasalpha && target_hasalpha) | |
| 1320 | ✗ | packed_to_planarrgb<uint8_t, false, true>(dstp_r, dstp_g, dstp_b, dstp_a, dstpitch, srcp, pitch, w, height, channel); | |
| 1321 | ✗ | else if (source_hasalpha && !target_hasalpha) | |
| 1322 | ✗ | packed_to_planarrgb<uint8_t, true, false>(dstp_r, dstp_g, dstp_b, dstp_a, dstpitch, srcp, pitch, w, height, channel); | |
| 1323 | else // if (source_hasalpha && target_hasalpha) | ||
| 1324 | ✗ | packed_to_planarrgb<uint8_t, true, true>(dstp_r, dstp_g, dstp_b, dstp_a, dstpitch, srcp, pitch, w, height, channel); | |
| 1325 | } | ||
| 1326 | else { | ||
| 1327 | // 16 bit | ||
| 1328 | ✗ | if (!source_hasalpha && !target_hasalpha) | |
| 1329 | ✗ | packed_to_planarrgb<uint16_t, false, false>(dstp_r, dstp_g, dstp_b, dstp_a, dstpitch, srcp, pitch, w, height, channel); | |
| 1330 | ✗ | else if (!source_hasalpha && target_hasalpha) | |
| 1331 | ✗ | packed_to_planarrgb<uint16_t, false, true>(dstp_r, dstp_g, dstp_b, dstp_a, dstpitch, srcp, pitch, w, height, channel); | |
| 1332 | ✗ | else if (source_hasalpha && !target_hasalpha) | |
| 1333 | ✗ | packed_to_planarrgb<uint16_t, true, false>(dstp_r, dstp_g, dstp_b, dstp_a, dstpitch, srcp, pitch, w, height, channel); | |
| 1334 | else // if (source_hasalpha && target_hasalpha) | ||
| 1335 | ✗ | packed_to_planarrgb<uint16_t, true, true>(dstp_r, dstp_g, dstp_b, dstp_a, dstpitch, srcp, pitch, w, height, channel); | |
| 1336 | } | ||
| 1337 | } | ||
| 1338 |
1/2✗ Branch 170 → 171 not taken.
✓ Branch 170 → 172 taken 2 times.
|
2 | return dst; |
| 1339 | ✗ | } // end of packed rgb source | |
| 1340 | |||
| 1341 | ✗ | if (input_type_is_planar_rgb || input_type_is_planar_rgba || input_type_is_yuv || input_type_is_yuva) { | |
| 1342 | // planar source | ||
| 1343 | ✗ | const int planesYUV[4] = { PLANAR_Y, PLANAR_U, PLANAR_V, PLANAR_A }; | |
| 1344 | ✗ | const int planesRGB[4] = { PLANAR_G, PLANAR_B, PLANAR_R, PLANAR_A }; | |
| 1345 | ✗ | const int* planes = (input_type_is_planar_rgb || input_type_is_planar_rgba) ? planesRGB : planesYUV; | |
| 1346 | ✗ | int final_channel = channel; | |
| 1347 | // RGB channels: B=0 G=1 R=2 (like packed) | ||
| 1348 | // Planar order: G=0 B=1 R=2 | ||
| 1349 | ✗ | if (input_type_is_planar_rgb || input_type_is_planar_rgba) { | |
| 1350 | // exchange B and G | ||
| 1351 | ✗ | if (channel == 0) final_channel = 1; | |
| 1352 | ✗ | else if (channel == 1) final_channel = 0; | |
| 1353 | } | ||
| 1354 | ✗ | const int plane = planes[final_channel]; | |
| 1355 | |||
| 1356 | ✗ | const BYTE* srcp = src->GetReadPtr(plane); // source plane | |
| 1357 | ✗ | const BYTE* srcp_a = source_hasalpha ? src->GetReadPtr(PLANAR_A) : nullptr; | |
| 1358 | |||
| 1359 | ✗ | const int width = src->GetRowSize(plane) / pixelsize; | |
| 1360 | ✗ | const int height = src->GetHeight(plane); | |
| 1361 | ✗ | const int pitch = src->GetPitch(plane); | |
| 1362 | |||
| 1363 | ✗ | if (vi.IsRGB() && !vi.IsPlanar()) | |
| 1364 | { | ||
| 1365 | // planar RGBP/YUVA -> packed RGB | ||
| 1366 | ✗ | PVideoFrame dst = env->NewVideoFrameP(vi, &src); | |
| 1367 | ✗ | BYTE* dstp = dst->GetWritePtr(); | |
| 1368 | ✗ | const int dstpitch = dst->GetPitch(); | |
| 1369 | |||
| 1370 | ✗ | if (!input_type_is_planar_rgb && !input_type_is_planar_rgba) { | |
| 1371 | ✗ | auto props = env->getFramePropsRW(dst); | |
| 1372 | // delete _Matrix and ChromaLocation when source is not RGB | ||
| 1373 | ✗ | env->propDeleteKey(props, "_Matrix"); | |
| 1374 | ✗ | env->propDeleteKey(props, "_ChromaLocation"); | |
| 1375 | } | ||
| 1376 | |||
| 1377 | ✗ | if (bits_per_pixel == 8) { | |
| 1378 | ✗ | if (target_hasalpha) { | |
| 1379 | ✗ | if (source_hasalpha) | |
| 1380 | ✗ | planar_to_packedrgb<uint8_t, true, true>(dstp, dstpitch, srcp, srcp_a, pitch, width, height); | |
| 1381 | else | ||
| 1382 | ✗ | planar_to_packedrgb<uint8_t, false, true>(dstp, dstpitch, srcp, srcp_a, pitch, width, height); | |
| 1383 | } | ||
| 1384 | else { | ||
| 1385 | ✗ | planar_to_packedrgb<uint8_t, false, false>(dstp, dstpitch, srcp, srcp_a, pitch, width, height); | |
| 1386 | } | ||
| 1387 | } | ||
| 1388 | else { | ||
| 1389 | // 16 bits | ||
| 1390 | ✗ | if (target_hasalpha) { | |
| 1391 | ✗ | if (source_hasalpha) | |
| 1392 | ✗ | planar_to_packedrgb<uint16_t, true, true>(dstp, dstpitch, srcp, srcp_a, pitch, width, height); | |
| 1393 | else | ||
| 1394 | ✗ | planar_to_packedrgb<uint16_t, false, true>(dstp, dstpitch, srcp, srcp_a, pitch, width, height); | |
| 1395 | } | ||
| 1396 | else { | ||
| 1397 | ✗ | planar_to_packedrgb<uint16_t, false, false>(dstp, dstpitch, srcp, srcp_a, pitch, width, height); | |
| 1398 | } | ||
| 1399 | } | ||
| 1400 | |||
| 1401 | ✗ | return dst; | |
| 1402 | ✗ | } | |
| 1403 | ✗ | else if (vi.pixel_type == VideoInfo::CS_YUY2) // RGB(A)P/YUVA->YUY2 | |
| 1404 | { | ||
| 1405 | ✗ | PVideoFrame dst = env->NewVideoFrameP(vi, &src); | |
| 1406 | ✗ | BYTE* dstp = dst->GetWritePtr(); | |
| 1407 | ✗ | const int dstpitch = dst->GetPitch(); | |
| 1408 | |||
| 1409 | ✗ | auto props = env->getFramePropsRW(dst); | |
| 1410 | ✗ | env->propDeleteKey(props, "_Matrix"); | |
| 1411 | ✗ | env->propDeleteKey(props, "_ChromaLocation"); | |
| 1412 | |||
| 1413 | ✗ | for (int i = 0; i < height; ++i) { | |
| 1414 | ✗ | for (int j = 0; j < width; j++) { | |
| 1415 | ✗ | dstp[j * 2 + 0] = srcp[j]; | |
| 1416 | ✗ | dstp[j * 2 + 1] = 128; | |
| 1417 | } | ||
| 1418 | ✗ | srcp += pitch; | |
| 1419 | ✗ | dstp += dstpitch; | |
| 1420 | } | ||
| 1421 | ✗ | return dst; | |
| 1422 | ✗ | } | |
| 1423 | else | ||
| 1424 | { // planar to planar | ||
| 1425 | // RGB(A)P/YUVA -> YV12/16/24/Y8 + 16bit | ||
| 1426 | ✗ | PVideoFrame dst = env->NewVideoFrameP(vi, &src); | |
| 1427 | |||
| 1428 | // remove frame props if either src or target is not RGB | ||
| 1429 | ✗ | if (!(input_type_is_planar_rgb || input_type_is_planar_rgba || vi.IsRGB())) { | |
| 1430 | // RGB origin to YUV | ||
| 1431 | ✗ | auto props = env->getFramePropsRW(dst); | |
| 1432 | ✗ | env->propDeleteKey(props, "_Matrix"); | |
| 1433 | ✗ | if(input_type_is_yuv || input_type_is_yuva) | |
| 1434 | ✗ | env->propDeleteKey(props, "_ChromaLocation"); | |
| 1435 | } | ||
| 1436 | |||
| 1437 | ✗ | if (vi.IsYUV() || vi.IsYUVA() || vi.IsY()) // Y8, YV12, Y16, YUV420P16, etc. | |
| 1438 | { | ||
| 1439 | ✗ | BYTE* dstp = dst->GetWritePtr(); | |
| 1440 | ✗ | int dstpitch = dst->GetPitch(); | |
| 1441 | |||
| 1442 | // copy source plane to luma | ||
| 1443 | ✗ | env->BitBlt(dstp, dstpitch, srcp, pitch, width * pixelsize, height); | |
| 1444 | // fill UV with neutral | ||
| 1445 | ✗ | if (!vi.IsY()) | |
| 1446 | { | ||
| 1447 | ✗ | int uvrowsize = dst->GetRowSize(PLANAR_U); | |
| 1448 | ✗ | int uvpitch = dst->GetPitch(PLANAR_U); | |
| 1449 | ✗ | int dstheight = dst->GetHeight(PLANAR_U); | |
| 1450 | ✗ | BYTE* dstp_u = dst->GetWritePtr(PLANAR_U); | |
| 1451 | ✗ | BYTE* dstp_v = dst->GetWritePtr(PLANAR_V); | |
| 1452 | ✗ | switch (pixelsize) { | |
| 1453 | ✗ | case 1: fill_chroma<uint8_t>(dstp_u, dstp_v, dstheight, uvrowsize, uvpitch, (uint8_t)0x80); break; | |
| 1454 | ✗ | case 2: fill_chroma<uint16_t>(dstp_u, dstp_v, dstheight, uvrowsize, uvpitch, 1 << (vi.BitsPerComponent() - 1)); break; | |
| 1455 | ✗ | case 4: fill_chroma<float>(dstp_u, dstp_v, dstheight, uvrowsize, uvpitch, chroma_center_f); break; | |
| 1456 | } | ||
| 1457 | } | ||
| 1458 | } | ||
| 1459 | ✗ | else if (vi.IsPlanarRGB() || vi.IsPlanarRGBA()) | |
| 1460 | { // RGBP(A)/YUVA -> Planar RGB | ||
| 1461 | ✗ | BYTE* dstp_g = dst->GetWritePtr(PLANAR_G); | |
| 1462 | ✗ | BYTE* dstp_b = dst->GetWritePtr(PLANAR_B); | |
| 1463 | ✗ | BYTE* dstp_r = dst->GetWritePtr(PLANAR_R); | |
| 1464 | |||
| 1465 | ✗ | int dstpitch = dst->GetPitch(); | |
| 1466 | ✗ | int dstwidth = dst->GetRowSize() / pixelsize; | |
| 1467 | |||
| 1468 | // copy to all channels | ||
| 1469 | ✗ | if (pixelsize == 1) { | |
| 1470 | ✗ | for (int i = 0; i < height; ++i) { | |
| 1471 | ✗ | for (int j = 0; j < dstwidth; ++j) { | |
| 1472 | ✗ | dstp_g[j] = dstp_b[j] = dstp_r[j] = srcp[j]; | |
| 1473 | } | ||
| 1474 | ✗ | srcp += pitch; | |
| 1475 | ✗ | dstp_g += dstpitch; | |
| 1476 | ✗ | dstp_b += dstpitch; | |
| 1477 | ✗ | dstp_r += dstpitch; | |
| 1478 | } | ||
| 1479 | } | ||
| 1480 | ✗ | else if (pixelsize == 2) { | |
| 1481 | ✗ | for (int i = 0; i < height; ++i) { | |
| 1482 | ✗ | for (int j = 0; j < dstwidth; ++j) { | |
| 1483 | ✗ | reinterpret_cast<uint16_t*>(dstp_g)[j] = | |
| 1484 | ✗ | reinterpret_cast<uint16_t*>(dstp_b)[j] = | |
| 1485 | ✗ | reinterpret_cast<uint16_t*>(dstp_r)[j] = reinterpret_cast<const uint16_t*>(srcp)[j]; | |
| 1486 | } | ||
| 1487 | ✗ | srcp += pitch; | |
| 1488 | ✗ | dstp_g += dstpitch; | |
| 1489 | ✗ | dstp_b += dstpitch; | |
| 1490 | ✗ | dstp_r += dstpitch; | |
| 1491 | } | ||
| 1492 | } | ||
| 1493 | else { // pixelsize==4 | ||
| 1494 | ✗ | for (int i = 0; i < height; ++i) { | |
| 1495 | ✗ | for (int j = 0; j < dstwidth; ++j) { | |
| 1496 | ✗ | reinterpret_cast<float*>(dstp_g)[j] = | |
| 1497 | ✗ | reinterpret_cast<float*>(dstp_b)[j] = | |
| 1498 | ✗ | reinterpret_cast<float*>(dstp_r)[j] = reinterpret_cast<const float*>(srcp)[j]; | |
| 1499 | } | ||
| 1500 | ✗ | srcp += pitch; | |
| 1501 | ✗ | dstp_g += dstpitch; | |
| 1502 | ✗ | dstp_b += dstpitch; | |
| 1503 | ✗ | dstp_r += dstpitch; | |
| 1504 | } | ||
| 1505 | } | ||
| 1506 | } | ||
| 1507 | ✗ | if (target_hasalpha) { | |
| 1508 | // fill alpha with transparent | ||
| 1509 | ✗ | const int dst_rowsizeA = dst->GetRowSize(PLANAR_A); | |
| 1510 | ✗ | const int dst_pitchA = dst->GetPitch(PLANAR_A); | |
| 1511 | ✗ | BYTE* dstp_a = dst->GetWritePtr(PLANAR_A); | |
| 1512 | ✗ | const int heightA = dst->GetHeight(PLANAR_A); | |
| 1513 | |||
| 1514 | ✗ | if (source_hasalpha) { | |
| 1515 | // copy source alpha plane to target alpha plane | ||
| 1516 | ✗ | env->BitBlt(dstp_a, dst_pitchA, srcp_a, pitch, width* pixelsize, height); | |
| 1517 | } | ||
| 1518 | else { | ||
| 1519 | ✗ | switch (vi.ComponentSize()) | |
| 1520 | { | ||
| 1521 | ✗ | case 1: | |
| 1522 | ✗ | fill_plane<uint8_t>(dstp_a, heightA, dst_rowsizeA, dst_pitchA, 0xFF); | |
| 1523 | ✗ | break; | |
| 1524 | ✗ | case 2: | |
| 1525 | ✗ | fill_plane<uint16_t>(dstp_a, heightA, dst_rowsizeA, dst_pitchA, (1 << vi.BitsPerComponent()) - 1); | |
| 1526 | ✗ | break; | |
| 1527 | ✗ | case 4: | |
| 1528 | ✗ | fill_plane<float>(dstp_a, heightA, dst_rowsizeA, dst_pitchA, 1.0f); | |
| 1529 | ✗ | break; | |
| 1530 | } | ||
| 1531 | } | ||
| 1532 | } | ||
| 1533 | ✗ | return dst; | |
| 1534 | ✗ | } | |
| 1535 | } // planar RGB(A) or YUVA source | ||
| 1536 | |||
| 1537 | ✗ | env->ThrowError("ShowChannel: unexpected end of function"); | |
| 1538 | ✗ | return src; | |
| 1539 | 2 | } | |
| 1540 | |||
| 1541 | |||
| 1542 | ✗ | AVSValue ShowChannel::Create(AVSValue args, void* channel, IScriptEnvironment* env) | |
| 1543 | { | ||
| 1544 | // yuy2 is autoconverted to YV16 | ||
| 1545 | ✗ | PClip clip = args[0].AsClip(); | |
| 1546 | ✗ | const VideoInfo& vi = clip->GetVideoInfo(); | |
| 1547 | |||
| 1548 | ✗ | if (vi.IsYUY2()) { | |
| 1549 | ✗ | AVSValue new_args[1] = { clip }; | |
| 1550 | ✗ | clip = env->Invoke("ConvertToYV16", AVSValue(new_args, 1)).AsClip(); | |
| 1551 | ✗ | } | |
| 1552 | ✗ | return new ShowChannel(clip, args[1].AsString(""), (int)(size_t)channel, env); | |
| 1553 | ✗ | } | |
| 1554 | |||
| 1555 | |||
| 1556 | /********************************** | ||
| 1557 | ****** MergeRGB filter ****** | ||
| 1558 | **********************************/ | ||
| 1559 | |||
| 1560 | |||
| 1561 | 4 | MergeRGB::MergeRGB(PClip _child, PClip _blue, PClip _green, PClip _red, PClip _alpha, | |
| 1562 | 4 | const char* pixel_type, IScriptEnvironment* env) | |
| 1563 |
3/6✓ Branch 6 → 7 taken 4 times.
✗ Branch 6 → 231 not taken.
✓ Branch 7 → 8 taken 4 times.
✗ Branch 7 → 229 not taken.
✓ Branch 8 → 9 taken 4 times.
✗ Branch 8 → 227 not taken.
|
4 | : GenericVideoFilter(_child), blue(_blue), green(_green), red(_red), alpha(_alpha), |
| 1564 |
3/6✓ Branch 10 → 11 taken 4 times.
✗ Branch 10 → 225 not taken.
✓ Branch 12 → 13 taken 4 times.
✗ Branch 12 → 225 not taken.
✓ Branch 14 → 15 taken 4 times.
✗ Branch 14 → 225 not taken.
|
4 | viB(blue->GetVideoInfo()), viG(green->GetVideoInfo()), viR(red->GetVideoInfo()), |
| 1565 |
8/12✓ Branch 2 → 3 taken 4 times.
✗ Branch 2 → 201 not taken.
✓ Branch 3 → 4 taken 4 times.
✗ Branch 3 → 199 not taken.
✓ Branch 5 → 6 taken 4 times.
✗ Branch 5 → 233 not taken.
✓ Branch 16 → 17 taken 2 times.
✓ Branch 16 → 18 taken 2 times.
✓ Branch 20 → 21 taken 4 times.
✗ Branch 20 → 225 not taken.
✓ Branch 22 → 23 taken 2 times.
✓ Branch 22 → 24 taken 2 times.
|
8 | viA(((alpha) ? alpha : child)->GetVideoInfo()), myname((alpha) ? "MergeARGB" : "MergeRGB") |
| 1566 | { | ||
| 1567 | 4 | vi = viR; // comparison base | |
| 1568 | |||
| 1569 |
5/10✓ Branch 25 → 26 taken 4 times.
✗ Branch 25 → 225 not taken.
✓ Branch 26 → 27 taken 4 times.
✗ Branch 26 → 225 not taken.
✓ Branch 28 → 29 taken 4 times.
✗ Branch 28 → 225 not taken.
✓ Branch 29 → 30 taken 4 times.
✗ Branch 29 → 225 not taken.
✓ Branch 30 → 31 taken 4 times.
✗ Branch 30 → 37 not taken.
|
8 | if ((vi.BitsPerComponent() != viB.BitsPerComponent()) || (vi.BitsPerComponent() != viG.BitsPerComponent()) || |
| 1570 |
8/16✓ Branch 27 → 28 taken 4 times.
✗ Branch 27 → 37 not taken.
✓ Branch 31 → 32 taken 4 times.
✗ Branch 31 → 225 not taken.
✓ Branch 32 → 33 taken 4 times.
✗ Branch 32 → 225 not taken.
✓ Branch 33 → 34 taken 4 times.
✗ Branch 33 → 37 not taken.
✓ Branch 34 → 35 taken 4 times.
✗ Branch 34 → 225 not taken.
✓ Branch 35 → 36 taken 4 times.
✗ Branch 35 → 225 not taken.
✗ Branch 36 → 37 not taken.
✓ Branch 36 → 38 taken 4 times.
✗ Branch 39 → 40 not taken.
✓ Branch 39 → 41 taken 4 times.
|
8 | (vi.BitsPerComponent() != viR.BitsPerComponent()) || (vi.BitsPerComponent() != viA.BitsPerComponent())) |
| 1571 | ✗ | env->ThrowError("%s: All clips must have the same bit depth.", myname); | |
| 1572 | |||
| 1573 |
5/8✓ Branch 41 → 42 taken 4 times.
✗ Branch 41 → 45 not taken.
✓ Branch 42 → 43 taken 3 times.
✓ Branch 42 → 45 taken 1 time.
✓ Branch 43 → 44 taken 3 times.
✗ Branch 43 → 45 not taken.
✗ Branch 44 → 45 not taken.
✓ Branch 44 → 46 taken 3 times.
|
4 | if ((vi.width != viB.width) || (vi.width != viG.width) || (vi.width != viR.width) || (vi.width != viA.width)) |
| 1574 |
1/2✗ Branch 45 → 46 not taken.
✓ Branch 45 → 225 taken 1 time.
|
1 | env->ThrowError("%s: All clips must have the same width.", myname); |
| 1575 | |||
| 1576 |
4/8✓ Branch 46 → 47 taken 3 times.
✗ Branch 46 → 50 not taken.
✓ Branch 47 → 48 taken 3 times.
✗ Branch 47 → 50 not taken.
✓ Branch 48 → 49 taken 3 times.
✗ Branch 48 → 50 not taken.
✗ Branch 49 → 50 not taken.
✓ Branch 49 → 51 taken 3 times.
|
3 | if ((vi.height != viB.height) || (vi.height != viG.height) || (vi.height != viR.height) || (vi.height != viA.height)) |
| 1577 | ✗ | env->ThrowError("%s: All clips must have the same height.", myname); | |
| 1578 | |||
| 1579 | const int is_any_planar_rgb = | ||
| 1580 |
3/6✓ Branch 51 → 52 taken 3 times.
✗ Branch 51 → 225 not taken.
✓ Branch 53 → 54 taken 1 time.
✗ Branch 53 → 225 not taken.
✓ Branch 54 → 55 taken 1 time.
✗ Branch 54 → 67 not taken.
|
4 | viR.IsPlanarRGB() || viR.IsPlanarRGBA() || |
| 1581 |
4/8✓ Branch 55 → 56 taken 1 time.
✗ Branch 55 → 225 not taken.
✓ Branch 56 → 57 taken 1 time.
✗ Branch 56 → 67 not taken.
✓ Branch 57 → 58 taken 1 time.
✗ Branch 57 → 225 not taken.
✓ Branch 58 → 59 taken 1 time.
✗ Branch 58 → 67 not taken.
|
1 | viG.IsPlanarRGB() || viG.IsPlanarRGBA() || |
| 1582 |
4/8✓ Branch 59 → 60 taken 1 time.
✗ Branch 59 → 225 not taken.
✓ Branch 60 → 61 taken 1 time.
✗ Branch 60 → 67 not taken.
✓ Branch 61 → 62 taken 1 time.
✗ Branch 61 → 225 not taken.
✓ Branch 62 → 63 taken 1 time.
✗ Branch 62 → 67 not taken.
|
1 | viB.IsPlanarRGB() || viB.IsPlanarRGBA() || |
| 1583 |
6/10✓ Branch 52 → 53 taken 1 time.
✓ Branch 52 → 67 taken 2 times.
✓ Branch 63 → 64 taken 1 time.
✗ Branch 63 → 225 not taken.
✓ Branch 64 → 65 taken 1 time.
✗ Branch 64 → 67 not taken.
✓ Branch 65 → 66 taken 1 time.
✗ Branch 65 → 225 not taken.
✗ Branch 66 → 67 not taken.
✓ Branch 66 → 68 taken 1 time.
|
4 | viA.IsPlanarRGB() || viA.IsPlanarRGBA(); |
| 1584 | |||
| 1585 |
1/2✓ Branch 69 → 70 taken 3 times.
✗ Branch 69 → 225 not taken.
|
3 | const int bits_per_pixel = viR.BitsPerComponent(); |
| 1586 |
3/4✓ Branch 70 → 71 taken 3 times.
✗ Branch 70 → 72 not taken.
✓ Branch 71 → 72 taken 1 time.
✓ Branch 71 → 73 taken 2 times.
|
3 | const bool empty_pixel_type = pixel_type == nullptr || *pixel_type == 0; |
| 1587 | |||
| 1588 | // planar rgb target if | ||
| 1589 | // - pixel_type is "rgb" or | ||
| 1590 | // - pixel_type not specified and | ||
| 1591 | // - bit depth is not 8 or 16 (cannot have packed rgb representation) or | ||
| 1592 | // - any of the input clips is planar rgb | ||
| 1593 | |||
| 1594 |
3/4✓ Branch 74 → 75 taken 3 times.
✗ Branch 74 → 79 not taken.
✓ Branch 75 → 76 taken 1 time.
✓ Branch 75 → 111 taken 2 times.
|
3 | if (!lstrcmpi(pixel_type, "rgb") || |
| 1595 |
2/6✓ Branch 76 → 77 taken 1 time.
✗ Branch 76 → 79 not taken.
✗ Branch 77 → 78 not taken.
✓ Branch 77 → 111 taken 1 time.
✗ Branch 78 → 79 not taken.
✗ Branch 78 → 111 not taken.
|
1 | (empty_pixel_type && (is_any_planar_rgb || (bits_per_pixel != 8 && bits_per_pixel != 16)))) |
| 1596 | { | ||
| 1597 | ✗ | switch (bits_per_pixel) { | |
| 1598 | ✗ | case 8: vi.pixel_type = alpha ? VideoInfo::CS_RGBAP8 : VideoInfo::CS_RGBP8; break; | |
| 1599 | ✗ | case 10: vi.pixel_type = alpha ? VideoInfo::CS_RGBAP10 : VideoInfo::CS_RGBP10; break; | |
| 1600 | ✗ | case 12: vi.pixel_type = alpha ? VideoInfo::CS_RGBAP12 : VideoInfo::CS_RGBP12; break; | |
| 1601 | ✗ | case 14: vi.pixel_type = alpha ? VideoInfo::CS_RGBAP14 : VideoInfo::CS_RGBP14; break; | |
| 1602 | ✗ | case 16: vi.pixel_type = alpha ? VideoInfo::CS_RGBAP16 : VideoInfo::CS_RGBP16; break; | |
| 1603 | ✗ | case 32: vi.pixel_type = alpha ? VideoInfo::CS_RGBAPS : VideoInfo::CS_RGBPS; break; | |
| 1604 | } | ||
| 1605 | } | ||
| 1606 |
6/8✓ Branch 111 → 112 taken 1 time.
✓ Branch 111 → 115 taken 2 times.
✓ Branch 112 → 113 taken 1 time.
✗ Branch 112 → 225 not taken.
✓ Branch 113 → 114 taken 1 time.
✗ Branch 113 → 115 not taken.
✓ Branch 116 → 117 taken 1 time.
✓ Branch 116 → 118 taken 2 times.
|
3 | else if (empty_pixel_type && vi.BitsPerComponent() == 8) { |
| 1607 | // default for 8 bit | ||
| 1608 | 1 | vi.pixel_type = VideoInfo::CS_BGR32; | |
| 1609 | } | ||
| 1610 |
2/8✗ Branch 118 → 119 not taken.
✓ Branch 118 → 122 taken 2 times.
✗ Branch 119 → 120 not taken.
✗ Branch 119 → 225 not taken.
✗ Branch 120 → 121 not taken.
✗ Branch 120 → 122 not taken.
✗ Branch 123 → 124 not taken.
✓ Branch 123 → 125 taken 2 times.
|
2 | else if (empty_pixel_type && vi.BitsPerComponent() == 16) { |
| 1611 | // default for 16 bit | ||
| 1612 | ✗ | vi.pixel_type = VideoInfo::CS_BGR64; | |
| 1613 | } | ||
| 1614 | else { | ||
| 1615 | // explicitely given output pixel type | ||
| 1616 | |||
| 1617 | // first try | ||
| 1618 | // Append bit depth and check | ||
| 1619 |
1/2✓ Branch 127 → 128 taken 2 times.
✗ Branch 127 → 202 not taken.
|
2 | std::string format = pixel_type; |
| 1620 | // RGBP --> RGBPS, Y -> Y16, YUV420 -> YUV420P10 | ||
| 1621 |
1/2✗ Branch 129 → 130 not taken.
✓ Branch 129 → 136 taken 2 times.
|
2 | if (!lstrcmpi(pixel_type, "y")) { |
| 1622 | ✗ | format = format + std::to_string(bits_per_pixel); // Y8..Y16, also Y32 | |
| 1623 | } | ||
| 1624 |
2/4✓ Branch 136 → 137 taken 2 times.
✗ Branch 136 → 138 not taken.
✓ Branch 137 → 138 taken 2 times.
✗ Branch 137 → 150 not taken.
|
2 | else if (!lstrcmpi(pixel_type, "rgbp") || !lstrcmpi(pixel_type, "rgbap")) { |
| 1625 |
1/2✗ Branch 138 → 139 not taken.
✓ Branch 138 → 143 taken 2 times.
|
2 | if (bits_per_pixel == 32) |
| 1626 | ✗ | format = format + "S"; // RGBAPS | |
| 1627 | else | ||
| 1628 |
1/2✓ Branch 144 → 145 taken 2 times.
✗ Branch 144 → 210 not taken.
|
2 | format = format + std::to_string(bits_per_pixel); // RGBP16 |
| 1629 | } | ||
| 1630 | else { | ||
| 1631 | // hopefully like "yuv420" or "yuva444" | ||
| 1632 | ✗ | if (bits_per_pixel == 32) | |
| 1633 | ✗ | format = format + "PS"; // YUV420PS | |
| 1634 | else | ||
| 1635 | ✗ | format = format + "P" + std::to_string(bits_per_pixel); // YUV420P16 | |
| 1636 | } | ||
| 1637 | |||
| 1638 |
1/2✓ Branch 164 → 165 taken 2 times.
✗ Branch 164 → 222 not taken.
|
2 | int new_pixel_type = GetPixelTypeFromName(format.c_str()); |
| 1639 |
1/2✗ Branch 165 → 166 not taken.
✓ Branch 165 → 169 taken 2 times.
|
2 | if (new_pixel_type == VideoInfo::CS_UNKNOWN) { |
| 1640 | ✗ | new_pixel_type = GetPixelTypeFromName(pixel_type); | |
| 1641 | ✗ | if (new_pixel_type == VideoInfo::CS_UNKNOWN) | |
| 1642 | ✗ | env->ThrowError("%s: invalid pixel_type!", myname); | |
| 1643 | } | ||
| 1644 | // new output format | ||
| 1645 | 2 | vi.pixel_type = new_pixel_type; | |
| 1646 | 2 | } | |
| 1647 | |||
| 1648 |
2/4✓ Branch 171 → 172 taken 3 times.
✗ Branch 171 → 225 not taken.
✗ Branch 172 → 173 not taken.
✓ Branch 172 → 175 taken 3 times.
|
3 | if (vi.BitsPerComponent() != bits_per_pixel) |
| 1649 | ✗ | env->ThrowError("%s: target bit depth (%d) must match with sources (%d)", myname, vi.BitsPerComponent(), bits_per_pixel); | |
| 1650 | |||
| 1651 |
2/4✓ Branch 175 → 176 taken 3 times.
✗ Branch 175 → 225 not taken.
✗ Branch 176 → 177 not taken.
✓ Branch 176 → 178 taken 3 times.
|
3 | if (!vi.IsRGB()) |
| 1652 | ✗ | env->ThrowError("%s: target format must be an RGB format", myname); | |
| 1653 | |||
| 1654 |
5/8✓ Branch 179 → 180 taken 2 times.
✓ Branch 179 → 183 taken 1 time.
✓ Branch 180 → 181 taken 2 times.
✗ Branch 180 → 225 not taken.
✗ Branch 181 → 182 not taken.
✓ Branch 181 → 183 taken 2 times.
✗ Branch 184 → 185 not taken.
✓ Branch 184 → 186 taken 3 times.
|
3 | if(alpha && vi.NumComponents() != 4) |
| 1655 | ✗ | env->ThrowError("MergeARGB: target format must have an alpha channel"); | |
| 1656 | |||
| 1657 | // When not in ARGB mode, target is still allowed to have an alpha channel. | ||
| 1658 | // If no alpha source is given, target alpha will be filled by default 0 value. | ||
| 1659 | |||
| 1660 |
11/16✓ Branch 187 → 188 taken 2 times.
✓ Branch 187 → 195 taken 1 time.
✓ Branch 188 → 189 taken 2 times.
✗ Branch 188 → 225 not taken.
✓ Branch 189 → 190 taken 2 times.
✗ Branch 189 → 194 not taken.
✓ Branch 190 → 191 taken 2 times.
✗ Branch 190 → 225 not taken.
✓ Branch 191 → 192 taken 2 times.
✗ Branch 191 → 194 not taken.
✓ Branch 192 → 193 taken 2 times.
✗ Branch 192 → 225 not taken.
✓ Branch 193 → 194 taken 1 time.
✓ Branch 193 → 195 taken 1 time.
✓ Branch 196 → 197 taken 1 time.
✓ Branch 196 → 198 taken 2 times.
|
3 | if (alpha && (viA.IsRGB24() || viA.IsRGB48() || viA.IsPlanarRGB())) |
| 1661 |
1/2✗ Branch 197 → 198 not taken.
✓ Branch 197 → 225 taken 1 time.
|
1 | env->ThrowError("MergeARGB: Alpha source channel cannot be obtained from RGB24, RGB48 or alphaless planar RGB"); |
| 1662 | 12 | } | |
| 1663 | |||
| 1664 | |||
| 1665 | 3 | PVideoFrame MergeRGB::GetFrame(int n, IScriptEnvironment* env) | |
| 1666 | { | ||
| 1667 |
1/2✓ Branch 3 → 4 taken 3 times.
✗ Branch 3 → 318 not taken.
|
3 | PVideoFrame B = blue->GetFrame(n, env); |
| 1668 |
1/2✓ Branch 5 → 6 taken 3 times.
✗ Branch 5 → 316 not taken.
|
3 | PVideoFrame G = green->GetFrame(n, env); |
| 1669 |
1/2✓ Branch 7 → 8 taken 3 times.
✗ Branch 7 → 314 not taken.
|
3 | PVideoFrame R = red->GetFrame(n, env); |
| 1670 |
4/6✓ Branch 9 → 10 taken 2 times.
✓ Branch 9 → 12 taken 1 time.
✓ Branch 11 → 13 taken 2 times.
✗ Branch 11 → 312 not taken.
✓ Branch 12 → 13 taken 1 time.
✗ Branch 12 → 312 not taken.
|
3 | PVideoFrame A = (alpha) ? alpha->GetFrame(n, env) : 0; |
| 1671 | |||
| 1672 | // choose one for frame property source: R | ||
| 1673 |
1/2✓ Branch 13 → 14 taken 3 times.
✗ Branch 13 → 310 not taken.
|
3 | PVideoFrame dst = env->NewVideoFrameP(vi, &R); |
| 1674 | |||
| 1675 |
1/2✓ Branch 14 → 15 taken 3 times.
✗ Branch 14 → 308 not taken.
|
3 | auto props = env->getFramePropsRW(dst); |
| 1676 |
3/4✓ Branch 15 → 16 taken 3 times.
✗ Branch 15 → 308 not taken.
✓ Branch 16 → 17 taken 1 time.
✓ Branch 16 → 19 taken 2 times.
|
3 | if (!viR.IsRGB()) { |
| 1677 | // delete _Matrix and ChromaLocation when source is not RGB | ||
| 1678 |
1/2✓ Branch 17 → 18 taken 1 time.
✗ Branch 17 → 308 not taken.
|
1 | env->propDeleteKey(props, "_Matrix"); |
| 1679 |
1/2✓ Branch 18 → 19 taken 1 time.
✗ Branch 18 → 308 not taken.
|
1 | env->propDeleteKey(props, "_ChromaLocation"); |
| 1680 | } | ||
| 1681 | |||
| 1682 |
1/2✓ Branch 19 → 20 taken 3 times.
✗ Branch 19 → 308 not taken.
|
3 | const int pixelsize = viR.ComponentSize(); |
| 1683 | |||
| 1684 |
3/4✓ Branch 20 → 21 taken 3 times.
✗ Branch 20 → 308 not taken.
✓ Branch 21 → 22 taken 1 time.
✓ Branch 21 → 216 taken 2 times.
|
3 | if (!vi.IsPlanar()) { |
| 1685 | // target is packed RGB | ||
| 1686 |
1/2✓ Branch 23 → 24 taken 1 time.
✗ Branch 23 → 308 not taken.
|
1 | const int height = dst->GetHeight(); |
| 1687 |
1/2✓ Branch 25 → 26 taken 1 time.
✗ Branch 25 → 308 not taken.
|
1 | const int pitch = dst->GetPitch(); |
| 1688 |
1/2✓ Branch 27 → 28 taken 1 time.
✗ Branch 27 → 308 not taken.
|
1 | const int rowsize = dst->GetRowSize(); |
| 1689 | 1 | const int modulo = pitch - rowsize; | |
| 1690 | |||
| 1691 |
1/2✓ Branch 29 → 30 taken 1 time.
✗ Branch 29 → 308 not taken.
|
1 | BYTE* dstp = dst->GetWritePtr(); |
| 1692 | |||
| 1693 | |||
| 1694 |
4/12✓ Branch 30 → 31 taken 1 time.
✗ Branch 30 → 308 not taken.
✗ Branch 31 → 32 not taken.
✓ Branch 31 → 34 taken 1 time.
✗ Branch 32 → 33 not taken.
✗ Branch 32 → 308 not taken.
✗ Branch 33 → 34 not taken.
✗ Branch 33 → 39 not taken.
✓ Branch 34 → 35 taken 1 time.
✗ Branch 34 → 308 not taken.
✓ Branch 35 → 36 taken 1 time.
✗ Branch 35 → 37 not taken.
|
1 | int planeB = viB.IsPlanar() && viB.IsRGB() ? PLANAR_B : vi.IsRGB() ? 0 : PLANAR_Y; |
| 1695 |
4/12✓ Branch 40 → 41 taken 1 time.
✗ Branch 40 → 308 not taken.
✗ Branch 41 → 42 not taken.
✓ Branch 41 → 44 taken 1 time.
✗ Branch 42 → 43 not taken.
✗ Branch 42 → 308 not taken.
✗ Branch 43 → 44 not taken.
✗ Branch 43 → 49 not taken.
✓ Branch 44 → 45 taken 1 time.
✗ Branch 44 → 308 not taken.
✓ Branch 45 → 46 taken 1 time.
✗ Branch 45 → 47 not taken.
|
1 | int planeG = viG.IsPlanar() && viG.IsRGB() ? PLANAR_G : vi.IsRGB() ? 0 : PLANAR_Y; |
| 1696 |
4/12✓ Branch 50 → 51 taken 1 time.
✗ Branch 50 → 308 not taken.
✗ Branch 51 → 52 not taken.
✓ Branch 51 → 54 taken 1 time.
✗ Branch 52 → 53 not taken.
✗ Branch 52 → 308 not taken.
✗ Branch 53 → 54 not taken.
✗ Branch 53 → 59 not taken.
✓ Branch 54 → 55 taken 1 time.
✗ Branch 54 → 308 not taken.
✓ Branch 55 → 56 taken 1 time.
✗ Branch 55 → 57 not taken.
|
1 | int planeR = viR.IsPlanar() && viR.IsRGB() ? PLANAR_R : vi.IsRGB() ? 0 : PLANAR_Y; |
| 1697 |
4/12✓ Branch 60 → 61 taken 1 time.
✗ Branch 60 → 308 not taken.
✗ Branch 61 → 62 not taken.
✓ Branch 61 → 64 taken 1 time.
✗ Branch 62 → 63 not taken.
✗ Branch 62 → 308 not taken.
✗ Branch 63 → 64 not taken.
✗ Branch 63 → 69 not taken.
✓ Branch 64 → 65 taken 1 time.
✗ Branch 64 → 308 not taken.
✓ Branch 65 → 66 taken 1 time.
✗ Branch 65 → 67 not taken.
|
1 | int planeA = viA.IsPlanar() && viA.IsRGB() ? PLANAR_A : vi.IsRGB() ? 0 : PLANAR_Y; |
| 1698 | |||
| 1699 | // RGB is upside-down, backscan any Planar to match | ||
| 1700 |
3/8✓ Branch 70 → 71 taken 1 time.
✗ Branch 70 → 308 not taken.
✗ Branch 71 → 72 not taken.
✓ Branch 71 → 75 taken 1 time.
✗ Branch 73 → 74 not taken.
✗ Branch 73 → 308 not taken.
✓ Branch 76 → 77 taken 1 time.
✗ Branch 76 → 308 not taken.
|
1 | const int Bpitch = (viB.IsPlanar()) ? -(B->GetPitch(planeB)) : B->GetPitch(); |
| 1701 |
3/8✓ Branch 78 → 79 taken 1 time.
✗ Branch 78 → 308 not taken.
✗ Branch 79 → 80 not taken.
✓ Branch 79 → 83 taken 1 time.
✗ Branch 81 → 82 not taken.
✗ Branch 81 → 308 not taken.
✓ Branch 84 → 85 taken 1 time.
✗ Branch 84 → 308 not taken.
|
1 | const int Gpitch = (viG.IsPlanar()) ? -(G->GetPitch(planeG)) : G->GetPitch(); |
| 1702 |
3/8✓ Branch 86 → 87 taken 1 time.
✗ Branch 86 → 308 not taken.
✗ Branch 87 → 88 not taken.
✓ Branch 87 → 91 taken 1 time.
✗ Branch 89 → 90 not taken.
✗ Branch 89 → 308 not taken.
✓ Branch 92 → 93 taken 1 time.
✗ Branch 92 → 308 not taken.
|
1 | const int Rpitch = (viR.IsPlanar()) ? -(R->GetPitch(planeR)) : R->GetPitch(); |
| 1703 | |||
| 1704 | // Bump any RGB channels, move any YUV channels to last line | ||
| 1705 |
3/6✓ Branch 95 → 96 taken 1 time.
✗ Branch 95 → 308 not taken.
✓ Branch 96 → 97 taken 1 time.
✗ Branch 96 → 308 not taken.
✗ Branch 97 → 98 not taken.
✓ Branch 97 → 99 taken 1 time.
|
1 | const BYTE* Bp = B->GetReadPtr(planeB) + (viB.IsPlanar() ? Bpitch * (1 - height) : 0); |
| 1706 |
3/6✓ Branch 101 → 102 taken 1 time.
✗ Branch 101 → 308 not taken.
✓ Branch 102 → 103 taken 1 time.
✗ Branch 102 → 308 not taken.
✗ Branch 103 → 104 not taken.
✓ Branch 103 → 105 taken 1 time.
|
1 | const BYTE* Gp = G->GetReadPtr(planeG) + (viG.IsPlanar() ? Gpitch * (1 - height) : (1 * pixelsize)); |
| 1707 |
3/6✓ Branch 107 → 108 taken 1 time.
✗ Branch 107 → 308 not taken.
✓ Branch 108 → 109 taken 1 time.
✗ Branch 108 → 308 not taken.
✗ Branch 109 → 110 not taken.
✓ Branch 109 → 111 taken 1 time.
|
1 | const BYTE* Rp = R->GetReadPtr(planeR) + (viR.IsPlanar() ? Rpitch * (1 - height) : (2 * pixelsize)); |
| 1708 | |||
| 1709 | // Adjustment from the end of 1 line to the start of the next | ||
| 1710 |
1/2✓ Branch 113 → 114 taken 1 time.
✗ Branch 113 → 308 not taken.
|
1 | const int Bmodulo = Bpitch - B->GetRowSize(planeB); |
| 1711 |
1/2✓ Branch 115 → 116 taken 1 time.
✗ Branch 115 → 308 not taken.
|
1 | const int Gmodulo = Gpitch - G->GetRowSize(planeG); |
| 1712 |
1/2✓ Branch 117 → 118 taken 1 time.
✗ Branch 117 → 308 not taken.
|
1 | const int Rmodulo = Rpitch - R->GetRowSize(planeR); |
| 1713 | |||
| 1714 | // Number of bytes per pixel (1, 2, 3 or 4 .. 8) | ||
| 1715 |
3/6✓ Branch 118 → 119 taken 1 time.
✗ Branch 118 → 308 not taken.
✗ Branch 119 → 120 not taken.
✓ Branch 119 → 121 taken 1 time.
✓ Branch 121 → 122 taken 1 time.
✗ Branch 121 → 308 not taken.
|
1 | const int Bstride = viB.IsPlanar() ? pixelsize : (viB.BitsPerPixel() >> 3); |
| 1716 |
3/6✓ Branch 123 → 124 taken 1 time.
✗ Branch 123 → 308 not taken.
✗ Branch 124 → 125 not taken.
✓ Branch 124 → 126 taken 1 time.
✓ Branch 126 → 127 taken 1 time.
✗ Branch 126 → 308 not taken.
|
1 | const int Gstride = viG.IsPlanar() ? pixelsize : (viG.BitsPerPixel() >> 3); |
| 1717 |
3/6✓ Branch 128 → 129 taken 1 time.
✗ Branch 128 → 308 not taken.
✗ Branch 129 → 130 not taken.
✓ Branch 129 → 131 taken 1 time.
✓ Branch 131 → 132 taken 1 time.
✗ Branch 131 → 308 not taken.
|
1 | const int Rstride = viR.IsPlanar() ? pixelsize : (viR.BitsPerPixel() >> 3); |
| 1718 | |||
| 1719 | // End of VFB | ||
| 1720 | 1 | BYTE const* yend = dstp + pitch * height; | |
| 1721 | |||
| 1722 |
1/2✗ Branch 134 → 135 not taken.
✓ Branch 134 → 174 taken 1 time.
|
1 | if (alpha) { // ARGB mode |
| 1723 | ✗ | const int Apitch = (viA.IsPlanar()) ? -(A->GetPitch(planeA)) : A->GetPitch(); | |
| 1724 | ✗ | const BYTE* Ap = A->GetReadPtr(planeA) + (viA.IsPlanar() ? Apitch * (1 - height) : (3 * pixelsize)); | |
| 1725 | ✗ | const int Amodulo = Apitch - A->GetRowSize(planeA); | |
| 1726 | ✗ | const int Astride = viA.IsPlanar() ? pixelsize : (viA.BitsPerPixel() >> 3); | |
| 1727 | |||
| 1728 | ✗ | switch (pixelsize) { | |
| 1729 | ✗ | case 1: | |
| 1730 | ✗ | while (dstp < yend) { | |
| 1731 | ✗ | BYTE const* xend = dstp + rowsize; | |
| 1732 | ✗ | while (dstp < xend) { | |
| 1733 | ✗ | *dstp++ = *Bp; Bp += Bstride; | |
| 1734 | ✗ | *dstp++ = *Gp; Gp += Gstride; | |
| 1735 | ✗ | *dstp++ = *Rp; Rp += Rstride; | |
| 1736 | ✗ | *dstp++ = *Ap; Ap += Astride; | |
| 1737 | } | ||
| 1738 | ✗ | dstp += modulo; | |
| 1739 | ✗ | Bp += Bmodulo; | |
| 1740 | ✗ | Gp += Gmodulo; | |
| 1741 | ✗ | Rp += Rmodulo; | |
| 1742 | ✗ | Ap += Amodulo; | |
| 1743 | } | ||
| 1744 | ✗ | break; | |
| 1745 | ✗ | case 2: | |
| 1746 | { | ||
| 1747 | ✗ | uint16_t* dstp16 = reinterpret_cast<uint16_t*>(dstp); | |
| 1748 | ✗ | uint16_t const* yend16 = dstp16 + pitch * height / sizeof(uint16_t); | |
| 1749 | ✗ | while (dstp16 < yend16) { | |
| 1750 | ✗ | uint16_t const* xend16 = dstp16 + rowsize / sizeof(uint16_t); | |
| 1751 | ✗ | while (dstp16 < xend16) { | |
| 1752 | ✗ | *dstp16++ = *reinterpret_cast<const uint16_t*>(Bp); Bp += Bstride; | |
| 1753 | ✗ | *dstp16++ = *reinterpret_cast<const uint16_t*>(Gp); Gp += Gstride; | |
| 1754 | ✗ | *dstp16++ = *reinterpret_cast<const uint16_t*>(Rp); Rp += Rstride; | |
| 1755 | ✗ | *dstp16++ = *reinterpret_cast<const uint16_t*>(Ap); Ap += Astride; | |
| 1756 | } | ||
| 1757 | ✗ | dstp16 += modulo / sizeof(uint16_t); | |
| 1758 | ✗ | Bp += Bmodulo; | |
| 1759 | ✗ | Gp += Gmodulo; | |
| 1760 | ✗ | Rp += Rmodulo; | |
| 1761 | ✗ | Ap += Amodulo; | |
| 1762 | } | ||
| 1763 | } | ||
| 1764 | ✗ | break; | |
| 1765 | ✗ | default: | |
| 1766 | ✗ | env->ThrowError("%s: float pixel type not supported", myname); | |
| 1767 | ✗ | break; | |
| 1768 | } | ||
| 1769 | } | ||
| 1770 |
1/4✗ Branch 174 → 175 not taken.
✓ Branch 174 → 176 taken 1 time.
✗ Branch 175 → 176 not taken.
✗ Branch 175 → 194 not taken.
|
1 | else if (vi.pixel_type == VideoInfo::CS_BGR32 || vi.pixel_type == VideoInfo::CS_BGR64) { // RGB32 mode |
| 1771 |
1/3✓ Branch 176 → 177 taken 1 time.
✗ Branch 176 → 184 not taken.
✗ Branch 176 → 191 not taken.
|
1 | switch (pixelsize) { |
| 1772 | 1 | case 1: | |
| 1773 |
2/2✓ Branch 182 → 178 taken 1 time.
✓ Branch 182 → 183 taken 1 time.
|
2 | while (dstp < yend) { |
| 1774 | 1 | BYTE const* xend = dstp + rowsize; | |
| 1775 |
2/2✓ Branch 180 → 179 taken 4 times.
✓ Branch 180 → 181 taken 1 time.
|
5 | while (dstp < xend) { |
| 1776 | 4 | *dstp++ = *Bp; Bp += Bstride; | |
| 1777 | 4 | *dstp++ = *Gp; Gp += Gstride; | |
| 1778 | 4 | *dstp++ = *Rp; Rp += Rstride; | |
| 1779 | 4 | *dstp++ = 0; // default Alpha is 0! | |
| 1780 | } | ||
| 1781 | 1 | dstp += modulo; | |
| 1782 | 1 | Bp += Bmodulo; | |
| 1783 | 1 | Gp += Gmodulo; | |
| 1784 | 1 | Rp += Rmodulo; | |
| 1785 | } | ||
| 1786 | 1 | break; | |
| 1787 | ✗ | case 2: | |
| 1788 | { | ||
| 1789 | ✗ | uint16_t* dstp16 = reinterpret_cast<uint16_t*>(dstp); | |
| 1790 | ✗ | uint16_t const* yend16 = dstp16 + pitch * height / sizeof(uint16_t); | |
| 1791 | ✗ | while (dstp16 < yend16) { | |
| 1792 | ✗ | uint16_t const* xend16 = dstp16 + rowsize / sizeof(uint16_t); | |
| 1793 | ✗ | while (dstp16 < xend16) { | |
| 1794 | ✗ | *dstp16++ = *reinterpret_cast<const uint16_t*>(Bp); Bp += Bstride; | |
| 1795 | ✗ | *dstp16++ = *reinterpret_cast<const uint16_t*>(Gp); Gp += Gstride; | |
| 1796 | ✗ | *dstp16++ = *reinterpret_cast<const uint16_t*>(Rp); Rp += Rstride; | |
| 1797 | ✗ | *dstp16++ = 0; // default Alpha is 0! | |
| 1798 | } | ||
| 1799 | ✗ | dstp16 += modulo / sizeof(uint16_t); | |
| 1800 | ✗ | Bp += Bmodulo; | |
| 1801 | ✗ | Gp += Gmodulo; | |
| 1802 | ✗ | Rp += Rmodulo; | |
| 1803 | } | ||
| 1804 | } | ||
| 1805 | ✗ | break; | |
| 1806 | ✗ | default: | |
| 1807 | ✗ | env->ThrowError("%s: float pixel type not supported", myname); | |
| 1808 | ✗ | break; | |
| 1809 | } | ||
| 1810 | 1 | } | |
| 1811 | ✗ | else if (vi.pixel_type == VideoInfo::CS_BGR24 || vi.pixel_type == VideoInfo::CS_BGR48) { // RGB24 mode | |
| 1812 | ✗ | switch (pixelsize) { | |
| 1813 | ✗ | case 1: | |
| 1814 | ✗ | while (dstp < yend) { | |
| 1815 | ✗ | BYTE const* xend = dstp + rowsize; | |
| 1816 | ✗ | while (dstp < xend) { | |
| 1817 | ✗ | *dstp++ = *Bp; Bp += Bstride; | |
| 1818 | ✗ | *dstp++ = *Gp; Gp += Gstride; | |
| 1819 | ✗ | *dstp++ = *Rp; Rp += Rstride; | |
| 1820 | } | ||
| 1821 | ✗ | dstp += modulo; | |
| 1822 | ✗ | Bp += Bmodulo; | |
| 1823 | ✗ | Gp += Gmodulo; | |
| 1824 | ✗ | Rp += Rmodulo; | |
| 1825 | } | ||
| 1826 | ✗ | break; | |
| 1827 | ✗ | case 2: | |
| 1828 | { | ||
| 1829 | ✗ | uint16_t* dstp16 = reinterpret_cast<uint16_t*>(dstp); | |
| 1830 | ✗ | uint16_t const* yend16 = dstp16 + pitch * height / sizeof(uint16_t); | |
| 1831 | ✗ | while (dstp16 < yend16) { | |
| 1832 | ✗ | uint16_t const* xend16 = dstp16 + rowsize / sizeof(uint16_t); | |
| 1833 | ✗ | while (dstp16 < xend16) { | |
| 1834 | ✗ | *dstp16++ = *reinterpret_cast<const uint16_t*>(Bp); Bp += Bstride; | |
| 1835 | ✗ | *dstp16++ = *reinterpret_cast<const uint16_t*>(Gp); Gp += Gstride; | |
| 1836 | ✗ | *dstp16++ = *reinterpret_cast<const uint16_t*>(Rp); Rp += Rstride; | |
| 1837 | } | ||
| 1838 | ✗ | dstp16 += modulo / sizeof(uint16_t); | |
| 1839 | ✗ | Bp += Bmodulo; | |
| 1840 | ✗ | Gp += Gmodulo; | |
| 1841 | ✗ | Rp += Rmodulo; | |
| 1842 | } | ||
| 1843 | } | ||
| 1844 | ✗ | break; | |
| 1845 | ✗ | default: | |
| 1846 | ✗ | env->ThrowError("%s: float pixel type not supported", myname); | |
| 1847 | ✗ | break; | |
| 1848 | } | ||
| 1849 | ✗ | } | |
| 1850 | else | ||
| 1851 | ✗ | env->ThrowError("%s: unexpected end of function", myname); | |
| 1852 | |||
| 1853 | 1 | return dst; | |
| 1854 | } | ||
| 1855 | |||
| 1856 | // target is planar RGB | ||
| 1857 |
3/4✓ Branch 298 → 299 taken 10 times.
✗ Branch 298 → 308 not taken.
✓ Branch 299 → 217 taken 8 times.
✓ Branch 299 → 300 taken 2 times.
|
10 | for (int p = 0; p < vi.NumComponents(); p++) { |
| 1858 | 8 | int planes_r[4] = { PLANAR_G, PLANAR_B, PLANAR_R, PLANAR_A }; | |
| 1859 | 8 | const int plane = planes_r[p]; | |
| 1860 | |||
| 1861 | 8 | const VideoInfo& vi_src = | |
| 1862 |
6/6✓ Branch 217 → 218 taken 2 times.
✓ Branch 217 → 219 taken 6 times.
✓ Branch 219 → 220 taken 2 times.
✓ Branch 219 → 221 taken 4 times.
✓ Branch 221 → 222 taken 2 times.
✓ Branch 221 → 223 taken 2 times.
|
8 | plane == PLANAR_G ? viG : |
| 1863 | plane == PLANAR_B ? viB : | ||
| 1864 | plane == PLANAR_R ? viR : viA; | ||
| 1865 | |||
| 1866 |
6/6✓ Branch 224 → 225 taken 2 times.
✓ Branch 224 → 226 taken 6 times.
✓ Branch 226 → 227 taken 2 times.
✓ Branch 226 → 228 taken 4 times.
✓ Branch 228 → 229 taken 2 times.
✓ Branch 228 → 230 taken 2 times.
|
8 | PVideoFrame& src = |
| 1867 | plane == PLANAR_G ? G : | ||
| 1868 | plane == PLANAR_B ? B : | ||
| 1869 | plane == PLANAR_R ? R : A; | ||
| 1870 | |||
| 1871 | // actual plane source is not a packed RGB type | ||
| 1872 | |||
| 1873 |
4/6✓ Branch 231 → 232 taken 2 times.
✓ Branch 231 → 235 taken 6 times.
✗ Branch 233 → 234 not taken.
✓ Branch 233 → 235 taken 2 times.
✗ Branch 236 → 237 not taken.
✓ Branch 236 → 254 taken 8 times.
|
8 | if (p == 3 && !alpha) { |
| 1874 | // fill alpha, but not ARGB mode | ||
| 1875 | |||
| 1876 | ✗ | const int dst_rowsizeA = dst->GetRowSize(PLANAR_A); | |
| 1877 | ✗ | const int dst_pitchA = dst->GetPitch(PLANAR_A); | |
| 1878 | ✗ | BYTE* dstp_a = dst->GetWritePtr(PLANAR_A); | |
| 1879 | ✗ | const int heightA = dst->GetHeight(PLANAR_A); | |
| 1880 | // unlike in other filters, default alpha value here is 0 instead of max transparent 255 | ||
| 1881 | ✗ | switch (vi.ComponentSize()) | |
| 1882 | { | ||
| 1883 | ✗ | case 1: | |
| 1884 | ✗ | fill_plane<uint8_t>(dstp_a, heightA, dst_rowsizeA, dst_pitchA, 0); | |
| 1885 | ✗ | break; | |
| 1886 | ✗ | case 2: | |
| 1887 | ✗ | fill_plane<uint16_t>(dstp_a, heightA, dst_rowsizeA, dst_pitchA, 0); | |
| 1888 | ✗ | break; | |
| 1889 | ✗ | case 4: | |
| 1890 | ✗ | fill_plane<float>(dstp_a, heightA, dst_rowsizeA, dst_pitchA, 0.0f); | |
| 1891 | ✗ | break; | |
| 1892 | } | ||
| 1893 | } | ||
| 1894 | else { | ||
| 1895 |
2/4✓ Branch 254 → 255 taken 8 times.
✗ Branch 254 → 307 not taken.
✓ Branch 255 → 256 taken 8 times.
✗ Branch 255 → 273 not taken.
|
8 | if (vi_src.IsPlanar()) |
| 1896 | { | ||
| 1897 | // plane copy | ||
| 1898 |
2/4✓ Branch 256 → 257 taken 8 times.
✗ Branch 256 → 307 not taken.
✓ Branch 257 → 258 taken 8 times.
✗ Branch 257 → 259 not taken.
|
8 | const int plane_src = vi_src.IsRGB() ? plane : PLANAR_Y; |
| 1899 |
7/14✓ Branch 261 → 262 taken 8 times.
✗ Branch 261 → 307 not taken.
✓ Branch 263 → 264 taken 8 times.
✗ Branch 263 → 307 not taken.
✓ Branch 265 → 266 taken 8 times.
✗ Branch 265 → 307 not taken.
✓ Branch 267 → 268 taken 8 times.
✗ Branch 267 → 307 not taken.
✓ Branch 269 → 270 taken 8 times.
✗ Branch 269 → 307 not taken.
✓ Branch 271 → 272 taken 8 times.
✗ Branch 271 → 307 not taken.
✓ Branch 272 → 297 taken 8 times.
✗ Branch 272 → 307 not taken.
|
8 | env->BitBlt(dst->GetWritePtr(plane), dst->GetPitch(plane), src->GetReadPtr(plane_src), |
| 1900 | src->GetPitch(plane_src), src->GetRowSize(plane_src), src->GetHeight(plane_src)); | ||
| 1901 | } | ||
| 1902 | else { | ||
| 1903 | // fill a plane from packed RGB | ||
| 1904 | // packed RGB -> Y, YUV(A) | ||
| 1905 | ✗ | BYTE* dstp = dst->GetWritePtr(plane); | |
| 1906 | ✗ | int dstpitch = dst->GetPitch(plane); | |
| 1907 | |||
| 1908 | |||
| 1909 | ✗ | const BYTE* srcp = src->GetReadPtr(); | |
| 1910 | ✗ | const int pitch = src->GetPitch(); | |
| 1911 | ✗ | const int packed_rgb_channel = | |
| 1912 | ✗ | plane == PLANAR_G ? 1 : | |
| 1913 | ✗ | plane == PLANAR_B ? 0 : | |
| 1914 | ✗ | plane == PLANAR_R ? 2 : 3; | |
| 1915 | |||
| 1916 | ✗ | if (pixelsize == 1) { | |
| 1917 | ✗ | if (vi_src.NumComponents() == 3) | |
| 1918 | ✗ | packed_to_luma_alpha<uint8_t, false, false>(dstp, nullptr, dstpitch, srcp, pitch, vi.width, vi.height, packed_rgb_channel); | |
| 1919 | else | ||
| 1920 | ✗ | packed_to_luma_alpha<uint8_t, true, false>(dstp, nullptr, dstpitch, srcp, pitch, vi.width, vi.height, packed_rgb_channel); | |
| 1921 | } | ||
| 1922 | else { | ||
| 1923 | ✗ | if (vi_src.NumComponents() == 3) | |
| 1924 | ✗ | packed_to_luma_alpha<uint16_t, false, false>(dstp, nullptr, dstpitch, srcp, pitch, vi.width, vi.height, packed_rgb_channel); | |
| 1925 | else | ||
| 1926 | ✗ | packed_to_luma_alpha<uint16_t, true, false>(dstp, nullptr, dstpitch, srcp, pitch, vi.width, vi.height, packed_rgb_channel); | |
| 1927 | } | ||
| 1928 | } | ||
| 1929 | } | ||
| 1930 | } | ||
| 1931 | 2 | return dst; | |
| 1932 | 3 | } | |
| 1933 | |||
| 1934 | |||
| 1935 | ✗ | AVSValue MergeRGB::Create(AVSValue args, void* mode, IScriptEnvironment* env) | |
| 1936 | { | ||
| 1937 | ✗ | if (mode) // ARGB | |
| 1938 | ✗ | return new MergeRGB(args[0].AsClip(), args[3].AsClip(), args[2].AsClip(), args[1].AsClip(), args[0].AsClip(), args[4].AsString(""), env); | |
| 1939 | else // RGB[type] | ||
| 1940 | ✗ | return new MergeRGB(args[0].AsClip(), args[2].AsClip(), args[1].AsClip(), args[0].AsClip(), 0, args[3].AsString(""), env); | |
| 1941 | } | ||
| 1942 | |||
| 1943 | |||
| 1944 | /******************************* | ||
| 1945 | ******* Layer Filter ****** | ||
| 1946 | *******************************/ | ||
| 1947 | |||
| 1948 | // Packed RGB (RGB24/32/48/64) and YUY2 are pre-converted in Create; this constructor | ||
| 1949 | // receives only planar formats. Post-conversion back to the original format is done in Create. | ||
| 1950 | 15 | Layer::Layer(PClip _child1, PClip _child2, PClip _mask_child, const char _op[], int _lev, int _x, int _y, | |
| 1951 | 15 | int _t, bool _chroma, float _opacity, int _placement, IScriptEnvironment* env) | |
| 1952 |
2/4✓ Branch 4 → 5 taken 15 times.
✗ Branch 4 → 117 not taken.
✓ Branch 5 → 6 taken 15 times.
✗ Branch 5 → 115 not taken.
|
15 | : child1(_child1), child2(_child2), mask_child(_mask_child), Op(_op), levelB(_lev), ofsX(_x), ofsY(_y), |
| 1953 |
1/2✓ Branch 3 → 4 taken 15 times.
✗ Branch 3 → 119 not taken.
|
15 | chroma(_chroma), opacity(_opacity), placement(_placement) |
| 1954 | { | ||
| 1955 |
1/2✓ Branch 7 → 8 taken 15 times.
✗ Branch 7 → 113 not taken.
|
15 | const VideoInfo& vi1 = child1->GetVideoInfo(); |
| 1956 |
1/2✓ Branch 9 → 10 taken 15 times.
✗ Branch 9 → 113 not taken.
|
15 | const VideoInfo& vi2 = child2->GetVideoInfo(); |
| 1957 | |||
| 1958 | // PlanarRGB base + PlanarRGBA overlay is valid: overlay A is the blend weight, base has no alpha. | ||
| 1959 | // All other format mismatches (including bit-depth) remain errors. | ||
| 1960 | const bool rgb_alpha_mismatch = | ||
| 1961 |
2/14✓ Branch 10 → 11 taken 15 times.
✗ Branch 10 → 113 not taken.
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 18 taken 15 times.
✗ Branch 12 → 13 not taken.
✗ Branch 12 → 113 not taken.
✗ Branch 13 → 14 not taken.
✗ Branch 13 → 18 not taken.
✗ Branch 14 → 15 not taken.
✗ Branch 14 → 113 not taken.
✗ Branch 15 → 16 not taken.
✗ Branch 15 → 113 not taken.
✗ Branch 16 → 17 not taken.
✗ Branch 16 → 18 not taken.
|
15 | (vi1.IsPlanarRGB() && vi2.IsPlanarRGBA()) && vi1.BitsPerComponent() == vi2.BitsPerComponent(); |
| 1962 |
2/10✗ Branch 19 → 20 not taken.
✓ Branch 19 → 24 taken 15 times.
✗ Branch 20 → 21 not taken.
✗ Branch 20 → 113 not taken.
✗ Branch 21 → 22 not taken.
✗ Branch 21 → 24 not taken.
✗ Branch 22 → 23 not taken.
✗ Branch 22 → 24 not taken.
✗ Branch 25 → 26 not taken.
✓ Branch 25 → 27 taken 15 times.
|
15 | if (vi1.pixel_type != vi2.pixel_type && !vi1.IsSameColorspace(vi2) && !rgb_alpha_mismatch) // i420 and YV12 are matched OK |
| 1963 | ✗ | env->ThrowError("Layer: image formats don't match"); | |
| 1964 | |||
| 1965 | 15 | vi = vi1; | |
| 1966 | |||
| 1967 | // hasAlpha: overlay supplies a per-pixel blend weight via its alpha plane. | ||
| 1968 | // Derived from overlay (vi2): it is the overlay's alpha that acts as the mask. | ||
| 1969 | // After pre-conversion in Create (when mode is non-"Add"/"Subtract", packed RGB32/64 | ||
| 1970 | // normally appear here as PlanarRGBA (non-native path). | ||
| 1971 | // For the native packed path (Add/Subtract with use_chroma=true), | ||
| 1972 | // vi2 stays as RGB32, we still fill hasAlpha for the level->opacity calc; | ||
| 1973 | // however the packed RGB32 (kept for speed reasons) blend kernel reads alpha directly. | ||
| 1974 | // RGB32 'add' + chroma=true uses magicdiv and opacity in their calc | ||
| 1975 | // Or from a separate mask_child clip. | ||
| 1976 |
12/16✓ Branch 27 → 28 taken 15 times.
✗ Branch 27 → 113 not taken.
✓ Branch 28 → 29 taken 13 times.
✓ Branch 28 → 35 taken 2 times.
✓ Branch 29 → 30 taken 13 times.
✗ Branch 29 → 113 not taken.
✓ Branch 30 → 31 taken 12 times.
✓ Branch 30 → 35 taken 1 time.
✓ Branch 31 → 32 taken 12 times.
✗ Branch 31 → 113 not taken.
✓ Branch 32 → 33 taken 8 times.
✓ Branch 32 → 35 taken 4 times.
✓ Branch 33 → 34 taken 8 times.
✗ Branch 33 → 113 not taken.
✓ Branch 34 → 35 taken 1 time.
✓ Branch 34 → 36 taken 7 times.
|
15 | hasAlpha = vi2.IsYUVA() || vi2.IsPlanarRGBA() || vi2.IsRGB32() || vi2.IsRGB64(); |
| 1977 | |||
| 1978 | // process_alpha_channel: both clips have an alpha plane → blend A exactly like the colour | ||
| 1979 | // channels (same op formula). This reproduces the historical RGB32 behaviour where the | ||
| 1980 | // MMX code applied the blend uniformly across all four packed bytes including alpha. | ||
| 1981 | // Used for planar rgb part, legacy packed rgb32 is working like this by default. | ||
| 1982 |
8/10✓ Branch 37 → 38 taken 8 times.
✓ Branch 37 → 43 taken 7 times.
✓ Branch 38 → 39 taken 8 times.
✗ Branch 38 → 113 not taken.
✓ Branch 39 → 40 taken 6 times.
✓ Branch 39 → 42 taken 2 times.
✓ Branch 40 → 41 taken 6 times.
✗ Branch 40 → 113 not taken.
✓ Branch 41 → 42 taken 1 time.
✓ Branch 41 → 43 taken 5 times.
|
15 | process_alpha_channel = hasAlpha && (vi1.IsYUVA() || vi1.IsPlanarRGBA()); |
| 1983 |
1/2✓ Branch 44 → 45 taken 15 times.
✗ Branch 44 → 113 not taken.
|
15 | bits_per_pixel = vi.BitsPerComponent(); |
| 1984 | |||
| 1985 | 15 | const bool levelSpecified = levelB >= 0; | |
| 1986 | 15 | const bool strengthSpecified = opacity >= 0.0f; | |
| 1987 | |||
| 1988 |
1/4✗ Branch 45 → 46 not taken.
✓ Branch 45 → 48 taken 15 times.
✗ Branch 46 → 47 not taken.
✗ Branch 46 → 48 not taken.
|
15 | if (levelSpecified && strengthSpecified) |
| 1989 | ✗ | env->ThrowError("Layer: cannot specify both level and opacity"); | |
| 1990 |
1/4✗ Branch 48 → 49 not taken.
✓ Branch 48 → 51 taken 15 times.
✗ Branch 49 → 50 not taken.
✗ Branch 49 → 51 not taken.
|
15 | if (levelSpecified && bits_per_pixel == 32) |
| 1991 | ✗ | env->ThrowError("Layer: cannot specify level for 32 bit float format"); | |
| 1992 | |||
| 1993 |
1/2✗ Branch 51 → 52 not taken.
✓ Branch 51 → 55 taken 15 times.
|
15 | if (levelSpecified) |
| 1994 | { | ||
| 1995 | ✗ | if (hasAlpha) | |
| 1996 | ✗ | opacity = (float)levelB / ((1 << bits_per_pixel) + 1); // gives 1.0f for 257 (@8bit) and 65537 (@16 bits) | |
| 1997 | // originally levelB was used in formula: (alpha*level + 1) / range_size, | ||
| 1998 | // now level is calculated from opacity as: level = opacity * ((1 << bits_per_pixel) + 1) | ||
| 1999 | else | ||
| 2000 | ✗ | opacity = (float)levelB / ((1 << bits_per_pixel)); // YUY2 or other non-Alpha, gives 1.0f for 256 (@8bit) | |
| 2001 | // we'll calculate back the level as: level = opacity * ((1 << bits_per_pixel)) | ||
| 2002 | } | ||
| 2003 |
1/2✗ Branch 55 → 56 not taken.
✓ Branch 55 → 57 taken 15 times.
|
15 | else if (!strengthSpecified) |
| 2004 | ✗ | opacity = 1.0f; | |
| 2005 | |||
| 2006 |
1/2✗ Branch 57 → 58 not taken.
✓ Branch 57 → 59 taken 15 times.
|
15 | if (opacity < 0.0f) opacity = 0.0f; |
| 2007 |
1/2✗ Branch 59 → 60 not taken.
✓ Branch 59 → 61 taken 15 times.
|
15 | if (opacity > 1.0f) opacity = 1.0f; |
| 2008 | |||
| 2009 | 15 | constexpr bool snap_offsets_to_chroma = false; | |
| 2010 | // Can be turned on for legacy subsampling-friendly behaviour (pre-3.7.6 Layer snapped the offsets) | ||
| 2011 | // Chroma width calculation is already made ready to accept odd positions. | ||
| 2012 | // "Overlay" does not snap. | ||
| 2013 | if (snap_offsets_to_chroma) { | ||
| 2014 | if ((vi.IsYUV() || vi.IsYUVA()) && !vi.IsY()) { | ||
| 2015 | // make offsets subsampling friendly | ||
| 2016 | ofsX = ofsX & ~((1 << vi.GetPlaneWidthSubsampling(PLANAR_U)) - 1); | ||
| 2017 | ofsY = ofsY & ~((1 << vi.GetPlaneHeightSubsampling(PLANAR_U)) - 1); | ||
| 2018 | } | ||
| 2019 | } | ||
| 2020 | // For the sake of completeness, packed rgb formats still exist, but for most modes they are converted to planar RGB. | ||
| 2021 |
12/18✓ Branch 61 → 62 taken 15 times.
✗ Branch 61 → 113 not taken.
✓ Branch 62 → 63 taken 11 times.
✓ Branch 62 → 69 taken 4 times.
✓ Branch 63 → 64 taken 11 times.
✗ Branch 63 → 113 not taken.
✓ Branch 64 → 65 taken 10 times.
✓ Branch 64 → 69 taken 1 time.
✓ Branch 65 → 66 taken 10 times.
✗ Branch 65 → 113 not taken.
✓ Branch 66 → 67 taken 10 times.
✗ Branch 66 → 69 not taken.
✓ Branch 67 → 68 taken 10 times.
✗ Branch 67 → 113 not taken.
✗ Branch 68 → 69 not taken.
✓ Branch 68 → 70 taken 10 times.
✓ Branch 71 → 72 taken 5 times.
✓ Branch 71 → 73 taken 10 times.
|
15 | if (vi.IsRGB32() || vi.IsRGB64() || vi.IsRGB24() || vi.IsRGB48()) |
| 2022 | 5 | ofsY = vi.height - vi2.height - ofsY; // packed RGB is upside down | |
| 2023 | |||
| 2024 | 15 | xdest = (ofsX < 0) ? 0 : ofsX; | |
| 2025 | 15 | ydest = (ofsY < 0) ? 0 : ofsY; | |
| 2026 | |||
| 2027 |
1/2✗ Branch 73 → 74 not taken.
✓ Branch 73 → 75 taken 15 times.
|
15 | xsrc = (ofsX < 0) ? (0 - ofsX) : 0; |
| 2028 |
1/2✗ Branch 76 → 77 not taken.
✓ Branch 76 → 78 taken 15 times.
|
15 | ysrc = (ofsY < 0) ? (0 - ofsY) : 0; |
| 2029 | |||
| 2030 |
1/2✗ Branch 79 → 80 not taken.
✓ Branch 79 → 81 taken 15 times.
|
15 | xcount = (vi.width < (ofsX + vi2.width)) ? (vi.width - xdest) : (vi2.width - xsrc); |
| 2031 |
1/2✗ Branch 82 → 83 not taken.
✓ Branch 82 → 84 taken 15 times.
|
15 | ycount = (vi.height < (ofsY + vi2.height)) ? (vi.height - ydest) : (vi2.height - ysrc); |
| 2032 | |||
| 2033 |
6/6✓ Branch 85 → 86 taken 13 times.
✓ Branch 85 → 93 taken 2 times.
✓ Branch 86 → 87 taken 8 times.
✓ Branch 86 → 93 taken 5 times.
✓ Branch 87 → 88 taken 7 times.
✓ Branch 87 → 93 taken 1 time.
|
15 | if (!(!lstrcmpi(Op, "Mul") || !lstrcmpi(Op, "Add") || !lstrcmpi(Op, "Fast") || |
| 2034 |
6/6✓ Branch 88 → 89 taken 6 times.
✓ Branch 88 → 93 taken 1 time.
✓ Branch 89 → 90 taken 3 times.
✓ Branch 89 → 93 taken 3 times.
✓ Branch 90 → 91 taken 2 times.
✓ Branch 90 → 93 taken 1 time.
|
7 | !lstrcmpi(Op, "Subtract") || !lstrcmpi(Op, "Lighten") || !lstrcmpi(Op, "Darken") || |
| 2035 |
1/2✗ Branch 91 → 92 not taken.
✓ Branch 91 → 93 taken 2 times.
|
2 | !lstrcmpi(Op, "mulovr"))) |
| 2036 | ✗ | env->ThrowError("Layer supports the following ops: Fast, Lighten, Darken, Add, Subtract, Mul, mulovr"); | |
| 2037 | |||
| 2038 |
8/12✓ Branch 93 → 94 taken 2 times.
✓ Branch 93 → 99 taken 13 times.
✓ Branch 94 → 95 taken 2 times.
✗ Branch 94 → 113 not taken.
✓ Branch 95 → 96 taken 1 time.
✓ Branch 95 → 99 taken 1 time.
✓ Branch 96 → 97 taken 1 time.
✗ Branch 96 → 113 not taken.
✗ Branch 97 → 98 not taken.
✓ Branch 97 → 99 taken 1 time.
✗ Branch 100 → 101 not taken.
✓ Branch 100 → 102 taken 15 times.
|
15 | if (!lstrcmpi(Op, "mulovr") && !vi.IsYUV() && !vi.IsYUVA()) |
| 2039 | ✗ | env->ThrowError("Layer mulovr: YUV(A) formats only"); | |
| 2040 | |||
| 2041 |
2/2✓ Branch 102 → 103 taken 1 time.
✓ Branch 102 → 109 taken 14 times.
|
15 | if (!chroma) |
| 2042 | { | ||
| 2043 |
1/4✗ Branch 103 → 104 not taken.
✓ Branch 103 → 105 taken 1 time.
✗ Branch 104 → 105 not taken.
✗ Branch 104 → 113 not taken.
|
1 | if (!lstrcmpi(Op, "Darken")) env->ThrowError("Layer: monochrome darken illegal op"); |
| 2044 |
1/4✗ Branch 105 → 106 not taken.
✓ Branch 105 → 107 taken 1 time.
✗ Branch 106 → 107 not taken.
✗ Branch 106 → 113 not taken.
|
1 | if (!lstrcmpi(Op, "Lighten")) env->ThrowError("Layer: monochrome lighten illegal op"); |
| 2045 |
1/4✗ Branch 107 → 108 not taken.
✓ Branch 107 → 109 taken 1 time.
✗ Branch 108 → 109 not taken.
✗ Branch 108 → 113 not taken.
|
1 | if (!lstrcmpi(Op, "Fast")) env->ThrowError("Layer: this mode not allowed in FAST; use ADD instead"); |
| 2046 | } | ||
| 2047 | |||
| 2048 | // autoscale ThresholdParam from 8 bit base | ||
| 2049 | // todo check validity | ||
| 2050 |
1/2✗ Branch 109 → 110 not taken.
✓ Branch 109 → 111 taken 15 times.
|
15 | if (bits_per_pixel == 32) |
| 2051 | ✗ | ThresholdParam = _t; // n/a | |
| 2052 | else | ||
| 2053 | 15 | ThresholdParam = _t << (bits_per_pixel - 8); | |
| 2054 | 15 | ThresholdParam_f = _t / 255.0f; | |
| 2055 | |||
| 2056 | 15 | overlay_frames = vi2.num_frames; | |
| 2057 | 15 | } | |
| 2058 | |||
| 2059 | |||
| 2060 | // simple averaging | ||
| 2061 | template<typename pixel_t> | ||
| 2062 | ✗ | static void layer_genericplane_fast_c(BYTE* dstp8, const BYTE* ovrp8, int dst_pitch, int overlay_pitch, int width, int height, int level) { | |
| 2063 | AVS_UNUSED(level); | ||
| 2064 | ✗ | pixel_t* dstp = reinterpret_cast<pixel_t*>(dstp8); | |
| 2065 | ✗ | const pixel_t* ovrp = reinterpret_cast<const pixel_t*>(ovrp8); | |
| 2066 | ✗ | dst_pitch /= sizeof(pixel_t); | |
| 2067 | ✗ | overlay_pitch /= sizeof(pixel_t); | |
| 2068 | |||
| 2069 | ✗ | for (int y = 0; y < height; ++y) { | |
| 2070 | ✗ | for (int x = 0; x < width; ++x) { | |
| 2071 | ✗ | dstp[x] = (dstp[x] + ovrp[x] + 1) / 2; | |
| 2072 | } | ||
| 2073 | ✗ | dstp += dst_pitch; | |
| 2074 | ✗ | ovrp += overlay_pitch; | |
| 2075 | } | ||
| 2076 | ✗ | } | |
| 2077 | |||
| 2078 | ✗ | static void layer_genericplane_fast_f_c(BYTE* dstp8, const BYTE* ovrp8, int dst_pitch, int overlay_pitch, int width, int height, float level) { | |
| 2079 | AVS_UNUSED(level); | ||
| 2080 | ✗ | float* dstp = reinterpret_cast<float*>(dstp8); | |
| 2081 | ✗ | const float* ovrp = reinterpret_cast<const float*>(ovrp8); | |
| 2082 | ✗ | dst_pitch /= sizeof(float); | |
| 2083 | ✗ | overlay_pitch /= sizeof(float); | |
| 2084 | |||
| 2085 | ✗ | for (int y = 0; y < height; ++y) { | |
| 2086 | ✗ | for (int x = 0; x < width; ++x) { | |
| 2087 | ✗ | dstp[x] = (dstp[x] + ovrp[x]) * 0.5f; | |
| 2088 | } | ||
| 2089 | ✗ | dstp += dst_pitch; | |
| 2090 | ✗ | ovrp += overlay_pitch; | |
| 2091 | } | ||
| 2092 | ✗ | } | |
| 2093 | |||
| 2094 | /* RGB32 */ | ||
| 2095 | |||
| 2096 | // For Full Strength: 8 bit Level must be 257, 16 bit must be 65537! | ||
| 2097 | // in 8 bit: (255*257+1)/256 = (65535+1)/256 = 256 -> alpha_max = 256 | ||
| 2098 | // in 16 bit: (65535*65537+1)/65536 = 65536, x=? 7FFFFFFF, x=65537 -> alpha_max = 65536 | ||
| 2099 | |||
| 2100 | template<typename pixel_t> | ||
| 2101 | ✗ | static void layer_rgb32_mul_chroma_c(BYTE* dstp8, const BYTE* ovrp8, int dst_pitch, int overlay_pitch, int width, int height, int level) { | |
| 2102 | ✗ | pixel_t* dstp = reinterpret_cast<pixel_t*>(dstp8); | |
| 2103 | ✗ | const pixel_t* ovrp = reinterpret_cast<const pixel_t*>(ovrp8); | |
| 2104 | ✗ | dst_pitch /= sizeof(pixel_t); | |
| 2105 | ✗ | overlay_pitch /= sizeof(pixel_t); | |
| 2106 | ✗ | const int SHIFT = sizeof(pixel_t) == 1 ? 8 : 16; | |
| 2107 | |||
| 2108 | typedef typename std::conditional < sizeof(pixel_t) == 1, int, int64_t>::type calc_t; | ||
| 2109 | |||
| 2110 | ✗ | for (int y = 0; y < height; ++y) { | |
| 2111 | ✗ | for (int x = 0; x < width; ++x) { | |
| 2112 | ✗ | calc_t alpha = ((calc_t)ovrp[x * 4 + 3] * level + 1) >> SHIFT; | |
| 2113 | |||
| 2114 | ✗ | dstp[x * 4 + 0] = (pixel_t)(dstp[x * 4 + 0] + ((((((calc_t)ovrp[x * 4 + 0] * dstp[x * 4 + 0]) >> SHIFT) - dstp[x * 4 + 0]) * alpha) >> SHIFT)); | |
| 2115 | ✗ | dstp[x * 4 + 1] = (pixel_t)(dstp[x * 4 + 1] + ((((((calc_t)ovrp[x * 4 + 1] * dstp[x * 4 + 1]) >> SHIFT) - dstp[x * 4 + 1]) * alpha) >> SHIFT)); | |
| 2116 | ✗ | dstp[x * 4 + 2] = (pixel_t)(dstp[x * 4 + 2] + ((((((calc_t)ovrp[x * 4 + 2] * dstp[x * 4 + 2]) >> SHIFT) - dstp[x * 4 + 2]) * alpha) >> SHIFT)); | |
| 2117 | ✗ | dstp[x * 4 + 3] = (pixel_t)(dstp[x * 4 + 3] + ((((((calc_t)ovrp[x * 4 + 3] * dstp[x * 4 + 3]) >> SHIFT) - dstp[x * 4 + 3]) * alpha) >> SHIFT)); | |
| 2118 | } | ||
| 2119 | ✗ | dstp += dst_pitch; | |
| 2120 | ✗ | ovrp += overlay_pitch; | |
| 2121 | } | ||
| 2122 | ✗ | } | |
| 2123 | |||
| 2124 | template<typename pixel_t> | ||
| 2125 | ✗ | static void layer_rgb32_mul_c(BYTE* dstp8, const BYTE* ovrp8, int dst_pitch, int overlay_pitch, int width, int height, int level) { | |
| 2126 | ✗ | pixel_t* dstp = reinterpret_cast<pixel_t*>(dstp8); | |
| 2127 | ✗ | const pixel_t* ovrp = reinterpret_cast<const pixel_t*>(ovrp8); | |
| 2128 | ✗ | dst_pitch /= sizeof(pixel_t); | |
| 2129 | ✗ | overlay_pitch /= sizeof(pixel_t); | |
| 2130 | ✗ | const int SHIFT = sizeof(pixel_t) == 1 ? 8 : 16; | |
| 2131 | |||
| 2132 | typedef typename std::conditional < sizeof(pixel_t) == 1, int, int64_t>::type calc_t; | ||
| 2133 | |||
| 2134 | ✗ | for (int y = 0; y < height; ++y) { | |
| 2135 | ✗ | for (int x = 0; x < width; ++x) { | |
| 2136 | ✗ | calc_t alpha = ((calc_t)ovrp[x * 4 + 3] * level + 1) >> SHIFT; | |
| 2137 | ✗ | calc_t luma = (cyb * ovrp[x * 4] + cyg * ovrp[x * 4 + 1] + cyr * ovrp[x * 4 + 2]) >> 15; | |
| 2138 | |||
| 2139 | ✗ | dstp[x * 4 + 0] = (pixel_t)(dstp[x * 4 + 0] + (((((luma * dstp[x * 4 + 0]) >> SHIFT) - dstp[x * 4 + 0]) * alpha) >> SHIFT)); | |
| 2140 | ✗ | dstp[x * 4 + 1] = (pixel_t)(dstp[x * 4 + 1] + (((((luma * dstp[x * 4 + 1]) >> SHIFT) - dstp[x * 4 + 1]) * alpha) >> SHIFT)); | |
| 2141 | ✗ | dstp[x * 4 + 2] = (pixel_t)(dstp[x * 4 + 2] + (((((luma * dstp[x * 4 + 2]) >> SHIFT) - dstp[x * 4 + 2]) * alpha) >> SHIFT)); | |
| 2142 | ✗ | dstp[x * 4 + 3] = (pixel_t)(dstp[x * 4 + 3] + (((((luma * dstp[x * 4 + 3]) >> SHIFT) - dstp[x * 4 + 3]) * alpha) >> SHIFT)); | |
| 2143 | } | ||
| 2144 | ✗ | dstp += dst_pitch; | |
| 2145 | ✗ | ovrp += overlay_pitch; | |
| 2146 | } | ||
| 2147 | ✗ | } | |
| 2148 | |||
| 2149 | |||
| 2150 | template<typename pixel_t> | ||
| 2151 | static void layer_rgb32_add_chroma_c(BYTE* dstp8, const BYTE* ovrp8, int dst_pitch, int overlay_pitch, int width, int height, int level) { | ||
| 2152 | pixel_t* dstp = reinterpret_cast<pixel_t*>(dstp8); | ||
| 2153 | const pixel_t* ovrp = reinterpret_cast<const pixel_t*>(ovrp8); | ||
| 2154 | dst_pitch /= sizeof(pixel_t); | ||
| 2155 | overlay_pitch /= sizeof(pixel_t); | ||
| 2156 | const int SHIFT = sizeof(pixel_t) == 1 ? 8 : 16; | ||
| 2157 | |||
| 2158 | typedef typename std::conditional < sizeof(pixel_t) == 1, int, int64_t>::type calc_t; | ||
| 2159 | |||
| 2160 | constexpr int rounder = sizeof(pixel_t) == 1 ? 128 : 32768; | ||
| 2161 | |||
| 2162 | for (int y = 0; y < height; ++y) { | ||
| 2163 | for (int x = 0; x < width; ++x) { | ||
| 2164 | calc_t alpha = ((calc_t)ovrp[x * 4 + 3] * level + 1) >> SHIFT; | ||
| 2165 | |||
| 2166 | dstp[x * 4] = (pixel_t)(dstp[x * 4] + ((((calc_t)ovrp[x * 4] - dstp[x * 4]) * alpha + rounder) >> SHIFT)); | ||
| 2167 | dstp[x * 4 + 1] = (pixel_t)(dstp[x * 4 + 1] + ((((calc_t)ovrp[x * 4 + 1] - dstp[x * 4 + 1]) * alpha + rounder) >> SHIFT)); | ||
| 2168 | dstp[x * 4 + 2] = (pixel_t)(dstp[x * 4 + 2] + ((((calc_t)ovrp[x * 4 + 2] - dstp[x * 4 + 2]) * alpha + rounder) >> SHIFT)); | ||
| 2169 | dstp[x * 4 + 3] = (pixel_t)(dstp[x * 4 + 3] + ((((calc_t)ovrp[x * 4 + 3] - dstp[x * 4 + 3]) * alpha + rounder) >> SHIFT)); | ||
| 2170 | } | ||
| 2171 | dstp += dst_pitch; | ||
| 2172 | ovrp += overlay_pitch; | ||
| 2173 | } | ||
| 2174 | } | ||
| 2175 | |||
| 2176 | template<typename pixel_t> | ||
| 2177 | ✗ | static void layer_rgb32_add_c(BYTE* dstp8, const BYTE* ovrp8, int dst_pitch, int overlay_pitch, int width, int height, int level) { | |
| 2178 | ✗ | pixel_t* dstp = reinterpret_cast<pixel_t*>(dstp8); | |
| 2179 | ✗ | const pixel_t* ovrp = reinterpret_cast<const pixel_t*>(ovrp8); | |
| 2180 | ✗ | dst_pitch /= sizeof(pixel_t); | |
| 2181 | ✗ | overlay_pitch /= sizeof(pixel_t); | |
| 2182 | ✗ | const int SHIFT = sizeof(pixel_t) == 1 ? 8 : 16; | |
| 2183 | |||
| 2184 | typedef typename std::conditional < sizeof(pixel_t) == 1, int, int64_t>::type calc_t; | ||
| 2185 | |||
| 2186 | ✗ | constexpr int rounder = sizeof(pixel_t) == 1 ? 128 : 32768; | |
| 2187 | |||
| 2188 | ✗ | for (int y = 0; y < height; ++y) { | |
| 2189 | ✗ | for (int x = 0; x < width; ++x) { | |
| 2190 | ✗ | calc_t alpha = ((calc_t)ovrp[x * 4 + 3] * level + 1) >> SHIFT; | |
| 2191 | ✗ | calc_t luma = (cyb * ovrp[x * 4] + cyg * ovrp[x * 4 + 1] + cyr * ovrp[x * 4 + 2]) >> 15; | |
| 2192 | |||
| 2193 | ✗ | dstp[x * 4] = (pixel_t)(dstp[x * 4] + (((luma - dstp[x * 4]) * alpha + rounder) >> SHIFT)); | |
| 2194 | ✗ | dstp[x * 4 + 1] = (pixel_t)(dstp[x * 4 + 1] + (((luma - dstp[x * 4 + 1]) * alpha + rounder) >> SHIFT)); | |
| 2195 | ✗ | dstp[x * 4 + 2] = (pixel_t)(dstp[x * 4 + 2] + (((luma - dstp[x * 4 + 2]) * alpha + rounder) >> SHIFT)); | |
| 2196 | ✗ | dstp[x * 4 + 3] = (pixel_t)(dstp[x * 4 + 3] + (((luma - dstp[x * 4 + 3]) * alpha + rounder) >> SHIFT)); | |
| 2197 | } | ||
| 2198 | ✗ | dstp += dst_pitch; | |
| 2199 | ✗ | ovrp += overlay_pitch; | |
| 2200 | } | ||
| 2201 | ✗ | } | |
| 2202 | |||
| 2203 | |||
| 2204 | |||
| 2205 | template<typename pixel_t> | ||
| 2206 | ✗ | static void layer_rgb32_fast_c(BYTE* dstp, const BYTE* ovrp, int dst_pitch, int overlay_pitch, int width, int height, int level) { | |
| 2207 | ✗ | layer_genericplane_fast_c<pixel_t>(dstp, ovrp, dst_pitch, overlay_pitch, width * 4, height, level); | |
| 2208 | ✗ | } | |
| 2209 | |||
| 2210 | |||
| 2211 | template<typename pixel_t> | ||
| 2212 | static void layer_rgb32_subtract_chroma_c(BYTE* dstp8, const BYTE* ovrp8, int dst_pitch, int overlay_pitch, int width, int height, int level) { | ||
| 2213 | pixel_t* dstp = reinterpret_cast<pixel_t*>(dstp8); | ||
| 2214 | const pixel_t* ovrp = reinterpret_cast<const pixel_t*>(ovrp8); | ||
| 2215 | dst_pitch /= sizeof(pixel_t); | ||
| 2216 | overlay_pitch /= sizeof(pixel_t); | ||
| 2217 | const int SHIFT = sizeof(pixel_t) == 1 ? 8 : 16; | ||
| 2218 | |||
| 2219 | typedef typename std::conditional < sizeof(pixel_t) == 1, int, int64_t>::type calc_t; | ||
| 2220 | |||
| 2221 | const calc_t MAX_PIXEL_VALUE = sizeof(pixel_t) == 1 ? 255 : 65535; | ||
| 2222 | constexpr int rounder = sizeof(pixel_t) == 1 ? 128 : 32768; | ||
| 2223 | |||
| 2224 | for (int y = 0; y < height; ++y) { | ||
| 2225 | for (int x = 0; x < width; ++x) { | ||
| 2226 | calc_t alpha = ((calc_t)ovrp[x * 4 + 3] * level + 1) >> SHIFT; | ||
| 2227 | |||
| 2228 | dstp[x * 4] = (pixel_t)(dstp[x * 4] + (((MAX_PIXEL_VALUE - ovrp[x * 4] - dstp[x * 4]) * alpha + rounder) >> SHIFT)); | ||
| 2229 | dstp[x * 4 + 1] = (pixel_t)(dstp[x * 4 + 1] + (((MAX_PIXEL_VALUE - ovrp[x * 4 + 1] - dstp[x * 4 + 1]) * alpha + rounder) >> SHIFT)); | ||
| 2230 | dstp[x * 4 + 2] = (pixel_t)(dstp[x * 4 + 2] + (((MAX_PIXEL_VALUE - ovrp[x * 4 + 2] - dstp[x * 4 + 2]) * alpha + rounder) >> SHIFT)); | ||
| 2231 | dstp[x * 4 + 3] = (pixel_t)(dstp[x * 4 + 3] + (((MAX_PIXEL_VALUE - ovrp[x * 4 + 3] - dstp[x * 4 + 3]) * alpha + rounder) >> SHIFT)); | ||
| 2232 | } | ||
| 2233 | dstp += dst_pitch; | ||
| 2234 | ovrp += overlay_pitch; | ||
| 2235 | } | ||
| 2236 | } | ||
| 2237 | |||
| 2238 | template<typename pixel_t> | ||
| 2239 | ✗ | static void layer_rgb32_subtract_c(BYTE* dstp8, const BYTE* ovrp8, int dst_pitch, int overlay_pitch, int width, int height, int level) { | |
| 2240 | ✗ | pixel_t* dstp = reinterpret_cast<pixel_t*>(dstp8); | |
| 2241 | ✗ | const pixel_t* ovrp = reinterpret_cast<const pixel_t*>(ovrp8); | |
| 2242 | ✗ | dst_pitch /= sizeof(pixel_t); | |
| 2243 | ✗ | overlay_pitch /= sizeof(pixel_t); | |
| 2244 | ✗ | const int SHIFT = sizeof(pixel_t) == 1 ? 8 : 16; | |
| 2245 | |||
| 2246 | typedef typename std::conditional < sizeof(pixel_t) == 1, int, int64_t>::type calc_t; | ||
| 2247 | |||
| 2248 | ✗ | const calc_t MAX_PIXEL_VALUE = sizeof(pixel_t) == 1 ? 255 : 65535; | |
| 2249 | ✗ | constexpr int rounder = sizeof(pixel_t) == 1 ? 128 : 32768; | |
| 2250 | |||
| 2251 | ✗ | for (int y = 0; y < height; ++y) { | |
| 2252 | ✗ | for (int x = 0; x < width; ++x) { | |
| 2253 | ✗ | calc_t alpha = ((calc_t)ovrp[x * 4 + 3] * level + 1) >> SHIFT; | |
| 2254 | ✗ | calc_t luma = (cyb * (MAX_PIXEL_VALUE - ovrp[x * 4]) + cyg * (MAX_PIXEL_VALUE - ovrp[x * 4 + 1]) + cyr * (MAX_PIXEL_VALUE - ovrp[x * 4 + 2])) >> 15; | |
| 2255 | |||
| 2256 | ✗ | dstp[x * 4] = (pixel_t)(dstp[x * 4] + (((luma - dstp[x * 4]) * alpha + rounder) >> SHIFT)); | |
| 2257 | ✗ | dstp[x * 4 + 1] = (pixel_t)(dstp[x * 4 + 1] + (((luma - dstp[x * 4 + 1]) * alpha + rounder) >> SHIFT)); | |
| 2258 | ✗ | dstp[x * 4 + 2] = (pixel_t)(dstp[x * 4 + 2] + (((luma - dstp[x * 4 + 2]) * alpha + rounder) >> SHIFT)); | |
| 2259 | ✗ | dstp[x * 4 + 3] = (pixel_t)(dstp[x * 4 + 3] + (((luma - dstp[x * 4 + 3]) * alpha + rounder) >> SHIFT)); | |
| 2260 | } | ||
| 2261 | ✗ | dstp += dst_pitch; | |
| 2262 | ✗ | ovrp += overlay_pitch; | |
| 2263 | } | ||
| 2264 | ✗ | } | |
| 2265 | |||
| 2266 | |||
| 2267 | template<int mode, typename pixel_t> | ||
| 2268 | ✗ | static void layer_rgb32_lighten_darken_c(BYTE* dstp8, const BYTE* ovrp8, int dst_pitch, int overlay_pitch, int width, int height, int level, int thresh) { | |
| 2269 | ✗ | pixel_t* dstp = reinterpret_cast<pixel_t*>(dstp8); | |
| 2270 | ✗ | const pixel_t* ovrp = reinterpret_cast<const pixel_t*>(ovrp8); | |
| 2271 | ✗ | dst_pitch /= sizeof(pixel_t); | |
| 2272 | ✗ | overlay_pitch /= sizeof(pixel_t); | |
| 2273 | ✗ | const int SHIFT = sizeof(pixel_t) == 1 ? 8 : 16; | |
| 2274 | |||
| 2275 | typedef typename std::conditional < sizeof(pixel_t) == 1, int, int64_t>::type calc_t; | ||
| 2276 | |||
| 2277 | ✗ | constexpr int rounder = sizeof(pixel_t) == 1 ? 128 : 32768; | |
| 2278 | |||
| 2279 | ✗ | for (int y = 0; y < height; ++y) { | |
| 2280 | ✗ | for (int x = 0; x < width; ++x) { | |
| 2281 | ✗ | calc_t alpha = ((calc_t)ovrp[x * 4 + 3] * level + 1) >> SHIFT; | |
| 2282 | ✗ | int luma_ovr = (cyb * ovrp[x * 4] + cyg * ovrp[x * 4 + 1] + cyr * ovrp[x * 4 + 2]) >> 15; | |
| 2283 | ✗ | int luma_src = (cyb * dstp[x * 4] + cyg * dstp[x * 4 + 1] + cyr * dstp[x * 4 + 2]) >> 15; | |
| 2284 | |||
| 2285 | if constexpr (mode == LIGHTEN) | ||
| 2286 | ✗ | alpha = luma_ovr > luma_src + thresh ? alpha : 0; | |
| 2287 | else // DARKEN | ||
| 2288 | ✗ | alpha = luma_ovr < luma_src - thresh ? alpha : 0; | |
| 2289 | |||
| 2290 | ✗ | dstp[x * 4] = (pixel_t)(dstp[x * 4] + ((((calc_t)ovrp[x * 4] - dstp[x * 4]) * alpha + rounder) >> SHIFT)); | |
| 2291 | ✗ | dstp[x * 4 + 1] = (pixel_t)(dstp[x * 4 + 1] + ((((calc_t)ovrp[x * 4 + 1] - dstp[x * 4 + 1]) * alpha + rounder) >> SHIFT)); | |
| 2292 | ✗ | dstp[x * 4 + 2] = (pixel_t)(dstp[x * 4 + 2] + ((((calc_t)ovrp[x * 4 + 2] - dstp[x * 4 + 2]) * alpha + rounder) >> SHIFT)); | |
| 2293 | ✗ | dstp[x * 4 + 3] = (pixel_t)(dstp[x * 4 + 3] + ((((calc_t)ovrp[x * 4 + 3] - dstp[x * 4 + 3]) * alpha + rounder) >> SHIFT)); | |
| 2294 | } | ||
| 2295 | ✗ | dstp += dst_pitch; | |
| 2296 | ✗ | ovrp += overlay_pitch; | |
| 2297 | } | ||
| 2298 | ✗ | } | |
| 2299 | |||
| 2300 | |||
| 2301 | // chroma placement to mask helpers | ||
| 2302 | // yuv add, subtract, mul, lighten, darken | ||
| 2303 | // included in base and the avx2 source module, to get different optimizations | ||
| 2304 | #include "layer.hpp" | ||
| 2305 | |||
| 2306 | |||
| 2307 | |||
| 2308 | 13 | PVideoFrame __stdcall Layer::GetFrame(int n, IScriptEnvironment* env) | |
| 2309 | { | ||
| 2310 |
1/2✓ Branch 3 → 4 taken 13 times.
✗ Branch 3 → 531 not taken.
|
13 | PVideoFrame src1 = child1->GetFrame(n, env); |
| 2311 | |||
| 2312 |
2/4✓ Branch 4 → 5 taken 13 times.
✗ Branch 4 → 6 not taken.
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 13 times.
|
13 | if (xcount <= 0 || ycount <= 0) return src1; |
| 2313 | |||
| 2314 |
1/2✓ Branch 9 → 10 taken 13 times.
✗ Branch 9 → 499 not taken.
|
13 | PVideoFrame src2 = child2->GetFrame(std::min(n, overlay_frames - 1), env); |
| 2315 | |||
| 2316 |
1/2✓ Branch 10 → 11 taken 13 times.
✗ Branch 10 → 527 not taken.
|
13 | env->MakeWritable(&src1); |
| 2317 | |||
| 2318 |
2/2✓ Branch 11 → 12 taken 8 times.
✓ Branch 11 → 13 taken 5 times.
|
13 | const int mylevel = hasAlpha ? (int)(opacity * ((1 << bits_per_pixel) + 1) + 0.5f) : (int)(opacity * (1 << bits_per_pixel) + 0.5f); |
| 2319 | |||
| 2320 | // For functions using magic-div (/max_val) arithmetic, | ||
| 2321 | // opacity_i in [0..max_pixel_value], same convention as masked_merge. | ||
| 2322 | 13 | const int opacity_i = (int)(opacity * ((1 << bits_per_pixel) - 1) + 0.5f); | |
| 2323 | |||
| 2324 | // The inner-loop functions that consume mylevel use a power-of-two shift (>> bpp) for the | ||
| 2325 | // blend, not division by max_val. With a ÷(max_val+1) = ÷256 (@8bit) divisor, a weight | ||
| 2326 | // of max_val (255) maps to 255/256 — never to 1.0 — so a mask/alpha pixel of 255 cannot | ||
| 2327 | // reach the maximum output value (gives 254 instead of 255). | ||
| 2328 | // The inflated level compensates: the two-step product alpha*level can reach the next | ||
| 2329 | // power-of-two (256 @8bit, 65536 @16bit), restoring exact values at both extremes. | ||
| 2330 | // This fixes both endpoints but leaves intermediate weights slightly asymmetric because | ||
| 2331 | // 255 equal mask steps are mapped through a 256-wide divisor. | ||
| 2332 | // The masked_merge family avoids this by dividing by max_val directly (see blend_common.h). | ||
| 2333 | // Alpha mode: opacity 1.0 → 257 (@8bit), 65537 (@16bit): alpha*257>>8 reaches 256 when alpha=255 | ||
| 2334 | // Non-alpha mode: opacity 1.0 → 256 (@8bit): direct flat weight, >> 8 at full weight gives src | ||
| 2335 | |||
| 2336 |
1/2✓ Branch 14 → 15 taken 13 times.
✗ Branch 14 → 527 not taken.
|
13 | const int pixelsize = vi.ComponentSize(); |
| 2337 | |||
| 2338 | 13 | const int height = ycount; // these may be divided by subsampling factor | |
| 2339 | 13 | const int width = xcount; | |
| 2340 | |||
| 2341 |
8/10✓ Branch 15 → 16 taken 13 times.
✗ Branch 15 → 527 not taken.
✓ Branch 16 → 17 taken 9 times.
✓ Branch 16 → 19 taken 4 times.
✓ Branch 17 → 18 taken 9 times.
✗ Branch 17 → 527 not taken.
✓ Branch 18 → 19 taken 1 time.
✓ Branch 18 → 20 taken 8 times.
✓ Branch 21 → 22 taken 5 times.
✓ Branch 21 → 188 taken 8 times.
|
13 | if (vi.IsRGB32() || vi.IsRGB64()) |
| 2342 | { | ||
| 2343 |
1/2✓ Branch 23 → 24 taken 5 times.
✗ Branch 23 → 527 not taken.
|
5 | const int src1_pitch = src1->GetPitch(); |
| 2344 |
1/2✓ Branch 25 → 26 taken 5 times.
✗ Branch 25 → 527 not taken.
|
5 | const int src2_pitch = src2->GetPitch(); |
| 2345 |
1/2✓ Branch 27 → 28 taken 5 times.
✗ Branch 27 → 527 not taken.
|
5 | BYTE* src1p = src1->GetWritePtr(); |
| 2346 |
1/2✓ Branch 29 → 30 taken 5 times.
✗ Branch 29 → 527 not taken.
|
5 | const BYTE* src2p = src2->GetReadPtr(); |
| 2347 | |||
| 2348 |
1/2✓ Branch 30 → 31 taken 5 times.
✗ Branch 30 → 527 not taken.
|
5 | int rgb_step = vi.BytesFromPixels(1); // 4 or 8 bytes/pixelgroup |
| 2349 | |||
| 2350 | 5 | src1p += (src1_pitch * ydest) + (xdest * rgb_step); | |
| 2351 | 5 | src2p += (src2_pitch * ysrc) + (xsrc * rgb_step); | |
| 2352 | |||
| 2353 | // note: ThresholdParam is not scaled automatically | ||
| 2354 | 5 | int thresh = std::max(0, std::min(ThresholdParam, (1 << bits_per_pixel) - 1)); // limit threshold, old method was: & 0xFF | |
| 2355 | |||
| 2356 | // only "Add" and "Subtract" is live code for RGB32/64, others are preconverted to planar RGBA in Create. | ||
| 2357 | // Kept for reference. | ||
| 2358 | |||
| 2359 | // packed rgb "Mul": dead code, kept for reference | ||
| 2360 |
1/2✗ Branch 33 → 34 not taken.
✓ Branch 33 → 69 taken 5 times.
|
5 | if (!lstrcmpi(Op, "Mul")) |
| 2361 | { | ||
| 2362 | ✗ | if (chroma) | |
| 2363 | { | ||
| 2364 | #ifdef INTEL_INTRINSICS | ||
| 2365 | ✗ | if ((pixelsize == 1) && (env->GetCPUFlags() & CPUF_AVX2)) | |
| 2366 | { | ||
| 2367 | ✗ | layer_rgb32_mul_avx2<true>(src1p, src2p, src1_pitch, src2_pitch, width, height, mylevel); | |
| 2368 | } | ||
| 2369 | ✗ | else if ((pixelsize == 1) && (env->GetCPUFlags() & CPUF_SSE2)) | |
| 2370 | { | ||
| 2371 | ✗ | layer_rgb32_mul_sse2<true>(src1p, src2p, src1_pitch, src2_pitch, width, height, mylevel); | |
| 2372 | } | ||
| 2373 | else | ||
| 2374 | #endif | ||
| 2375 | { | ||
| 2376 | ✗ | if (pixelsize == 1) | |
| 2377 | ✗ | layer_rgb32_mul_chroma_c<uint8_t>(src1p, src2p, src1_pitch, src2_pitch, width, height, mylevel); | |
| 2378 | else | ||
| 2379 | ✗ | layer_rgb32_mul_chroma_c<uint16_t>(src1p, src2p, src1_pitch, src2_pitch, width, height, mylevel); | |
| 2380 | } | ||
| 2381 | } | ||
| 2382 | else // Mul, chroma==false | ||
| 2383 | { | ||
| 2384 | #ifdef INTEL_INTRINSICS | ||
| 2385 | ✗ | if ((pixelsize == 1) && (env->GetCPUFlags() & CPUF_AVX2)) | |
| 2386 | { | ||
| 2387 | ✗ | layer_rgb32_mul_avx2<false>(src1p, src2p, src1_pitch, src2_pitch, width, height, mylevel); | |
| 2388 | } | ||
| 2389 | ✗ | else if ((pixelsize == 1) && (env->GetCPUFlags() & CPUF_SSE2)) | |
| 2390 | { | ||
| 2391 | ✗ | layer_rgb32_mul_sse2<false>(src1p, src2p, src1_pitch, src2_pitch, width, height, mylevel); | |
| 2392 | } | ||
| 2393 | else | ||
| 2394 | #endif | ||
| 2395 | { | ||
| 2396 | ✗ | if (pixelsize == 1) | |
| 2397 | ✗ | layer_rgb32_mul_c<uint8_t>(src1p, src2p, src1_pitch, src2_pitch, width, height, mylevel); | |
| 2398 | else | ||
| 2399 | ✗ | layer_rgb32_mul_c<uint16_t>(src1p, src2p, src1_pitch, src2_pitch, width, height, mylevel); | |
| 2400 | } | ||
| 2401 | } | ||
| 2402 | } | ||
| 2403 |
2/2✓ Branch 69 → 70 taken 2 times.
✓ Branch 69 → 112 taken 3 times.
|
5 | if (!lstrcmpi(Op, "Add")) |
| 2404 | { | ||
| 2405 |
1/2✓ Branch 70 → 71 taken 2 times.
✗ Branch 70 → 95 not taken.
|
2 | if (chroma) |
| 2406 | { | ||
| 2407 | // rgb32 packed rgb "Add" + chroma=true: NOT dead code, kept for speed, too many scripts are using | ||
| 2408 | |||
| 2409 | // Packed RGBA Add/chroma (and pre-inverted Subtract/chroma): | ||
| 2410 | // overlay alpha acts as per-pixel blend weight, or a separate mask | ||
| 2411 | // clip supplies the original alpha when the overlay was pre-inverted. | ||
| 2412 | // Uses magic-div (÷max_val) arithmetic | ||
| 2413 | |||
| 2414 | // mask_child is set for pre-inverted Subtract; nullptr for plain Add. | ||
| 2415 | // The mask clip (ExtractA of original overlay) has 1 channel with 1 byte/pixel | ||
| 2416 | // for RGB32 and 2 bytes/pixel for RGB64; offset by (ysrc, xsrc) as the overlay. | ||
| 2417 | 2 | const BYTE* maskp8 = nullptr; | |
| 2418 | 2 | int maskpitch = 0; | |
| 2419 |
1/2✓ Branch 71 → 72 taken 2 times.
✗ Branch 71 → 506 not taken.
|
2 | PVideoFrame mask_frame; |
| 2420 |
1/2✗ Branch 73 → 74 not taken.
✓ Branch 73 → 84 taken 2 times.
|
2 | if (mask_child) { |
| 2421 | ✗ | mask_frame = mask_child->GetFrame(std::min(n, overlay_frames - 1), env); | |
| 2422 | ✗ | maskpitch = mask_frame->GetPitch(); | |
| 2423 | ✗ | maskp8 = mask_frame->GetReadPtr() + maskpitch * ysrc + xsrc * pixelsize; | |
| 2424 | } | ||
| 2425 | 2 | const bool has_separate_mask = (mask_child != nullptr); | |
| 2426 | |||
| 2427 | layer_packedrgb_blend_c_t* blend_fn; | ||
| 2428 | #ifdef INTEL_INTRINSICS | ||
| 2429 | // for 8+ bits these may return the C reference, but compiled with avx2/sse4.1 arch. | ||
| 2430 |
2/4✓ Branch 85 → 86 taken 2 times.
✗ Branch 85 → 504 not taken.
✓ Branch 86 → 87 taken 2 times.
✗ Branch 86 → 88 not taken.
|
2 | if ((env->GetCPUFlags() & CPUF_AVX2)) |
| 2431 |
1/2✓ Branch 87 → 92 taken 2 times.
✗ Branch 87 → 504 not taken.
|
2 | get_layer_packedrgb_blend_functions_avx2(has_separate_mask, bits_per_pixel, &blend_fn); |
| 2432 | ✗ | else if ((env->GetCPUFlags() & CPUF_SSE4_1)) | |
| 2433 | ✗ | get_layer_packedrgb_blend_functions_sse41(has_separate_mask, bits_per_pixel, &blend_fn); | |
| 2434 | else | ||
| 2435 | #endif | ||
| 2436 | ✗ | get_layer_packedrgb_blend_functions(has_separate_mask, bits_per_pixel, &blend_fn); | |
| 2437 |
1/2✓ Branch 92 → 93 taken 2 times.
✗ Branch 92 → 504 not taken.
|
2 | blend_fn(src1p, src2p, maskp8, src1_pitch, src2_pitch, maskpitch, width, height, opacity_i); |
| 2438 | 2 | } | |
| 2439 | else // Add, chroma == false, Special "Layer" case, not a simple MaskedMerge. | ||
| 2440 | { | ||
| 2441 | #ifdef INTEL_INTRINSICS | ||
| 2442 | ✗ | if ((pixelsize == 1) && (env->GetCPUFlags() & CPUF_AVX2)) | |
| 2443 | { | ||
| 2444 | ✗ | layer_rgb32_add_avx2<false>(src1p, src2p, src1_pitch, src2_pitch, width, height, mylevel); | |
| 2445 | } | ||
| 2446 | ✗ | else if ((pixelsize == 1) && (env->GetCPUFlags() & CPUF_SSE2)) | |
| 2447 | { | ||
| 2448 | ✗ | layer_rgb32_add_sse2<false>(src1p, src2p, src1_pitch, src2_pitch, width, height, mylevel); | |
| 2449 | } | ||
| 2450 | else | ||
| 2451 | #endif | ||
| 2452 | { | ||
| 2453 | ✗ | if (pixelsize == 1) | |
| 2454 | ✗ | layer_rgb32_add_c<uint8_t>(src1p, src2p, src1_pitch, src2_pitch, width, height, mylevel); | |
| 2455 | else | ||
| 2456 | ✗ | layer_rgb32_add_c<uint16_t>(src1p, src2p, src1_pitch, src2_pitch, width, height, mylevel); | |
| 2457 | } | ||
| 2458 | } | ||
| 2459 | } | ||
| 2460 | // packed rgb "Mul": dead code, kept for reference | ||
| 2461 |
2/2✓ Branch 112 → 113 taken 1 time.
✓ Branch 112 → 130 taken 4 times.
|
5 | if (!lstrcmpi(Op, "Lighten")) |
| 2462 | { | ||
| 2463 | // Copy overlay_clip over base_clip in areas where overlay_clip is lighter by threshold. | ||
| 2464 | // only chroma == true | ||
| 2465 | #ifdef INTEL_INTRINSICS | ||
| 2466 |
4/8✓ Branch 113 → 114 taken 1 time.
✗ Branch 113 → 117 not taken.
✓ Branch 114 → 115 taken 1 time.
✗ Branch 114 → 527 not taken.
✓ Branch 115 → 116 taken 1 time.
✗ Branch 115 → 117 not taken.
✓ Branch 118 → 119 taken 1 time.
✗ Branch 118 → 120 not taken.
|
1 | if ((pixelsize == 1) && (env->GetCPUFlags() & CPUF_AVX2)) |
| 2467 | { | ||
| 2468 |
1/2✓ Branch 119 → 130 taken 1 time.
✗ Branch 119 → 527 not taken.
|
1 | layer_rgb32_lighten_darken_avx2<LIGHTEN>(src1p, src2p, src1_pitch, src2_pitch, width, height, mylevel, thresh); |
| 2469 | } | ||
| 2470 | ✗ | else if ((pixelsize == 1) && (env->GetCPUFlags() & CPUF_SSE2)) | |
| 2471 | { | ||
| 2472 | ✗ | layer_rgb32_lighten_darken_sse2<LIGHTEN>(src1p, src2p, src1_pitch, src2_pitch, width, height, mylevel, thresh); | |
| 2473 | } | ||
| 2474 | else | ||
| 2475 | #endif | ||
| 2476 | { | ||
| 2477 | ✗ | if (pixelsize == 1) | |
| 2478 | ✗ | layer_rgb32_lighten_darken_c<LIGHTEN, uint8_t>(src1p, src2p, src1_pitch, src2_pitch, width, height, mylevel, thresh); | |
| 2479 | else | ||
| 2480 | ✗ | layer_rgb32_lighten_darken_c<LIGHTEN, uint16_t>(src1p, src2p, src1_pitch, src2_pitch, width, height, mylevel, thresh); | |
| 2481 | } | ||
| 2482 | } | ||
| 2483 | // packed rgb "Mul": dead code, kept for reference | ||
| 2484 |
2/2✓ Branch 130 → 131 taken 1 time.
✓ Branch 130 → 148 taken 4 times.
|
5 | if (!lstrcmpi(Op, "Darken")) |
| 2485 | { | ||
| 2486 | // Copy overlay_clip over base_clip in areas where overlay_clip is darker by threshold. | ||
| 2487 | // only chroma == true | ||
| 2488 | #ifdef INTEL_INTRINSICS | ||
| 2489 |
4/8✓ Branch 131 → 132 taken 1 time.
✗ Branch 131 → 135 not taken.
✓ Branch 132 → 133 taken 1 time.
✗ Branch 132 → 527 not taken.
✓ Branch 133 → 134 taken 1 time.
✗ Branch 133 → 135 not taken.
✓ Branch 136 → 137 taken 1 time.
✗ Branch 136 → 138 not taken.
|
1 | if ((pixelsize == 1) && (env->GetCPUFlags() & CPUF_AVX2)) |
| 2490 | { | ||
| 2491 |
1/2✓ Branch 137 → 148 taken 1 time.
✗ Branch 137 → 527 not taken.
|
1 | layer_rgb32_lighten_darken_avx2<DARKEN>(src1p, src2p, src1_pitch, src2_pitch, width, height, mylevel, thresh); |
| 2492 | } | ||
| 2493 | ✗ | else if ((pixelsize == 1) && (env->GetCPUFlags() & CPUF_SSE2)) | |
| 2494 | { | ||
| 2495 | ✗ | layer_rgb32_lighten_darken_sse2<DARKEN>(src1p, src2p, src1_pitch, src2_pitch, width, height, mylevel, thresh); | |
| 2496 | } | ||
| 2497 | else | ||
| 2498 | #endif | ||
| 2499 | { | ||
| 2500 | ✗ | if (pixelsize == 1) | |
| 2501 | ✗ | layer_rgb32_lighten_darken_c<DARKEN, uint8_t>(src1p, src2p, src1_pitch, src2_pitch, width, height, mylevel, thresh); | |
| 2502 | else | ||
| 2503 | ✗ | layer_rgb32_lighten_darken_c<DARKEN, uint16_t>(src1p, src2p, src1_pitch, src2_pitch, width, height, mylevel, thresh); | |
| 2504 | } | ||
| 2505 | } | ||
| 2506 |
1/2✗ Branch 148 → 149 not taken.
✓ Branch 148 → 170 taken 5 times.
|
5 | if (!lstrcmpi(Op, "Fast")) |
| 2507 | { | ||
| 2508 | // Like add, but without masking. | ||
| 2509 | // use_chroma must be true; level and threshold are not used. | ||
| 2510 | // The result is simply the average of base_clip and overlay_clip. | ||
| 2511 | // only chroma == true | ||
| 2512 | #ifdef INTEL_INTRINSICS | ||
| 2513 | // yes, alignment check, we can have x offsets | ||
| 2514 | // But this avx2 does not require it | ||
| 2515 | ✗ | if ((pixelsize == 1) && (env->GetCPUFlags() & CPUF_AVX2)) | |
| 2516 | { | ||
| 2517 | ✗ | layer_rgb32_fast_avx2(src1p, src2p, src1_pitch, src2_pitch, width, height, mylevel); | |
| 2518 | } | ||
| 2519 | ✗ | else if ((pixelsize == 1) && (env->GetCPUFlags() & CPUF_SSE2) && IsPtrAligned(src1p, 16) && IsPtrAligned(src2p, 16)) | |
| 2520 | { | ||
| 2521 | ✗ | layer_rgb32_fast_sse2(src1p, src2p, src1_pitch, src2_pitch, width, height, mylevel); | |
| 2522 | } | ||
| 2523 | else | ||
| 2524 | #endif | ||
| 2525 | { | ||
| 2526 | ✗ | if (pixelsize == 1) | |
| 2527 | ✗ | layer_rgb32_fast_c<uint8_t>(src1p, src2p, src1_pitch, src2_pitch, width, height, mylevel); | |
| 2528 | else // rgb64 | ||
| 2529 | ✗ | layer_rgb32_fast_c<uint16_t>(src1p, src2p, src1_pitch, src2_pitch, width, height, mylevel); | |
| 2530 | } | ||
| 2531 | } | ||
| 2532 |
2/2✓ Branch 170 → 171 taken 1 time.
✓ Branch 170 → 496 taken 4 times.
|
5 | if (!lstrcmpi(Op, "Subtract")) |
| 2533 | { | ||
| 2534 | // use_chroma=true Subtract was redirected to Add in Create() with pre-inverted | ||
| 2535 | // overlay and a separate mask clip, so only use_chroma=false reaches here. | ||
| 2536 | // use_chroma=false: flat-weight subtract without alpha mask. | ||
| 2537 | #ifdef INTEL_INTRINSICS | ||
| 2538 |
4/8✓ Branch 171 → 172 taken 1 time.
✗ Branch 171 → 175 not taken.
✓ Branch 172 → 173 taken 1 time.
✗ Branch 172 → 527 not taken.
✓ Branch 173 → 174 taken 1 time.
✗ Branch 173 → 175 not taken.
✓ Branch 176 → 177 taken 1 time.
✗ Branch 176 → 178 not taken.
|
1 | if ((pixelsize == 1) && (env->GetCPUFlags() & CPUF_AVX2)) |
| 2539 | { | ||
| 2540 |
1/2✓ Branch 177 → 496 taken 1 time.
✗ Branch 177 → 527 not taken.
|
1 | layer_rgb32_subtract_avx2<false>(src1p, src2p, src1_pitch, src2_pitch, width, height, mylevel); |
| 2541 | } | ||
| 2542 | ✗ | else if ((pixelsize == 1) && (env->GetCPUFlags() & CPUF_SSE2)) | |
| 2543 | { | ||
| 2544 | ✗ | layer_rgb32_subtract_sse2<false>(src1p, src2p, src1_pitch, src2_pitch, width, height, mylevel); | |
| 2545 | } | ||
| 2546 | else | ||
| 2547 | #endif | ||
| 2548 | { | ||
| 2549 | ✗ | if (pixelsize == 1) | |
| 2550 | ✗ | layer_rgb32_subtract_c<uint8_t>(src1p, src2p, src1_pitch, src2_pitch, width, height, mylevel); | |
| 2551 | else | ||
| 2552 | ✗ | layer_rgb32_subtract_c<uint16_t>(src1p, src2p, src1_pitch, src2_pitch, width, height, mylevel); | |
| 2553 | } | ||
| 2554 | } | ||
| 2555 | } | ||
| 2556 |
8/10✓ Branch 188 → 189 taken 8 times.
✗ Branch 188 → 527 not taken.
✓ Branch 189 → 190 taken 3 times.
✓ Branch 189 → 192 taken 5 times.
✓ Branch 190 → 191 taken 3 times.
✗ Branch 190 → 527 not taken.
✓ Branch 191 → 192 taken 2 times.
✓ Branch 191 → 193 taken 1 time.
✓ Branch 194 → 195 taken 7 times.
✓ Branch 194 → 398 taken 1 time.
|
8 | else if (vi.IsYUV() || vi.IsYUVA()) |
| 2557 | { | ||
| 2558 | // planar YUV(A) source | ||
| 2559 | |||
| 2560 |
2/4✓ Branch 195 → 196 taken 7 times.
✗ Branch 195 → 197 not taken.
✗ Branch 196 → 197 not taken.
✓ Branch 196 → 254 taken 7 times.
|
7 | if (!lstrcmpi(Op, "Lighten") || !lstrcmpi(Op, "Darken")) |
| 2561 | { | ||
| 2562 | ✗ | const bool isLighten = !lstrcmpi(Op, "Lighten"); | |
| 2563 | // only chroma == true | ||
| 2564 | |||
| 2565 | ✗ | const int src1_pitch = src1->GetPitch(PLANAR_Y); | |
| 2566 | ✗ | const int src2_pitch = src2->GetPitch(PLANAR_Y); | |
| 2567 | ✗ | const int src1_pitchUV = src1->GetPitch(PLANAR_U); | |
| 2568 | ✗ | const int src2_pitchUV = src2->GetPitch(PLANAR_U); | |
| 2569 | |||
| 2570 | ✗ | const bool grey = vi.IsY(); | |
| 2571 | |||
| 2572 | ✗ | const int ws = grey ? 1 : vi.GetPlaneWidthSubsampling(PLANAR_U); | |
| 2573 | ✗ | const int hs = grey ? 1 : vi.GetPlaneHeightSubsampling(PLANAR_U); | |
| 2574 | |||
| 2575 | ✗ | BYTE* src1p = src1->GetWritePtr(PLANAR_Y) + src1_pitch * (ydest)+(xdest)*pixelsize; // in-place source and destination | |
| 2576 | ✗ | const BYTE* src2p = src2->GetReadPtr(PLANAR_Y) + src2_pitch * (ysrc)+(xsrc)*pixelsize; // overlay | |
| 2577 | |||
| 2578 | ✗ | BYTE* src1p_u = grey ? nullptr : src1->GetWritePtr(PLANAR_U) + src1_pitchUV * (ydest >> hs) + (xdest >> ws) * pixelsize; | |
| 2579 | ✗ | const BYTE* src2p_u = grey ? nullptr : src2->GetReadPtr(PLANAR_U) + src2_pitchUV * (ysrc >> hs) + (xsrc >> ws) * pixelsize; | |
| 2580 | |||
| 2581 | ✗ | BYTE* src1p_v = grey ? nullptr : src1->GetWritePtr(PLANAR_V) + src1_pitchUV * (ydest >> hs) + (xdest >> ws) * pixelsize; | |
| 2582 | ✗ | const BYTE* src2p_v = grey ? nullptr : src2->GetReadPtr(PLANAR_V) + src2_pitchUV * (ysrc >> hs) + (xsrc >> ws) * pixelsize; | |
| 2583 | |||
| 2584 | // target alpha channel is unaffected | ||
| 2585 | // Until we support providing a separate mask clip, we pass A plane pointer for overlay alpha | ||
| 2586 | ✗ | const BYTE* maskp = hasAlpha ? src2->GetReadPtr(PLANAR_A) + src2_pitch * ysrc + xsrc * pixelsize : nullptr; // overlay alpha | |
| 2587 | ✗ | const int mask_pitch = src2->GetPitch(PLANAR_A); | |
| 2588 | |||
| 2589 | // called only once, for all planes | ||
| 2590 | |||
| 2591 | ✗ | layer_yuv_lighten_darken_c_t* layer_fn = nullptr; | |
| 2592 | ✗ | layer_yuv_lighten_darken_f_c_t* layer_f_fn = nullptr; | |
| 2593 | |||
| 2594 | // fills layer_yuv_lighten_darken_c_t or layer_yuv_lighten_darken_f_c_t depending on video format, and bits_per_pixel. | ||
| 2595 | ✗ | get_layer_yuv_lighten_darken_functions(isLighten, placement, vi, bits_per_pixel, &layer_fn, &layer_f_fn); | |
| 2596 | |||
| 2597 | // ThresholdParam was scaled from the 8 bit world | ||
| 2598 | ✗ | if (layer_fn && bits_per_pixel != 32) { | |
| 2599 | ✗ | layer_fn( | |
| 2600 | src1p, src1p_u, src1p_v, | ||
| 2601 | src2p, src2p_u, src2p_v, maskp, | ||
| 2602 | src1_pitch, src1_pitchUV, | ||
| 2603 | src2_pitch, src2_pitchUV, | ||
| 2604 | mask_pitch, | ||
| 2605 | width, height, opacity_i, ThresholdParam, bits_per_pixel); | ||
| 2606 | } | ||
| 2607 | ✗ | else if (layer_f_fn && bits_per_pixel == 32) { | |
| 2608 | ✗ | layer_f_fn( | |
| 2609 | src1p, src1p_u, src1p_v, | ||
| 2610 | src2p, src2p_u, src2p_v, maskp, | ||
| 2611 | src1_pitch, src1_pitchUV, | ||
| 2612 | src2_pitch, src2_pitchUV, | ||
| 2613 | mask_pitch, | ||
| 2614 | width, height, opacity, ThresholdParam_f); | ||
| 2615 | } | ||
| 2616 | // lighten/darken end | ||
| 2617 | ✗ | } | |
| 2618 |
2/2✓ Branch 254 → 255 taken 2 times.
✓ Branch 254 → 300 taken 5 times.
|
7 | else if (!lstrcmpi(Op, "mulovr")) |
| 2619 | { | ||
| 2620 |
1/2✓ Branch 256 → 257 taken 2 times.
✗ Branch 256 → 508 not taken.
|
2 | const int src1_pitch = src1->GetPitch(PLANAR_Y); |
| 2621 |
1/2✓ Branch 258 → 259 taken 2 times.
✗ Branch 258 → 508 not taken.
|
2 | const int src2_pitch = src2->GetPitch(PLANAR_Y); |
| 2622 |
1/2✓ Branch 260 → 261 taken 2 times.
✗ Branch 260 → 508 not taken.
|
2 | const int src1_pitchUV = src1->GetPitch(PLANAR_U); |
| 2623 | |||
| 2624 |
1/2✓ Branch 261 → 262 taken 2 times.
✗ Branch 261 → 508 not taken.
|
2 | const bool grey = vi.IsY(); |
| 2625 |
2/4✗ Branch 262 → 263 not taken.
✓ Branch 262 → 264 taken 2 times.
✓ Branch 264 → 265 taken 2 times.
✗ Branch 264 → 508 not taken.
|
2 | const int ws = grey ? 1 : vi.GetPlaneWidthSubsampling(PLANAR_U); |
| 2626 |
2/4✗ Branch 266 → 267 not taken.
✓ Branch 266 → 268 taken 2 times.
✓ Branch 268 → 269 taken 2 times.
✗ Branch 268 → 508 not taken.
|
2 | const int hs = grey ? 1 : vi.GetPlaneHeightSubsampling(PLANAR_U); |
| 2627 | |||
| 2628 |
1/2✓ Branch 271 → 272 taken 2 times.
✗ Branch 271 → 508 not taken.
|
2 | BYTE* src1p = src1->GetWritePtr(PLANAR_Y) + src1_pitch * ydest + xdest * pixelsize; |
| 2629 |
1/2✓ Branch 273 → 274 taken 2 times.
✗ Branch 273 → 508 not taken.
|
2 | const BYTE* src2p = src2->GetReadPtr (PLANAR_Y) + src2_pitch * ysrc + xsrc * pixelsize; |
| 2630 |
2/4✗ Branch 274 → 275 not taken.
✓ Branch 274 → 276 taken 2 times.
✓ Branch 277 → 278 taken 2 times.
✗ Branch 277 → 508 not taken.
|
2 | BYTE* src1p_u = grey ? nullptr : src1->GetWritePtr(PLANAR_U) + src1_pitchUV * (ydest >> hs) + (xdest >> ws) * pixelsize; |
| 2631 |
2/4✗ Branch 279 → 280 not taken.
✓ Branch 279 → 281 taken 2 times.
✓ Branch 282 → 283 taken 2 times.
✗ Branch 282 → 508 not taken.
|
2 | BYTE* src1p_v = grey ? nullptr : src1->GetWritePtr(PLANAR_V) + src1_pitchUV * (ydest >> hs) + (xdest >> ws) * pixelsize; |
| 2632 | |||
| 2633 | // mulovr uses only overlay Y; overlay UV is ignored. | ||
| 2634 | // mask: overlay alpha plane (if hasAlpha), luma-resolution, same stride as overlay Y. | ||
| 2635 |
3/4✓ Branch 284 → 285 taken 1 time.
✓ Branch 284 → 288 taken 1 time.
✓ Branch 286 → 287 taken 1 time.
✗ Branch 286 → 508 not taken.
|
2 | const BYTE* maskp = hasAlpha ? src2->GetReadPtr(PLANAR_A) + src2_pitch * ysrc + xsrc * pixelsize : nullptr; |
| 2636 |
3/4✓ Branch 289 → 290 taken 1 time.
✓ Branch 289 → 293 taken 1 time.
✓ Branch 291 → 292 taken 1 time.
✗ Branch 291 → 508 not taken.
|
2 | const int mask_pitch = hasAlpha ? src2->GetPitch(PLANAR_A) : 0; |
| 2637 | |||
| 2638 | 2 | layer_yuv_mulovr_c_t* layer_fn = nullptr; | |
| 2639 | 2 | layer_yuv_mulovr_f_c_t* layer_f_fn = nullptr; | |
| 2640 |
1/2✓ Branch 294 → 295 taken 2 times.
✗ Branch 294 → 508 not taken.
|
2 | get_layer_yuv_mulovr_functions(hasAlpha, placement, vi, bits_per_pixel, &layer_fn, &layer_f_fn); |
| 2641 | |||
| 2642 |
1/2✓ Branch 295 → 296 taken 2 times.
✗ Branch 295 → 297 not taken.
|
2 | if (layer_fn) |
| 2643 |
1/2✓ Branch 296 → 299 taken 2 times.
✗ Branch 296 → 508 not taken.
|
2 | layer_fn(src1p, src1p_u, src1p_v, src2p, maskp, |
| 2644 | src1_pitch, src1_pitchUV, src2_pitch, mask_pitch, | ||
| 2645 | width, height, opacity_i, bits_per_pixel); | ||
| 2646 | ✗ | else if (layer_f_fn) | |
| 2647 | ✗ | layer_f_fn(src1p, src1p_u, src1p_v, src2p, maskp, | |
| 2648 | src1_pitch, src1_pitchUV, src2_pitch, mask_pitch, | ||
| 2649 | width, height, opacity); | ||
| 2650 | } | ||
| 2651 | else { | ||
| 2652 | // not Lighten, Darken, or mulovr — process planes individually. | ||
| 2653 | // Add (Subtract is pre-inverted Add), Fast, Mul will follow | ||
| 2654 |
1/2✓ Branch 300 → 301 taken 5 times.
✗ Branch 300 → 509 not taken.
|
5 | const int maxPlanes = std::min(vi.NumComponents(), 3); // intentionally do not process alpha plane |
| 2655 | 5 | const int planesYUV[4] = { PLANAR_Y, PLANAR_U, PLANAR_V, PLANAR_A }; | |
| 2656 |
2/2✓ Branch 395 → 303 taken 15 times.
✓ Branch 395 → 396 taken 5 times.
|
20 | for (int channel = 0; channel < maxPlanes; channel++) |
| 2657 | { | ||
| 2658 | 15 | const int plane = planesYUV[channel]; | |
| 2659 | |||
| 2660 |
1/2✓ Branch 304 → 305 taken 15 times.
✗ Branch 304 → 515 not taken.
|
15 | const int src1_pitch = src1->GetPitch(plane); |
| 2661 |
1/2✓ Branch 306 → 307 taken 15 times.
✗ Branch 306 → 515 not taken.
|
15 | int src2_pitch = src2->GetPitch(plane); // not const, maybe changed later |
| 2662 | |||
| 2663 |
1/2✓ Branch 307 → 308 taken 15 times.
✗ Branch 307 → 515 not taken.
|
15 | const int ws = vi.GetPlaneWidthSubsampling(plane); |
| 2664 |
1/2✓ Branch 308 → 309 taken 15 times.
✗ Branch 308 → 515 not taken.
|
15 | const int hs = vi.GetPlaneHeightSubsampling(plane); |
| 2665 | |||
| 2666 | // For chroma planes, the processing width/height is reduced by the subsampling factor. | ||
| 2667 | // old formula: assuming that xdest and ydest was already snapped to the chroma grid | ||
| 2668 | //const int currentwidth = width >> ws; | ||
| 2669 | //const int currentheight = height >> hs; | ||
| 2670 | |||
| 2671 | // Ceiling formula: account for odd xdest/ydest so the last partial chroma | ||
| 2672 | // column/row is included when xdest (or ydest) is misaligned to the chroma grid. | ||
| 2673 | 15 | const int ws_mask = (1 << ws) - 1; | |
| 2674 | 15 | const int hs_mask = (1 << hs) - 1; | |
| 2675 |
2/2✓ Branch 309 → 310 taken 6 times.
✓ Branch 309 → 311 taken 9 times.
|
15 | const int currentwidth = (ws > 0) ? ((xdest & ws_mask) + width + ws_mask) >> ws : width; |
| 2676 |
2/2✓ Branch 312 → 313 taken 4 times.
✓ Branch 312 → 314 taken 11 times.
|
15 | const int currentheight = (hs > 0) ? ((ydest & hs_mask) + height + hs_mask) >> hs : height; |
| 2677 | |||
| 2678 |
1/2✓ Branch 316 → 317 taken 15 times.
✗ Branch 316 → 515 not taken.
|
15 | BYTE* src1p = src1->GetWritePtr(plane) + src1_pitch * (ydest >> hs) + (xdest >> ws) * pixelsize; // destination |
| 2679 |
1/2✓ Branch 318 → 319 taken 15 times.
✗ Branch 318 → 515 not taken.
|
15 | const BYTE* src2p = src2->GetReadPtr(plane) + src2_pitch * (ysrc >> hs) + (xsrc >> ws) * pixelsize; // source plane |
| 2680 | |||
| 2681 |
4/6✓ Branch 319 → 320 taken 3 times.
✓ Branch 319 → 325 taken 12 times.
✓ Branch 321 → 322 taken 3 times.
✗ Branch 321 → 515 not taken.
✓ Branch 323 → 324 taken 3 times.
✗ Branch 323 → 515 not taken.
|
15 | const BYTE* maskp = hasAlpha ? src2->GetReadPtr(PLANAR_A) + src2->GetPitch(PLANAR_A) * (ysrc) + (xsrc) * pixelsize : nullptr; // alpha plane from Overlay |
| 2682 |
3/4✓ Branch 326 → 327 taken 3 times.
✓ Branch 326 → 330 taken 12 times.
✓ Branch 328 → 329 taken 3 times.
✗ Branch 328 → 515 not taken.
|
15 | const int mask_pitch = hasAlpha ? src2->GetPitch(PLANAR_A) : 0; |
| 2683 | |||
| 2684 | 15 | const bool is_chroma = plane != PLANAR_Y; | |
| 2685 | |||
| 2686 | // Special "Add" and "Mul" use_chroma == false case for chroma planes | ||
| 2687 | // Overlay chroma data is ignored, blending occurs toward grey (e.g. 128) instead of the chroma source pixel. | ||
| 2688 | // We can "Fake" a chroma scanline buffer of chroma center ("half") by passing a pointer to a local variable, and ignoring the pitch. | ||
| 2689 | // This is a special "Layer" mode, if we pass a prepared overlay plane, MaskedMerge or Weighted Merge is transparently using this | ||
| 2690 | // buffer. | ||
| 2691 | 15 | std::vector <uint8_t> chroma_scanline_buffer; // only used if is_chroma && !chroma | |
| 2692 |
4/4✓ Branch 332 → 333 taken 6 times.
✓ Branch 332 → 334 taken 9 times.
✓ Branch 333 → 334 taken 3 times.
✓ Branch 333 → 359 taken 3 times.
|
15 | if (!lstrcmpi(Op, "Add") || !lstrcmpi(Op, "Mul")) { |
| 2693 |
3/4✓ Branch 334 → 335 taken 8 times.
✓ Branch 334 → 359 taken 4 times.
✗ Branch 335 → 336 not taken.
✓ Branch 335 → 359 taken 8 times.
|
12 | if (is_chroma && !chroma) { |
| 2694 | ✗ | chroma_scanline_buffer.resize(pixelsize * currentwidth); | |
| 2695 | ✗ | if (bits_per_pixel == 8) | |
| 2696 | ✗ | std::fill((uint8_t *)chroma_scanline_buffer.data(), (uint8_t *)chroma_scanline_buffer.data() + chroma_scanline_buffer.size(), 128); | |
| 2697 | ✗ | else if (bits_per_pixel <= 16) | |
| 2698 | ✗ | std::fill((uint16_t *)chroma_scanline_buffer.data(), (uint16_t *)chroma_scanline_buffer.data() + chroma_scanline_buffer.size() / sizeof(uint16_t), 1 << (bits_per_pixel - 1)); | |
| 2699 | else // float | ||
| 2700 | ✗ | std::fill((float*)chroma_scanline_buffer.data(), (float*)chroma_scanline_buffer.data() + chroma_scanline_buffer.size() / sizeof(float), 0.0f); // chroma center: 0.0f | |
| 2701 | ✗ | src2p = chroma_scanline_buffer.data(); | |
| 2702 | ✗ | src2_pitch = 0; // pitch is 0, so this prepared scanline is used for every line of the chroma plane. | |
| 2703 | } | ||
| 2704 | } | ||
| 2705 | // preparation end | ||
| 2706 | |||
| 2707 |
2/2✓ Branch 359 → 360 taken 3 times.
✓ Branch 359 → 372 taken 12 times.
|
15 | if (!lstrcmpi(Op, "Mul")) |
| 2708 | { | ||
| 2709 | 3 | layer_yuv_mul_c_t* layer_fn = nullptr; | |
| 2710 | 3 | layer_yuv_mul_f_c_t* layer_f_fn = nullptr; | |
| 2711 | |||
| 2712 | #ifdef INTEL_INTRINSICS | ||
| 2713 |
2/4✓ Branch 360 → 361 taken 3 times.
✗ Branch 360 → 511 not taken.
✓ Branch 361 → 362 taken 3 times.
✗ Branch 361 → 363 not taken.
|
3 | if (env->GetCPUFlags() & CPUF_AVX2) { |
| 2714 | // no SIMD yet, wrapper for the avx2 module, which calls its local scoped get_layer_yuv_mul_functions the included layer.hpp | ||
| 2715 |
1/2✓ Branch 362 → 364 taken 3 times.
✗ Branch 362 → 511 not taken.
|
3 | get_layer_yuv_mul_functions_avx2(is_chroma, hasAlpha, placement, vi, bits_per_pixel, &layer_fn, &layer_f_fn); |
| 2716 | } | ||
| 2717 | else | ||
| 2718 | #endif | ||
| 2719 | ✗ | get_layer_yuv_mul_functions(is_chroma, hasAlpha, placement, vi, bits_per_pixel, &layer_fn, &layer_f_fn); | |
| 2720 | |||
| 2721 |
2/4✓ Branch 364 → 365 taken 3 times.
✗ Branch 364 → 368 not taken.
✓ Branch 365 → 366 taken 3 times.
✗ Branch 365 → 368 not taken.
|
3 | if (layer_fn && bits_per_pixel != 32) { |
| 2722 |
1/2✓ Branch 366 → 367 taken 3 times.
✗ Branch 366 → 511 not taken.
|
3 | layer_fn(src1p, src2p, maskp, src1_pitch, src2_pitch, mask_pitch, |
| 2723 | currentwidth, currentheight, opacity_i, bits_per_pixel); | ||
| 2724 | } | ||
| 2725 | ✗ | else if (layer_f_fn && bits_per_pixel == 32) { | |
| 2726 | ✗ | layer_f_fn(src1p, src2p, maskp, src1_pitch, src2_pitch, mask_pitch, | |
| 2727 | currentwidth, currentheight, opacity); | ||
| 2728 | } | ||
| 2729 | } | ||
| 2730 |
3/4✓ Branch 372 → 373 taken 3 times.
✓ Branch 372 → 374 taken 9 times.
✓ Branch 373 → 374 taken 3 times.
✗ Branch 373 → 393 not taken.
|
12 | else if (!lstrcmpi(Op, "Add") || !lstrcmpi(Op, "Fast")) |
| 2731 | { | ||
| 2732 | // Note: "Subtract" is handled by pre-inverting the overlay in Layer::Create | ||
| 2733 | // for planar YUV(A) formats. GetFrame never sees Op="Subtract" for these. | ||
| 2734 | 12 | const bool is_fast = !lstrcmpi(Op, "Fast"); | |
| 2735 |
2/2✓ Branch 374 → 375 taken 3 times.
✓ Branch 374 → 376 taken 9 times.
|
12 | if (is_fast) { |
| 2736 | // "Fast" is weighted merge with exact 0.5 weight, and it is optimized inside the called merge function | ||
| 2737 | 3 | const bool use_padded_width = false; // exact width | |
| 2738 | // in merge.cpp/h | ||
| 2739 |
1/2✓ Branch 375 → 393 taken 3 times.
✗ Branch 375 → 513 not taken.
|
3 | merge_plane(src1p, src2p, src1_pitch, src2_pitch, currentwidth * pixelsize /*src_rowsize*/, currentheight, 0.5f, bits_per_pixel, use_padded_width, env); |
| 2740 | } | ||
| 2741 |
2/2✓ Branch 376 → 377 taken 6 times.
✓ Branch 376 → 378 taken 3 times.
|
9 | else if (!hasAlpha) { |
| 2742 | // check for weighted merge: this call is automatically dispatches to the proper bit_depth, CPU opt | ||
| 2743 | 6 | const bool use_padded_width = false; // exact width | |
| 2744 | // in merge.cpp/h | ||
| 2745 |
1/2✓ Branch 377 → 393 taken 6 times.
✗ Branch 377 → 513 not taken.
|
6 | merge_plane(src1p, src2p, src1_pitch, src2_pitch, currentwidth * pixelsize /*src_rowsize*/, currentheight, opacity, bits_per_pixel, use_padded_width, env); |
| 2746 | } | ||
| 2747 | else { | ||
| 2748 | // masked merge, use the unified masked blend functions | ||
| 2749 | 3 | layer_yuv_add_c_t* layer_fn = nullptr; | |
| 2750 | 3 | layer_yuv_add_f_c_t* layer_f_fn = nullptr; | |
| 2751 | |||
| 2752 | #ifdef INTEL_INTRINSICS | ||
| 2753 |
2/4✓ Branch 378 → 379 taken 3 times.
✗ Branch 378 → 512 not taken.
✓ Branch 379 → 380 taken 3 times.
✗ Branch 379 → 381 not taken.
|
3 | if (env->GetCPUFlags() & CPUF_AVX2) { |
| 2754 | // AVX2 masked merge with chroma placement support | ||
| 2755 |
1/2✓ Branch 380 → 385 taken 3 times.
✗ Branch 380 → 512 not taken.
|
3 | get_layer_yuv_masked_add_functions_avx2(is_chroma, placement, vi, bits_per_pixel, &layer_fn, &layer_f_fn); |
| 2756 | } | ||
| 2757 | ✗ | else if (env->GetCPUFlags() & CPUF_SSE4_1) { | |
| 2758 | // SSE4.1 masked merge with chroma placement support | ||
| 2759 | ✗ | get_layer_yuv_masked_add_functions_sse41(is_chroma, placement, vi, bits_per_pixel, &layer_fn, &layer_f_fn); | |
| 2760 | } | ||
| 2761 | else | ||
| 2762 | #elif defined(NEON_INTRINSICS) | ||
| 2763 | if (env->GetCPUFlags() & CPUF_ARM_NEON) | ||
| 2764 | get_layer_yuv_masked_add_functions_neon(is_chroma, placement, vi, bits_per_pixel, &layer_fn, &layer_f_fn); | ||
| 2765 | else | ||
| 2766 | #endif | ||
| 2767 | { | ||
| 2768 | ✗ | get_layer_yuv_add_masked_functions(is_chroma, hasAlpha, placement, vi, bits_per_pixel, &layer_fn, &layer_f_fn); | |
| 2769 | } | ||
| 2770 | |||
| 2771 |
2/4✓ Branch 385 → 386 taken 3 times.
✗ Branch 385 → 389 not taken.
✓ Branch 386 → 387 taken 3 times.
✗ Branch 386 → 389 not taken.
|
3 | if (layer_fn && bits_per_pixel != 32) { |
| 2772 |
1/2✓ Branch 387 → 388 taken 3 times.
✗ Branch 387 → 512 not taken.
|
3 | layer_fn(src1p, src2p, maskp, src1_pitch, src2_pitch, mask_pitch, |
| 2773 | currentwidth, currentheight, opacity_i, bits_per_pixel); | ||
| 2774 | } | ||
| 2775 | ✗ | else if (layer_f_fn && bits_per_pixel == 32) { | |
| 2776 | ✗ | layer_f_fn(src1p, src2p, maskp, src1_pitch, src2_pitch, mask_pitch, | |
| 2777 | currentwidth, currentheight, opacity); | ||
| 2778 | } | ||
| 2779 | } | ||
| 2780 | } | ||
| 2781 | 15 | } // for one channel | |
| 2782 | } // if lighten/darken else | ||
| 2783 | |||
| 2784 | } | ||
| 2785 |
5/10✓ Branch 398 → 399 taken 1 time.
✗ Branch 398 → 527 not taken.
✓ Branch 399 → 400 taken 1 time.
✗ Branch 399 → 402 not taken.
✓ Branch 400 → 401 taken 1 time.
✗ Branch 400 → 527 not taken.
✓ Branch 401 → 402 taken 1 time.
✗ Branch 401 → 403 not taken.
✓ Branch 404 → 405 taken 1 time.
✗ Branch 404 → 496 not taken.
|
1 | else if (vi.IsPlanarRGB() || vi.IsPlanarRGBA()) |
| 2786 | { | ||
| 2787 | BYTE* dstp[4]; | ||
| 2788 | const BYTE* ovrp[4]; | ||
| 2789 |
1/2✓ Branch 406 → 407 taken 1 time.
✗ Branch 406 → 526 not taken.
|
1 | const int dstp_pitch = src1->GetPitch(PLANAR_G); // same for all |
| 2790 |
1/2✓ Branch 408 → 409 taken 1 time.
✗ Branch 408 → 526 not taken.
|
1 | const int ovrp_pitch = src2->GetPitch(PLANAR_G); // same for all |
| 2791 | |||
| 2792 |
1/2✓ Branch 409 → 410 taken 1 time.
✗ Branch 409 → 526 not taken.
|
1 | const int maxPlanes = vi.NumComponents(); |
| 2793 | 1 | const int planesRGB[4] = { PLANAR_G, PLANAR_B, PLANAR_R, PLANAR_A }; | |
| 2794 |
2/2✓ Branch 416 → 411 taken 4 times.
✓ Branch 416 → 417 taken 1 time.
|
5 | for (int channel = 0; channel < maxPlanes; channel++) |
| 2795 | { | ||
| 2796 | 4 | const int plane = planesRGB[channel]; | |
| 2797 |
1/2✓ Branch 412 → 413 taken 4 times.
✗ Branch 412 → 526 not taken.
|
4 | dstp[channel] = src1->GetWritePtr(plane) + dstp_pitch * ydest + xdest * pixelsize; // destination |
| 2798 |
1/2✓ Branch 414 → 415 taken 4 times.
✗ Branch 414 → 526 not taken.
|
4 | ovrp[channel] = src2->GetReadPtr(plane) + ovrp_pitch * ysrc + xsrc * pixelsize; // overlay |
| 2799 | } | ||
| 2800 | |||
| 2801 | // Per-pixel blend weight (maskp): normally the overlay's A plane. | ||
| 2802 | // For Subtract, mask_child holds ExtractA of the pre-Invert overlay (original A = weight); | ||
| 2803 | // ovrp[3] then points to the inverted A, used only as the alpha blend target when | ||
| 2804 | // process_alpha_channel=true. For all other ops mask_child is nullptr. | ||
| 2805 |
1/2✓ Branch 417 → 418 taken 1 time.
✗ Branch 417 → 526 not taken.
|
1 | PVideoFrame mask_frame; |
| 2806 | 1 | const BYTE* maskp = nullptr; | |
| 2807 | 1 | int mask_pitch = 0; | |
| 2808 |
1/2✓ Branch 418 → 419 taken 1 time.
✗ Branch 418 → 436 not taken.
|
1 | if (hasAlpha) { |
| 2809 |
1/2✗ Branch 420 → 421 not taken.
✓ Branch 420 → 433 taken 1 time.
|
1 | if (mask_child) { |
| 2810 | ✗ | mask_frame = mask_child->GetFrame(std::min(n, overlay_frames - 1), env); | |
| 2811 | ✗ | maskp = mask_frame->GetReadPtr(PLANAR_Y) + mask_frame->GetPitch(PLANAR_Y) * ysrc + xsrc * pixelsize; | |
| 2812 | ✗ | mask_pitch = mask_frame->GetPitch(PLANAR_Y); | |
| 2813 | } | ||
| 2814 | else { | ||
| 2815 | // Direct add/mul/lighten/darken: overlay A plane is the blend weight. | ||
| 2816 |
1/2✓ Branch 434 → 435 taken 1 time.
✗ Branch 434 → 524 not taken.
|
1 | maskp = src2->GetReadPtr(PLANAR_A) + ovrp_pitch * ysrc + xsrc * pixelsize; |
| 2817 | 1 | mask_pitch = ovrp_pitch; | |
| 2818 | } | ||
| 2819 | } | ||
| 2820 | |||
| 2821 |
1/2✓ Branch 436 → 437 taken 1 time.
✗ Branch 436 → 449 not taken.
|
1 | if (!lstrcmpi(Op, "Mul")) |
| 2822 | { | ||
| 2823 | // called only once, for all planes | ||
| 2824 | 1 | layer_planarrgb_mul_c_t* layer_fn = nullptr; | |
| 2825 | 1 | layer_planarrgb_mul_f_c_t* layer_f_fn = nullptr; | |
| 2826 | |||
| 2827 | #ifdef INTEL_INTRINSICS | ||
| 2828 |
2/4✓ Branch 437 → 438 taken 1 time.
✗ Branch 437 → 521 not taken.
✓ Branch 438 → 439 taken 1 time.
✗ Branch 438 → 440 not taken.
|
1 | if (env->GetCPUFlags() & CPUF_AVX2) { |
| 2829 |
1/2✓ Branch 439 → 441 taken 1 time.
✗ Branch 439 → 521 not taken.
|
1 | get_layer_planarrgb_mul_functions_avx2(chroma, hasAlpha, process_alpha_channel, bits_per_pixel, &layer_fn, &layer_f_fn); |
| 2830 | } | ||
| 2831 | else | ||
| 2832 | #endif | ||
| 2833 | { | ||
| 2834 | ✗ | get_layer_planarrgb_mul_functions(chroma, hasAlpha, process_alpha_channel, bits_per_pixel, &layer_fn, &layer_f_fn); | |
| 2835 | } | ||
| 2836 | |||
| 2837 |
2/4✓ Branch 441 → 442 taken 1 time.
✗ Branch 441 → 445 not taken.
✓ Branch 442 → 443 taken 1 time.
✗ Branch 442 → 445 not taken.
|
1 | if (layer_fn && bits_per_pixel != 32) |
| 2838 |
1/2✓ Branch 443 → 444 taken 1 time.
✗ Branch 443 → 521 not taken.
|
1 | layer_fn(dstp, ovrp, maskp, dstp_pitch, ovrp_pitch, mask_pitch, width, height, opacity_i, bits_per_pixel); |
| 2839 | ✗ | else if (layer_f_fn && bits_per_pixel == 32) | |
| 2840 | ✗ | layer_f_fn(dstp, ovrp, maskp, dstp_pitch, ovrp_pitch, mask_pitch, width, height, opacity); | |
| 2841 | // planar_rgba mul end | ||
| 2842 | } | ||
| 2843 | ✗ | else if (!lstrcmpi(Op, "Add") || !lstrcmpi(Op, "Fast")) | |
| 2844 | { | ||
| 2845 | // no subsampling | ||
| 2846 | // use the unified weigthed blend or masked blend function depending on the presence of alpha, | ||
| 2847 | // for both Add and Fast (Fast is just a special case of weighted blend with fixed 0.5 weight, and may be optimized internally in the called function) | ||
| 2848 | ✗ | const int currentwidth = width; | |
| 2849 | ✗ | const int currentheight = height; | |
| 2850 | // Note: "Subtract" is handled by pre-inverting the overlay in Layer::Create | ||
| 2851 | // for planar YUV(A) formats. GetFrame never sees Op="Subtract" for these. | ||
| 2852 | ✗ | const bool is_fast = !lstrcmpi(Op, "Fast"); | |
| 2853 | ✗ | if (is_fast || !hasAlpha) { | |
| 2854 | ✗ | const float actual_opacity = is_fast ? 0.5f : opacity; // "Add" with opacity=0.5 is the same as "Fast", but "Fast" is optimized for this case, and may be faster than a general weighted merge. | |
| 2855 | // "Fast" is weighted merge with exact 0.5 weight, and it is optimized inside the called merge function | ||
| 2856 | ✗ | const bool use_padded_width = false; // exact width | |
| 2857 | |||
| 2858 | ✗ | for (int channel = 0; channel < maxPlanes; channel++) | |
| 2859 | { | ||
| 2860 | ✗ | uint8_t *src1p = dstp[channel]; | |
| 2861 | ✗ | const uint8_t *src2p = ovrp[channel]; | |
| 2862 | ✗ | int src1_pitch = dstp_pitch; | |
| 2863 | ✗ | int src2_pitch = ovrp_pitch; | |
| 2864 | // in merge.cpp/h | ||
| 2865 | ✗ | merge_plane(src1p, src2p, src1_pitch, src2_pitch, currentwidth * pixelsize /*src_rowsize*/, currentheight, actual_opacity, bits_per_pixel, use_padded_width, env); | |
| 2866 | } | ||
| 2867 | ✗ | } | |
| 2868 | else { | ||
| 2869 | // masked merge, use the unified masked blend functions | ||
| 2870 | ✗ | layer_yuv_add_c_t* layer_fn = nullptr; | |
| 2871 | ✗ | layer_yuv_add_f_c_t* layer_f_fn = nullptr; | |
| 2872 | |||
| 2873 | ✗ | const bool effective_is_chroma = false; // rgb: all planes are treated as luma for blending purposes | |
| 2874 | // placement is n/a | ||
| 2875 | |||
| 2876 | #ifdef INTEL_INTRINSICS | ||
| 2877 | ✗ | if (env->GetCPUFlags() & CPUF_AVX2) { | |
| 2878 | // AVX2 masked merge with chroma placement support | ||
| 2879 | ✗ | get_layer_yuv_masked_add_functions_avx2(effective_is_chroma, placement, vi, bits_per_pixel, &layer_fn, &layer_f_fn); | |
| 2880 | } | ||
| 2881 | ✗ | else if (env->GetCPUFlags() & CPUF_SSE4_1) { | |
| 2882 | // SSE4.1 masked merge with chroma placement support | ||
| 2883 | ✗ | get_layer_yuv_masked_add_functions_sse41(effective_is_chroma, placement, vi, bits_per_pixel, &layer_fn, &layer_f_fn); | |
| 2884 | } | ||
| 2885 | else | ||
| 2886 | #elif defined(NEON_INTRINSICS) | ||
| 2887 | if (env->GetCPUFlags() & CPUF_ARM_NEON) | ||
| 2888 | get_layer_yuv_masked_add_functions_neon(effective_is_chroma, placement, vi, bits_per_pixel, &layer_fn, &layer_f_fn); | ||
| 2889 | else | ||
| 2890 | #endif | ||
| 2891 | { | ||
| 2892 | ✗ | get_layer_yuv_add_masked_functions(effective_is_chroma, hasAlpha, placement, vi, bits_per_pixel, &layer_fn, &layer_f_fn); | |
| 2893 | } | ||
| 2894 | |||
| 2895 | ✗ | for (int channel = 0; channel < maxPlanes; channel++) | |
| 2896 | { | ||
| 2897 | ✗ | uint8_t* src1p = dstp[channel]; | |
| 2898 | ✗ | const uint8_t* src2p = ovrp[channel]; | |
| 2899 | ✗ | int src1_pitch = dstp_pitch; | |
| 2900 | ✗ | int src2_pitch = ovrp_pitch; | |
| 2901 | |||
| 2902 | ✗ | if (layer_fn && bits_per_pixel != 32) { | |
| 2903 | ✗ | layer_fn(src1p, src2p, maskp, src1_pitch, src2_pitch, mask_pitch, | |
| 2904 | currentwidth, currentheight, opacity_i, bits_per_pixel); | ||
| 2905 | } | ||
| 2906 | ✗ | else if (layer_f_fn && bits_per_pixel == 32) { | |
| 2907 | ✗ | layer_f_fn(src1p, src2p, maskp, src1_pitch, src2_pitch, mask_pitch, | |
| 2908 | currentwidth, currentheight, opacity); | ||
| 2909 | } | ||
| 2910 | } | ||
| 2911 | } | ||
| 2912 | ✗ | } // Add/Fast | |
| 2913 | ✗ | else if (!lstrcmpi(Op, "Lighten") || !lstrcmpi(Op, "Darken")) | |
| 2914 | { | ||
| 2915 | ✗ | const bool isLighten = !lstrcmpi(Op, "Lighten"); | |
| 2916 | // only chroma == true | ||
| 2917 | // Copy overlay_clip over base_clip in areas where overlay_clip is lighter by threshold. | ||
| 2918 | // called only once, for all planes | ||
| 2919 | // integer 8-16 bits version | ||
| 2920 | ✗ | layer_planarrgb_lighten_darken_c_t* layer_fn = nullptr; | |
| 2921 | // 32 bit float version | ||
| 2922 | ✗ | layer_planarrgb_lighten_darken_f_c_t* layer_f_fn = nullptr; | |
| 2923 | |||
| 2924 | #ifdef INTEL_INTRINSICS | ||
| 2925 | ✗ | if (env->GetCPUFlags() & CPUF_AVX2) { | |
| 2926 | // wrapper for the avx2 module, which calls its local scoped get_layer_planarrgb_lighten_darken_functions the included layer.hpp | ||
| 2927 | ✗ | get_layer_planarrgb_lighten_darken_functions_avx2(isLighten, hasAlpha, process_alpha_channel, bits_per_pixel, &layer_fn, &layer_f_fn); | |
| 2928 | } | ||
| 2929 | else | ||
| 2930 | #endif | ||
| 2931 | ✗ | get_layer_planarrgb_lighten_darken_functions(isLighten, hasAlpha, process_alpha_channel, bits_per_pixel, &layer_fn, &layer_f_fn); | |
| 2932 | |||
| 2933 | ✗ | if (layer_fn && bits_per_pixel != 32) | |
| 2934 | ✗ | layer_fn(dstp, ovrp, maskp, dstp_pitch, ovrp_pitch, mask_pitch, width, height, opacity_i, ThresholdParam, bits_per_pixel); | |
| 2935 | ✗ | else if (layer_f_fn && bits_per_pixel == 32) | |
| 2936 | ✗ | layer_f_fn(dstp, ovrp, maskp, dstp_pitch, ovrp_pitch, mask_pitch, width, height, opacity, ThresholdParam_f); | |
| 2937 | // planar rgba lighten/darken end | ||
| 2938 | } | ||
| 2939 | // Layer planar RGB(A) end | ||
| 2940 | 1 | } | |
| 2941 | |||
| 2942 | 13 | return src1; | |
| 2943 | 13 | } | |
| 2944 | |||
| 2945 | |||
| 2946 | ✗ | AVSValue __cdecl Layer::Create(AVSValue args, void*, IScriptEnvironment* env) | |
| 2947 | { | ||
| 2948 | ✗ | const VideoInfo& vi1 = args[0].AsClip()->GetVideoInfo(); | |
| 2949 | ✗ | const VideoInfo& vi2 = args[1].AsClip()->GetVideoInfo(); | |
| 2950 | |||
| 2951 | // "Add" and "Subtract" (use_chroma=true) have native interleaved paths for RGB32 | ||
| 2952 | // (masked_blend_packedrgba_avx2/sse41/c — magic-div, correct at both endpoints). | ||
| 2953 | // For those ops the packed clips are passed straight through; no round-trip conversion | ||
| 2954 | // to PlanarRGBA is needed. Subtract pre-inverts the overlay and extracts the original | ||
| 2955 | // alpha as a separate clip (mask_child) below — see the comment on the Subtract redirect. | ||
| 2956 | // All other ops (Mul, Lighten, Darken, Fast, ...) still require the planar path, and | ||
| 2957 | // RGB24/RGB48/RGB64/YUY2 always convert regardless of op. | ||
| 2958 | ✗ | const char* opStr = args[2].AsString("Add"); | |
| 2959 | const bool packedRGBNativePath = | ||
| 2960 | ✗ | vi1.IsRGB32() && | |
| 2961 | ✗ | (!lstrcmpi(opStr, "Add") || !lstrcmpi(opStr, "Subtract")); | |
| 2962 | |||
| 2963 | // Convert base clip to planar when needed. | ||
| 2964 | // RGB24/RGB48 → PlanarRGB; RGB32/RGB64 → PlanarRGBA (unless native path). | ||
| 2965 | // ConvertToPlanarRGB(A) handles the packed-RGB vertical flip. | ||
| 2966 | ✗ | PClip clip1; | |
| 2967 | ✗ | if (!packedRGBNativePath && (vi1.IsRGB32() || vi1.IsRGB64())) { | |
| 2968 | ✗ | AVSValue new_args[1] = { args[0].AsClip() }; | |
| 2969 | ✗ | clip1 = env->Invoke("ConvertToPlanarRGBA", AVSValue(new_args, 1)).AsClip(); | |
| 2970 | ✗ | } | |
| 2971 | ✗ | else if (vi1.IsRGB24() || vi1.IsRGB48()) { | |
| 2972 | ✗ | AVSValue new_args[1] = { args[0].AsClip() }; | |
| 2973 | ✗ | clip1 = env->Invoke("ConvertToPlanarRGB", AVSValue(new_args, 1)).AsClip(); | |
| 2974 | ✗ | } | |
| 2975 | ✗ | else if (vi1.IsYUY2()) { | |
| 2976 | ✗ | AVSValue new_args[1] = { args[0].AsClip() }; | |
| 2977 | ✗ | clip1 = env->Invoke("ConvertToYV16", AVSValue(new_args, 1)).AsClip(); | |
| 2978 | ✗ | } | |
| 2979 | else { | ||
| 2980 | ✗ | clip1 = args[0].AsClip(); | |
| 2981 | } | ||
| 2982 | |||
| 2983 | // Convert overlay clip to planar when needed. | ||
| 2984 | // For the native packed path the overlay stays as RGB32/RGB64 so GetFrame can read | ||
| 2985 | // the interleaved alpha directly (Add) or from a separate extracted clip (Subtract). | ||
| 2986 | // For the planar path, RGB32/RGB64 → PlanarRGBA so that the A plane is available | ||
| 2987 | // as the per-pixel blend weight. | ||
| 2988 | ✗ | PClip clip2; | |
| 2989 | ✗ | if (!packedRGBNativePath && (vi2.IsRGB32() || vi2.IsRGB64())) { | |
| 2990 | ✗ | AVSValue new_args[1] = { args[1].AsClip() }; | |
| 2991 | ✗ | clip2 = env->Invoke("ConvertToPlanarRGBA", AVSValue(new_args, 1)).AsClip(); | |
| 2992 | ✗ | } | |
| 2993 | ✗ | else if (vi2.IsRGB24() || vi2.IsRGB48()) { | |
| 2994 | ✗ | AVSValue new_args[1] = { args[1].AsClip() }; | |
| 2995 | ✗ | clip2 = env->Invoke("ConvertToPlanarRGB", AVSValue(new_args, 1)).AsClip(); | |
| 2996 | ✗ | } | |
| 2997 | ✗ | else if (vi2.IsYUY2()) { | |
| 2998 | ✗ | AVSValue new_args[1] = { args[1].AsClip() }; | |
| 2999 | ✗ | clip2 = env->Invoke("ConvertToYV16", AVSValue(new_args, 1)).AsClip(); | |
| 3000 | ✗ | } | |
| 3001 | else { | ||
| 3002 | ✗ | clip2 = args[1].AsClip(); | |
| 3003 | } | ||
| 3004 | |||
| 3005 | // When op="Subtract", pre-invert the overlay clip and redirect to "Add". | ||
| 3006 | // This eliminates the subtract template dimension in layer.hpp and the SIMD dispatchers. | ||
| 3007 | // Equivalence is exact for Y/U/V/R/G/B planes. | ||
| 3008 | // | ||
| 3009 | // Alpha treatment: alpha is just another plane — Invert() inverts ALL channels including A. | ||
| 3010 | // For overlay clips that have an alpha plane (YUVA / PlanarRGBA / RGB32 / RGB64), we must | ||
| 3011 | // save the original (pre-invert) A before calling Invert, because: | ||
| 3012 | // - The original A is the per-pixel blend *weight* for all colour channels. | ||
| 3013 | // - The inverted A is the blend *target* for the alpha channel itself when both clips | ||
| 3014 | // have alpha (process_alpha_channel=true, or packed-RGB native path). | ||
| 3015 | // ExtractA is a free SubplanarFrame alias (no copy) for planar formats; for packed | ||
| 3016 | // RGB32/RGB64 it produces a separate 8/16-bit Y grayscale clip. | ||
| 3017 | // The result is stored in mask_child and wired into GetFrame as the per-pixel mask. | ||
| 3018 | // For overlay clips without alpha, and for all non-Subtract operations, mask_child stays | ||
| 3019 | // nullptr and GetFrame reads the mask directly from the overlay's alpha channel. | ||
| 3020 | // | ||
| 3021 | // Native packed RGB32 Subtract (use_chroma=true): treated uniformly here — | ||
| 3022 | // ExtractA + Invert + redirect to Add — so no subtract logic remains in any kernel. | ||
| 3023 | // use_chroma=false Subtract keeps the old formula-based kernels (no alpha mask). | ||
| 3024 | ✗ | const bool use_chroma_arg = args[7].AsBool(true); | |
| 3025 | ✗ | PClip mask_clip = nullptr; | |
| 3026 | ✗ | if (!lstrcmpi(opStr, "Subtract")) { | |
| 3027 | ✗ | const VideoInfo& vi_c2 = clip2->GetVideoInfo(); | |
| 3028 | ✗ | if (packedRGBNativePath && use_chroma_arg) { | |
| 3029 | // Native packed RGB32 Subtract (use_chroma=true): | ||
| 3030 | // extract original alpha as separate mask, pre-invert the overlay, redirect. | ||
| 3031 | ✗ | AVSValue ea_args[1] = { AVSValue(clip2) }; | |
| 3032 | ✗ | mask_clip = env->Invoke("ExtractA", AVSValue(ea_args, 1)).AsClip(); | |
| 3033 | ✗ | AVSValue inv_args[1] = { AVSValue(clip2) }; | |
| 3034 | ✗ | clip2 = env->Invoke("Invert", AVSValue(inv_args, 1)).AsClip(); | |
| 3035 | ✗ | opStr = "Add"; | |
| 3036 | ✗ | } | |
| 3037 | ✗ | else if (vi_c2.IsYUV() || vi_c2.IsYUVA() || vi_c2.IsPlanarRGB() || vi_c2.IsPlanarRGBA()) { | |
| 3038 | // Save pre-invert alpha as the per-pixel blend weight. | ||
| 3039 | ✗ | if (vi_c2.IsYUVA() || vi_c2.IsPlanarRGBA()) { | |
| 3040 | ✗ | AVSValue ea_args[1] = { AVSValue(clip2) }; | |
| 3041 | ✗ | mask_clip = env->Invoke("ExtractA", AVSValue(ea_args, 1)).AsClip(); | |
| 3042 | ✗ | } | |
| 3043 | // Invert ALL channels — alpha is treated uniformly like any other plane. | ||
| 3044 | ✗ | AVSValue inv_args[1] = { AVSValue(clip2) }; | |
| 3045 | ✗ | clip2 = env->Invoke("Invert", AVSValue(inv_args, 1)).AsClip(); | |
| 3046 | ✗ | opStr = "Add"; | |
| 3047 | ✗ | } | |
| 3048 | } | ||
| 3049 | |||
| 3050 | ✗ | Layer* Result = new Layer(clip1, clip2, mask_clip, opStr, args[3].AsInt(-1), | |
| 3051 | ✗ | args[4].AsInt(0), args[5].AsInt(0), args[6].AsInt(0), | |
| 3052 | ✗ | args[7].AsBool(true), // chroma | |
| 3053 | ✗ | args[8].AsFloatf(-1.0f), // opacity | |
| 3054 | getPlacement(args[9], env), // chroma placement | ||
| 3055 | ✗ | env); | |
| 3056 | |||
| 3057 | ✗ | if (vi1.IsRGB24()) { | |
| 3058 | ✗ | AVSValue new_args2[1] = { Result }; | |
| 3059 | ✗ | return env->Invoke("ConvertToRGB24", AVSValue(new_args2, 1)).AsClip(); | |
| 3060 | ✗ | } | |
| 3061 | ✗ | else if (vi1.IsRGB48()) { | |
| 3062 | ✗ | AVSValue new_args2[1] = { Result }; | |
| 3063 | ✗ | return env->Invoke("ConvertToRGB48", AVSValue(new_args2, 1)).AsClip(); | |
| 3064 | ✗ | } | |
| 3065 | ✗ | else if (!packedRGBNativePath && vi1.IsRGB32()) { | |
| 3066 | // Non-native op: result is PlanarRGBA, convert back to packed. | ||
| 3067 | ✗ | AVSValue new_args2[1] = { Result }; | |
| 3068 | ✗ | return env->Invoke("ConvertToRGB32", AVSValue(new_args2, 1)).AsClip(); | |
| 3069 | ✗ | } | |
| 3070 | ✗ | else if (!packedRGBNativePath && vi1.IsRGB64()) { | |
| 3071 | ✗ | AVSValue new_args2[1] = { Result }; | |
| 3072 | ✗ | return env->Invoke("ConvertToRGB64", AVSValue(new_args2, 1)).AsClip(); | |
| 3073 | ✗ | } | |
| 3074 | ✗ | else if (vi1.IsYUY2()) { | |
| 3075 | ✗ | AVSValue new_args2[1] = { Result }; | |
| 3076 | ✗ | return env->Invoke("ConvertToYUY2", AVSValue(new_args2, 1)).AsClip(); | |
| 3077 | ✗ | } | |
| 3078 | |||
| 3079 | ✗ | return Result; | |
| 3080 | |||
| 3081 | ✗ | } | |
| 3082 | |||
| 3083 | |||
| 3084 | |||
| 3085 | /********************************** | ||
| 3086 | ******* Subtract Filter ****** | ||
| 3087 | *********************************/ | ||
| 3088 | bool Subtract::DiffFlag = false; | ||
| 3089 | BYTE Subtract::LUT_Diff8[513]; | ||
| 3090 | |||
| 3091 | 2 | Subtract::Subtract(PClip _child1, PClip _child2, IScriptEnvironment* env) | |
| 3092 |
2/4✓ Branch 3 → 4 taken 2 times.
✗ Branch 3 → 31 not taken.
✓ Branch 4 → 5 taken 2 times.
✗ Branch 4 → 29 not taken.
|
2 | : child1(_child1), child2(_child2) |
| 3093 | { | ||
| 3094 |
1/2✓ Branch 6 → 7 taken 2 times.
✗ Branch 6 → 26 not taken.
|
2 | VideoInfo vi1 = child1->GetVideoInfo(); |
| 3095 |
1/2✓ Branch 8 → 9 taken 2 times.
✗ Branch 8 → 26 not taken.
|
2 | VideoInfo vi2 = child2->GetVideoInfo(); |
| 3096 | |||
| 3097 |
2/4✓ Branch 9 → 10 taken 2 times.
✗ Branch 9 → 11 not taken.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 2 times.
|
2 | if (vi1.width != vi2.width || vi1.height != vi2.height) |
| 3098 | ✗ | env->ThrowError("Subtract: image dimensions don't match"); | |
| 3099 | |||
| 3100 |
2/4✓ Branch 12 → 13 taken 2 times.
✗ Branch 12 → 26 not taken.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 2 times.
|
2 | if (!(vi1.IsSameColorspace(vi2))) |
| 3101 | ✗ | env->ThrowError("Subtract: image formats don't match"); | |
| 3102 | |||
| 3103 | 2 | vi = vi1; | |
| 3104 | 2 | vi.num_frames = std::max(vi1.num_frames, vi2.num_frames); | |
| 3105 | 2 | vi.num_audio_samples = std::max(vi1.num_audio_samples, vi2.num_audio_samples); | |
| 3106 | |||
| 3107 |
1/2✓ Branch 17 → 18 taken 2 times.
✗ Branch 17 → 26 not taken.
|
2 | pixelsize = vi.ComponentSize(); |
| 3108 |
1/2✓ Branch 18 → 19 taken 2 times.
✗ Branch 18 → 26 not taken.
|
2 | bits_per_pixel = vi.BitsPerComponent(); |
| 3109 | |||
| 3110 |
1/2✓ Branch 19 → 20 taken 2 times.
✗ Branch 19 → 25 not taken.
|
2 | if (!DiffFlag) { // Init the global Diff table |
| 3111 | 2 | DiffFlag = true; | |
| 3112 |
2/2✓ Branch 24 → 21 taken 1026 times.
✓ Branch 24 → 25 taken 2 times.
|
1028 | for (int i = 0; i <= 512; i++) LUT_Diff8[i] = std::max(0, std::min(255, i - 129)); |
| 3113 | // 0 .. 129 130 131 ... 255 256 257 258 384 ... 512 | ||
| 3114 | // 0 .. 0 1 2 3 ... 126 127 128 129 ... 255 ... 255 | ||
| 3115 | } | ||
| 3116 | 2 | } | |
| 3117 | |||
| 3118 | template<typename pixel_t, int midpixel, bool chroma> | ||
| 3119 | ✗ | static void subtract_plane(BYTE* src1p, const BYTE* src2p, int src1_pitch, int src2_pitch, int width, int height, int bits_per_pixel) | |
| 3120 | { | ||
| 3121 | typedef typename std::conditional < sizeof(pixel_t) == 4, float, int>::type limits_t; | ||
| 3122 | |||
| 3123 | ✗ | const limits_t limit_lo = sizeof(pixel_t) <= 2 ? 0 : (limits_t)(chroma ? uv8tof(0) : c8tof(0)); | |
| 3124 | ✗ | const limits_t limit_hi = sizeof(pixel_t) == 1 ? 255 : sizeof(pixel_t) == 2 ? ((1 << bits_per_pixel) - 1) : (limits_t)(chroma ? uv8tof(255) : c8tof(255)); | |
| 3125 | ✗ | const limits_t equal_luma = sizeof(pixel_t) == 1 ? midpixel : sizeof(pixel_t) == 2 ? (midpixel << (bits_per_pixel - 8)) : (limits_t)(chroma ? uv8tof(midpixel) : c8tof(midpixel)); | |
| 3126 | ✗ | for (int y = 0; y < height; y++) { | |
| 3127 | ✗ | for (int x = 0; x < width; x++) { | |
| 3128 | ✗ | limits_t val = reinterpret_cast<const pixel_t*>(src2p)[x] - reinterpret_cast<const pixel_t*>(src1p)[x] + equal_luma; | |
| 3129 | // 126: luma of equality | ||
| 3130 | ✗ | reinterpret_cast<pixel_t*>(src1p)[x] = | |
| 3131 | ✗ | (pixel_t)std::min(std::max(val, limit_lo), limit_hi); | |
| 3132 | } | ||
| 3133 | ✗ | src1p += src1_pitch; | |
| 3134 | ✗ | src2p += src2_pitch; | |
| 3135 | } | ||
| 3136 | ✗ | } | |
| 3137 | |||
| 3138 | 2 | PVideoFrame __stdcall Subtract::GetFrame(int n, IScriptEnvironment* env) | |
| 3139 | { | ||
| 3140 |
2/2✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 127 taken 1 time.
|
2 | PVideoFrame src1 = child1->GetFrame(n, env); |
| 3141 |
1/2✓ Branch 5 → 6 taken 1 time.
✗ Branch 5 → 125 not taken.
|
1 | PVideoFrame src2 = child2->GetFrame(n, env); |
| 3142 | |||
| 3143 |
1/2✓ Branch 6 → 7 taken 1 time.
✗ Branch 6 → 123 not taken.
|
1 | env->MakeWritable(&src1); |
| 3144 | |||
| 3145 |
1/2✓ Branch 8 → 9 taken 1 time.
✗ Branch 8 → 123 not taken.
|
1 | BYTE* src1p = src1->GetWritePtr(); |
| 3146 |
1/2✓ Branch 10 → 11 taken 1 time.
✗ Branch 10 → 123 not taken.
|
1 | const BYTE* src2p = src2->GetReadPtr(); |
| 3147 |
1/2✓ Branch 12 → 13 taken 1 time.
✗ Branch 12 → 123 not taken.
|
1 | int row_size = src1->GetRowSize(); |
| 3148 |
1/2✓ Branch 14 → 15 taken 1 time.
✗ Branch 14 → 123 not taken.
|
1 | int src1_pitch = src1->GetPitch(); |
| 3149 |
1/2✓ Branch 16 → 17 taken 1 time.
✗ Branch 16 → 123 not taken.
|
1 | int src2_pitch = src2->GetPitch(); |
| 3150 | |||
| 3151 | 1 | int width = row_size / pixelsize; | |
| 3152 | 1 | int height = vi.height; | |
| 3153 | |||
| 3154 |
5/14✓ Branch 17 → 18 taken 1 time.
✗ Branch 17 → 123 not taken.
✓ Branch 18 → 19 taken 1 time.
✗ Branch 18 → 24 not taken.
✓ Branch 19 → 20 taken 1 time.
✗ Branch 19 → 123 not taken.
✗ Branch 20 → 21 not taken.
✓ Branch 20 → 23 taken 1 time.
✗ Branch 21 → 22 not taken.
✗ Branch 21 → 123 not taken.
✗ Branch 22 → 23 not taken.
✗ Branch 22 → 24 not taken.
✓ Branch 25 → 26 taken 1 time.
✗ Branch 25 → 70 not taken.
|
1 | if (vi.IsPlanar() && (vi.IsYUV() || vi.IsYUVA())) { |
| 3155 | // alpha | ||
| 3156 |
1/2✓ Branch 26 → 27 taken 1 time.
✗ Branch 26 → 37 not taken.
|
1 | if (pixelsize == 1) { |
| 3157 | // LUT is a bit faster than clamp version | ||
| 3158 |
2/2✓ Branch 36 → 28 taken 2 times.
✓ Branch 36 → 40 taken 1 time.
|
3 | for (int y = 0; y < vi.height; y++) { |
| 3159 |
2/2✓ Branch 30 → 29 taken 14 times.
✓ Branch 30 → 31 taken 2 times.
|
16 | for (int x = 0; x < row_size; x++) { |
| 3160 | 14 | src1p[x] = LUT_Diff8[src1p[x] - src2p[x] + 126 + 129]; | |
| 3161 | } | ||
| 3162 |
1/2✓ Branch 32 → 33 taken 2 times.
✗ Branch 32 → 123 not taken.
|
2 | src1p += src1->GetPitch(); |
| 3163 |
1/2✓ Branch 34 → 35 taken 2 times.
✗ Branch 34 → 123 not taken.
|
2 | src2p += src2->GetPitch(); |
| 3164 | } | ||
| 3165 | } | ||
| 3166 | ✗ | else if (pixelsize == 2) | |
| 3167 | ✗ | subtract_plane<uint16_t, 126, false>(src1p, src2p, src1_pitch, src2_pitch, width, height, bits_per_pixel); | |
| 3168 | else //if (pixelsize==4) | ||
| 3169 | ✗ | subtract_plane<float, 126, false>(src1p, src2p, src1_pitch, src2_pitch, width, height, bits_per_pixel); | |
| 3170 | |||
| 3171 | // chroma | ||
| 3172 |
1/2✓ Branch 41 → 42 taken 1 time.
✗ Branch 41 → 123 not taken.
|
1 | row_size = src1->GetRowSize(PLANAR_U); |
| 3173 |
1/2✓ Branch 42 → 43 taken 1 time.
✗ Branch 42 → 69 not taken.
|
1 | if (row_size) { |
| 3174 | 1 | width = row_size / pixelsize; | |
| 3175 |
1/2✓ Branch 44 → 45 taken 1 time.
✗ Branch 44 → 123 not taken.
|
1 | height = src1->GetHeight(PLANAR_U); |
| 3176 |
1/2✓ Branch 46 → 47 taken 1 time.
✗ Branch 46 → 123 not taken.
|
1 | src1_pitch = src1->GetPitch(PLANAR_U); |
| 3177 |
1/2✓ Branch 48 → 49 taken 1 time.
✗ Branch 48 → 123 not taken.
|
1 | src2_pitch = src2->GetPitch(PLANAR_U); |
| 3178 | // U_plane exists | ||
| 3179 |
1/2✓ Branch 50 → 51 taken 1 time.
✗ Branch 50 → 123 not taken.
|
1 | BYTE* src1p = src1->GetWritePtr(PLANAR_U); |
| 3180 |
1/2✓ Branch 52 → 53 taken 1 time.
✗ Branch 52 → 123 not taken.
|
1 | const BYTE* src2p = src2->GetReadPtr(PLANAR_U); |
| 3181 |
1/2✓ Branch 54 → 55 taken 1 time.
✗ Branch 54 → 123 not taken.
|
1 | BYTE* src1pV = src1->GetWritePtr(PLANAR_V); |
| 3182 |
1/2✓ Branch 56 → 57 taken 1 time.
✗ Branch 56 → 123 not taken.
|
1 | const BYTE* src2pV = src2->GetReadPtr(PLANAR_V); |
| 3183 | |||
| 3184 |
1/2✓ Branch 57 → 58 taken 1 time.
✗ Branch 57 → 64 not taken.
|
1 | if (pixelsize == 1) { |
| 3185 | // LUT is a bit faster than clamp version | ||
| 3186 |
2/2✓ Branch 63 → 59 taken 2 times.
✓ Branch 63 → 69 taken 1 time.
|
3 | for (int y = 0; y < height; y++) { |
| 3187 |
2/2✓ Branch 61 → 60 taken 14 times.
✓ Branch 61 → 62 taken 2 times.
|
16 | for (int x = 0; x < width; x++) { |
| 3188 | 14 | src1p[x] = LUT_Diff8[src1p[x] - src2p[x] + 128 + 129]; | |
| 3189 | 14 | src1pV[x] = LUT_Diff8[src1pV[x] - src2pV[x] + 128 + 129]; | |
| 3190 | } | ||
| 3191 | 2 | src1p += src1_pitch; | |
| 3192 | 2 | src2p += src2_pitch; | |
| 3193 | 2 | src1pV += src1_pitch; | |
| 3194 | 2 | src2pV += src2_pitch; | |
| 3195 | } | ||
| 3196 | } | ||
| 3197 | ✗ | else if (pixelsize == 2) { | |
| 3198 | ✗ | subtract_plane<uint16_t, 128, true>(src1p, src2p, src1_pitch, src2_pitch, width, height, bits_per_pixel); | |
| 3199 | ✗ | subtract_plane<uint16_t, 128, true>(src1pV, src2pV, src1_pitch, src2_pitch, width, height, bits_per_pixel); | |
| 3200 | } | ||
| 3201 | else { //if (pixelsize==4) | ||
| 3202 | ✗ | subtract_plane<float, 128, true>(src1p, src2p, src1_pitch, src2_pitch, width, height, bits_per_pixel); | |
| 3203 | ✗ | subtract_plane<float, 128, true>(src1pV, src2pV, src1_pitch, src2_pitch, width, height, bits_per_pixel); | |
| 3204 | } | ||
| 3205 | } | ||
| 3206 | 1 | return src1; | |
| 3207 | } // End planar YUV | ||
| 3208 | |||
| 3209 | // For YUY2, 50% gray is about (126,128,128) instead of (128,128,128). Grr... | ||
| 3210 | ✗ | if (vi.IsYUY2()) { | |
| 3211 | ✗ | for (int y = 0; y < vi.height; ++y) { | |
| 3212 | ✗ | for (int x = 0; x < row_size; x += 2) { | |
| 3213 | ✗ | src1p[x] = LUT_Diff8[src1p[x] - src2p[x] + 126 + 129]; | |
| 3214 | ✗ | src1p[x + 1] = LUT_Diff8[src1p[x + 1] - src2p[x + 1] + 128 + 129]; | |
| 3215 | } | ||
| 3216 | ✗ | src1p += src1->GetPitch(); | |
| 3217 | ✗ | src2p += src2->GetPitch(); | |
| 3218 | } | ||
| 3219 | } | ||
| 3220 | else { // RGB | ||
| 3221 | ✗ | if (vi.IsPlanarRGB() || vi.IsPlanarRGBA()) { | |
| 3222 | ✗ | const int planesRGB[4] = { PLANAR_G, PLANAR_B, PLANAR_R, PLANAR_A }; | |
| 3223 | |||
| 3224 | // do not diff Alpha | ||
| 3225 | ✗ | for (int p = 0; p < 3; p++) { | |
| 3226 | ✗ | const int plane = planesRGB[p]; | |
| 3227 | ✗ | src1p = src1->GetWritePtr(plane); | |
| 3228 | ✗ | src2p = src2->GetReadPtr(plane); | |
| 3229 | ✗ | src1_pitch = src1->GetPitch(plane); | |
| 3230 | ✗ | src2_pitch = src2->GetPitch(plane); | |
| 3231 | ✗ | if (pixelsize == 1) | |
| 3232 | ✗ | subtract_plane<uint8_t, 128, false>(src1p, src2p, src1_pitch, src2_pitch, width, height, bits_per_pixel); | |
| 3233 | ✗ | else if (pixelsize == 2) | |
| 3234 | ✗ | subtract_plane<uint16_t, 128, false>(src1p, src2p, src1_pitch, src2_pitch, width, height, bits_per_pixel); | |
| 3235 | else | ||
| 3236 | ✗ | subtract_plane<float, 128, false>(src1p, src2p, src1_pitch, src2_pitch, width, height, bits_per_pixel); | |
| 3237 | } | ||
| 3238 | } | ||
| 3239 | else { // packed RGB | ||
| 3240 | ✗ | if (pixelsize == 1) { | |
| 3241 | ✗ | for (int y = 0; y < vi.height; ++y) { | |
| 3242 | ✗ | for (int x = 0; x < row_size; ++x) | |
| 3243 | ✗ | src1p[x] = LUT_Diff8[src1p[x] - src2p[x] + 128 + 129]; | |
| 3244 | |||
| 3245 | ✗ | src1p += src1->GetPitch(); | |
| 3246 | ✗ | src2p += src2->GetPitch(); | |
| 3247 | } | ||
| 3248 | } | ||
| 3249 | else { // pixelsize == 2: RGB48, RGB64 | ||
| 3250 | // width is getrowsize based here: ok. | ||
| 3251 | ✗ | subtract_plane<uint16_t, 128, false>(src1p, src2p, src1_pitch, src2_pitch, width, height, bits_per_pixel); | |
| 3252 | } | ||
| 3253 | } | ||
| 3254 | } | ||
| 3255 | ✗ | return src1; | |
| 3256 | 1 | } | |
| 3257 | |||
| 3258 | |||
| 3259 | |||
| 3260 | ✗ | AVSValue __cdecl Subtract::Create(AVSValue args, void*, IScriptEnvironment* env) | |
| 3261 | { | ||
| 3262 | ✗ | return new Subtract(args[0].AsClip(), args[1].AsClip(), env); | |
| 3263 | } | ||
| 3264 |