filters/overlay/imghelpers.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Avisynth v2.5. Copyright 2002 Ben Rudiak-Gould et al. | ||
| 2 | // http://avisynth.nl | ||
| 3 | |||
| 4 | // This program is free software; you can redistribute it and/or modify | ||
| 5 | // it under the terms of the GNU General Public License as published by | ||
| 6 | // the Free Software Foundation; either version 2 of the License, or | ||
| 7 | // (at your option) any later version. | ||
| 8 | // | ||
| 9 | // This program is distributed in the hope that it will be useful, | ||
| 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 12 | // GNU General Public License for more details. | ||
| 13 | // | ||
| 14 | // You should have received a copy of the GNU General Public License | ||
| 15 | // along with this program; if not, write to the Free Software | ||
| 16 | // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit | ||
| 17 | // http://www.gnu.org/copyleft/gpl.html . | ||
| 18 | // | ||
| 19 | // Linking Avisynth statically or dynamically with other modules is making a | ||
| 20 | // combined work based on Avisynth. Thus, the terms and conditions of the GNU | ||
| 21 | // General Public License cover the whole combination. | ||
| 22 | // | ||
| 23 | // As a special exception, the copyright holders of Avisynth give you | ||
| 24 | // permission to link Avisynth with independent modules that communicate with | ||
| 25 | // Avisynth solely through the interfaces defined in avisynth.h, regardless of the license | ||
| 26 | // terms of these independent modules, and to copy and distribute the | ||
| 27 | // resulting combined work under terms of your choice, provided that | ||
| 28 | // every copy of the combined work is accompanied by a complete copy of | ||
| 29 | // the source code of Avisynth (the version of Avisynth used to produce the | ||
| 30 | // combined work), being distributed under the terms of the GNU General | ||
| 31 | // Public License plus this exception. An independent module is a module | ||
| 32 | // which is not derived from or based on Avisynth, such as 3rd-party filters, | ||
| 33 | // import and export plugins, or graphical user interfaces. | ||
| 34 | |||
| 35 | // Overlay (c) 2003, 2004 by Klaus Post | ||
| 36 | |||
| 37 | #ifndef __Overlay_helpers_h | ||
| 38 | #define __Overlay_helpers_h | ||
| 39 | |||
| 40 | #include <avisynth.h> | ||
| 41 | #include <avs/minmax.h> | ||
| 42 | #include <avs/alignment.h> | ||
| 43 | #include "444convert.h" | ||
| 44 | #include "blend_common.h" | ||
| 45 | |||
| 46 | class ImageOverlayInternal { | ||
| 47 | private: | ||
| 48 | IScriptEnvironment* Env; | ||
| 49 | |||
| 50 | PVideoFrame &frame; | ||
| 51 | |||
| 52 | BYTE* origPlanes[4]; | ||
| 53 | BYTE* fakePlanes[4]; | ||
| 54 | |||
| 55 | int fake_w; | ||
| 56 | int fake_h; | ||
| 57 | int fake_x_accum; // cumulative luma x offset from all SubFrame calls | ||
| 58 | int fake_y_accum; // cumulative luma y offset from all SubFrame calls | ||
| 59 | const int _w; | ||
| 60 | const int _h; | ||
| 61 | const int _bits_per_pixel; | ||
| 62 | const bool grey; | ||
| 63 | |||
| 64 | bool return_original; | ||
| 65 | |||
| 66 | const int planes_y[4] = { PLANAR_Y, PLANAR_U, PLANAR_V, PLANAR_A }; | ||
| 67 | const int planes_r[4] = { PLANAR_G, PLANAR_B, PLANAR_R, PLANAR_A }; | ||
| 68 | const int *planes; | ||
| 69 | |||
| 70 | int planeCount; | ||
| 71 | int pitches[4]; | ||
| 72 | |||
| 73 | BYTE *maskChroma; | ||
| 74 | public: | ||
| 75 | int xSubSamplingShifts[4]; | ||
| 76 | int ySubSamplingShifts[4]; | ||
| 77 | int pitch; | ||
| 78 | int pitchUV; | ||
| 79 | int pitchA; | ||
| 80 | |||
| 81 | 109 | ImageOverlayInternal( | |
| 82 | PVideoFrame &_frame, | ||
| 83 | 109 | int _inw, int _inh, VideoInfo &_workingVI, bool _hasAlpha, bool _grey, IScriptEnvironment* env) : | |
| 84 | 109 | Env(env), | |
| 85 | 109 | frame(_frame), | |
| 86 | 109 | _w(_inw), _h(_inh), _bits_per_pixel(_workingVI.BitsPerComponent()), grey(_grey), maskChroma(nullptr) { | |
| 87 | |||
| 88 | 109 | planeCount = _workingVI.NumComponents(); | |
| 89 | |||
| 90 |
4/4✓ Branch 5 → 6 taken 26 times.
✓ Branch 5 → 8 taken 83 times.
✓ Branch 7 → 8 taken 10 times.
✓ Branch 7 → 9 taken 16 times.
|
109 | planes = (_workingVI.IsYUV() || _workingVI.IsYUVA()) ? planes_y : planes_r; |
| 91 |
2/2✓ Branch 12 → 11 taken 436 times.
✓ Branch 12 → 13 taken 109 times.
|
545 | for (int p = 0; p < 4; p++) { |
| 92 | 436 | xSubSamplingShifts[p] = ySubSamplingShifts[p] = 0; | |
| 93 | 436 | pitches[p] = 0; | |
| 94 | } | ||
| 95 | |||
| 96 |
2/2✓ Branch 19 → 14 taken 337 times.
✓ Branch 19 → 20 taken 109 times.
|
446 | for (int p = 0; p < planeCount; ++p) { |
| 97 | 337 | const int plane = planes[p]; | |
| 98 | 337 | xSubSamplingShifts[p] = _workingVI.GetPlaneWidthSubsampling(plane); | |
| 99 | 337 | ySubSamplingShifts[p] = _workingVI.GetPlaneHeightSubsampling(plane); | |
| 100 | 337 | pitches[p] = frame->GetPitch(plane); | |
| 101 | } | ||
| 102 | |||
| 103 | // Set chroma pitches for greymask mode. | ||
| 104 | // All paths now use the luma pitch for chroma planes — no separate chroma buffer. | ||
| 105 |
2/2✓ Branch 20 → 21 taken 15 times.
✓ Branch 20 → 22 taken 94 times.
|
109 | if (grey) { |
| 106 | 15 | pitches[1] = pitches[2] = pitches[0]; | |
| 107 | } | ||
| 108 | |||
| 109 | // temporarily. so far only blend is ported to arrays | ||
| 110 | 109 | pitch = pitches[0]; | |
| 111 | 109 | pitchUV = pitches[1]; | |
| 112 | 109 | pitchA = pitches[3]; | |
| 113 | |||
| 114 |
2/2✓ Branch 26 → 23 taken 337 times.
✓ Branch 26 → 27 taken 109 times.
|
446 | for (int p = 0; p < planeCount; p++) |
| 115 | 337 | origPlanes[p] = (BYTE*)frame->GetReadPtr(planes[p]); | |
| 116 |
2/2✓ Branch 27 → 28 taken 15 times.
✓ Branch 27 → 37 taken 94 times.
|
109 | if (grey) { |
| 117 |
6/6✓ Branch 29 → 30 taken 13 times.
✓ Branch 29 → 32 taken 2 times.
✓ Branch 31 → 32 taken 1 time.
✓ Branch 31 → 33 taken 12 times.
✓ Branch 34 → 35 taken 3 times.
✓ Branch 34 → 36 taken 12 times.
|
15 | if (_workingVI.Is420() || _workingVI.Is422()) { |
| 118 | // Chroma planes alias the luma plane. BlendImageMask (Overlay Blend/Luma/Chroma — | ||
| 119 | // the only modes that reach this branch with use444=false for subsampled YUV) | ||
| 120 | // computes placement-correct spatial averages per row into a per-row scratch buffer. | ||
| 121 | // xSubSamplingShifts[1/2] remain at their actual values (1) so callers can derive | ||
| 122 | // chroma dimensions and advance the luma-resolution mask row pointer correctly. | ||
| 123 | 3 | origPlanes[1] = origPlanes[2] = origPlanes[0]; | |
| 124 | |||
| 125 | // ------------------------------------------------------------------- | ||
| 126 | // Historical: former per-frame chroma-mask pre-resampling (removed). | ||
| 127 | // ConvertYToYV12Chroma / ConvertYToYV16Chroma were called here once per | ||
| 128 | // frame to produce a pre-downsampled chroma-resolution mask plane stored | ||
| 129 | // in the maskChroma allocation. Removed due to several limitations: | ||
| 130 | // | ||
| 131 | // 1. MPEG1 (centre) chroma placement only. The converters averaged | ||
| 132 | // horizontally centred pairs of luma pixels, which corresponds to | ||
| 133 | // MPEG1/JPEG placement. MPEG2 (left-aligned / co-sited) placement | ||
| 134 | // — the default for H.262, H.264, and most broadcast content — was | ||
| 135 | // never supported; callers used the result regardless. | ||
| 136 | // | ||
| 137 | // 2. SIMD path was not bit-exact with the C path. The SSE2 variant | ||
| 138 | // used the avg_epu8( avg_epu8(a,b), avg_epu8(a^1,b^1) ) trick | ||
| 139 | // to approximate unbiased rounding, while the C fallback used plain | ||
| 140 | // (a+b+1)>>1. Results could differ by ±1 LSB. | ||
| 141 | // | ||
| 142 | // 3. No YV411 (4:1:1) support. No ConvertYToYV411Chroma function | ||
| 143 | // existed, so a greymask + YV411 blend in subsampled mode would | ||
| 144 | // have silently produced wrong output (chroma planes pointing at | ||
| 145 | // luma data without any horizontal averaging applied). | ||
| 146 | // | ||
| 147 | // 4. Only Overlay Blend, Luma, and Chroma with subsampled (420/422) | ||
| 148 | // YUV input and use444=false ever reached this code. All other | ||
| 149 | // modes are blocked from use444=false with YUV input: | ||
| 150 | // - Multiply, Darken, Lighten, SoftLight, HardLight, Difference, | ||
| 151 | // Exclusion: overlay.cpp throws "cannot specify use444=false | ||
| 152 | // for this overlay mode" at construction time. | ||
| 153 | // - Add, Subtract: use444=false is auto-set only for RGB input | ||
| 154 | // (lines 203-208 of overlay.cpp); for YUV these modes stay on | ||
| 155 | // the use444=true path and their ImageOverlayInternal is always | ||
| 156 | // created with a 4:4:4 working format. | ||
| 157 | // maskChroma (the BYTE* member) and its ~ImageOverlayInternal check | ||
| 158 | // are preserved below so the historical code compiles. | ||
| 159 | // ------------------------------------------------------------------- | ||
| 160 | #if 0 | ||
| 161 | // 4:2:0 former path | ||
| 162 | { | ||
| 163 | int pixelsize; | ||
| 164 | if (_bits_per_pixel == 8) pixelsize = 1; | ||
| 165 | else if (_bits_per_pixel <= 16) pixelsize = 2; | ||
| 166 | else pixelsize = 4; | ||
| 167 | |||
| 168 | int tmppitch = AlignNumber((_w >> xSubSamplingShifts[1]) * pixelsize, FRAME_ALIGN); | ||
| 169 | maskChroma = static_cast<BYTE*>(Env->Allocate( | ||
| 170 | tmppitch * (_h >> ySubSamplingShifts[1]), 64, AVS_POOLED_ALLOC)); | ||
| 171 | pitches[1] = pitches[2] = tmppitch; | ||
| 172 | ConvertYToYV12Chroma(maskChroma, origPlanes[0], pitches[1], pitches[0], pixelsize, | ||
| 173 | _w >> xSubSamplingShifts[1], _h >> ySubSamplingShifts[1], Env); | ||
| 174 | origPlanes[1] = origPlanes[2] = maskChroma; | ||
| 175 | } | ||
| 176 | // 4:2:2 former path | ||
| 177 | { | ||
| 178 | int tmppitch = AlignNumber((_w >> xSubSamplingShifts[1]) * pixelsize, FRAME_ALIGN); | ||
| 179 | maskChroma = static_cast<BYTE*>(Env->Allocate( | ||
| 180 | tmppitch * (_h >> ySubSamplingShifts[1]), 64, AVS_POOLED_ALLOC)); | ||
| 181 | pitches[1] = pitches[2] = tmppitch; | ||
| 182 | ConvertYToYV16Chroma(maskChroma, origPlanes[0], pitches[1], pitches[0], pixelsize, | ||
| 183 | _w >> xSubSamplingShifts[1], _h >> ySubSamplingShifts[1], Env); | ||
| 184 | origPlanes[1] = origPlanes[2] = maskChroma; | ||
| 185 | } | ||
| 186 | #endif | ||
| 187 | } | ||
| 188 | else { | ||
| 189 | // 444 / RGB / YV411 (no dedicated converter): UV = Y plane pointer. | ||
| 190 | 12 | origPlanes[1] = origPlanes[2] = origPlanes[0]; | |
| 191 | 12 | xSubSamplingShifts[1] = xSubSamplingShifts[2] = 0; | |
| 192 | 12 | ySubSamplingShifts[1] = ySubSamplingShifts[2] = 0; | |
| 193 | } | ||
| 194 | } | ||
| 195 | |||
| 196 | 109 | ResetFake(); | |
| 197 | 109 | } | |
| 198 | |||
| 199 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 466 times.
|
466 | __inline int w() { return (return_original) ? _w : fake_w; } |
| 200 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 466 times.
|
466 | __inline int h() { return (return_original) ? _h : fake_h; } |
| 201 | |||
| 202 | 183 | BYTE* GetPtr(int plane) { | |
| 203 |
1/2✓ Branch 2 → 3 taken 183 times.
✗ Branch 2 → 4 not taken.
|
183 | if (!(_w && _h)) { |
| 204 | _RPT0(1,"Image444: Height or Width is 0"); | ||
| 205 | } | ||
| 206 |
3/5✓ Branch 4 → 5 taken 61 times.
✓ Branch 4 → 9 taken 61 times.
✓ Branch 4 → 13 taken 61 times.
✗ Branch 4 → 17 not taken.
✗ Branch 4 → 21 not taken.
|
183 | switch (plane) { |
| 207 | 61 | case PLANAR_Y: | |
| 208 |
1/2✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 61 times.
|
61 | return (return_original) ? origPlanes[0] : fakePlanes[0]; |
| 209 | 61 | case PLANAR_U: | |
| 210 |
1/2✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 61 times.
|
61 | return (return_original) ? origPlanes[1] : fakePlanes[1]; |
| 211 | 61 | case PLANAR_V: | |
| 212 |
1/2✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 61 times.
|
61 | return (return_original) ? origPlanes[2] : fakePlanes[2]; |
| 213 | ✗ | case PLANAR_A: | |
| 214 | ✗ | return (return_original) ? origPlanes[3] : fakePlanes[3]; | |
| 215 | } | ||
| 216 | ✗ | return origPlanes[0]; | |
| 217 | } | ||
| 218 | |||
| 219 | 141 | int GetPitchByIndex(int planeIndex) { | |
| 220 |
2/4✓ Branch 2 → 3 taken 141 times.
✗ Branch 2 → 5 not taken.
✓ Branch 3 → 4 taken 141 times.
✗ Branch 3 → 5 not taken.
|
141 | if (planeIndex>=0 && planeIndex <=3) |
| 221 | 141 | return pitches[planeIndex]; | |
| 222 | ✗ | return pitches[0]; // Y | |
| 223 | } | ||
| 224 | |||
| 225 | |||
| 226 | 333 | BYTE* GetPtrByIndex(int planeIndex) { | |
| 227 |
1/2✓ Branch 2 → 3 taken 333 times.
✗ Branch 2 → 4 not taken.
|
333 | if (!(_w && _h)) { |
| 228 | _RPT0(1,"Image444: Height or Width is 0"); | ||
| 229 | } | ||
| 230 |
2/4✓ Branch 4 → 5 taken 333 times.
✗ Branch 4 → 10 not taken.
✓ Branch 5 → 6 taken 333 times.
✗ Branch 5 → 10 not taken.
|
333 | if (planeIndex>=0 && planeIndex <=3) |
| 231 |
1/2✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 333 times.
|
333 | return (return_original) ? origPlanes[planeIndex] : fakePlanes[planeIndex]; |
| 232 | ✗ | return origPlanes[0]; // Y | |
| 233 | } | ||
| 234 | |||
| 235 | /* | ||
| 236 | void SetPtr(BYTE* ptr, int plane) { | ||
| 237 | if (!(_w && _h)) { | ||
| 238 | _RPT0(1,"Image444: Height or Width is 0"); | ||
| 239 | } | ||
| 240 | switch (plane) { | ||
| 241 | case PLANAR_Y: | ||
| 242 | fake_Y_plane = Y_plane = ptr; | ||
| 243 | break; | ||
| 244 | case PLANAR_U: | ||
| 245 | fake_Y_plane = U_plane = ptr; | ||
| 246 | break; | ||
| 247 | case PLANAR_V: | ||
| 248 | fake_Y_plane = V_plane = ptr; | ||
| 249 | break; | ||
| 250 | case PLANAR_A: | ||
| 251 | fake_A_plane = A_plane = ptr; | ||
| 252 | break; | ||
| 253 | } | ||
| 254 | } | ||
| 255 | */ | ||
| 256 | void SetPtrByIndex(BYTE* ptr, int planeIndex) { | ||
| 257 | if (!(_w && _h)) { | ||
| 258 | _RPT0(1,"Image444: Height or Width is 0"); | ||
| 259 | } | ||
| 260 | if (planeIndex >= 0 && planeIndex <= 3) | ||
| 261 | origPlanes[planeIndex] = fakePlanes[planeIndex] = ptr; | ||
| 262 | } | ||
| 263 | |||
| 264 | |||
| 265 | int GetPitch(int plane) { | ||
| 266 | if (!(_w && _h)) { | ||
| 267 | _RPT0(1,"Image444: Height or Width is 0"); | ||
| 268 | } | ||
| 269 | switch (plane) { | ||
| 270 | case PLANAR_Y: | ||
| 271 | case PLANAR_G: | ||
| 272 | case PLANAR_B: | ||
| 273 | case PLANAR_R: | ||
| 274 | return pitch; | ||
| 275 | case PLANAR_U: | ||
| 276 | case PLANAR_V: | ||
| 277 | return pitchUV; | ||
| 278 | case PLANAR_A: | ||
| 279 | return pitchA; | ||
| 280 | } | ||
| 281 | return pitch; | ||
| 282 | } | ||
| 283 | |||
| 284 | 62 | void SubFrame(int x, int y, int new_w, int new_h) { | |
| 285 | 62 | new_w = min(new_w, w()-x); | |
| 286 | 62 | new_h = min(new_h, h()-y); | |
| 287 | |||
| 288 | int pixelsize; | ||
| 289 |
3/3✓ Branch 6 → 7 taken 50 times.
✓ Branch 6 → 8 taken 8 times.
✓ Branch 6 → 9 taken 4 times.
|
62 | switch(_bits_per_pixel) { |
| 290 | 50 | case 8: pixelsize = 1; break; | |
| 291 | 8 | case 32: pixelsize = 4; break; | |
| 292 | 4 | default: pixelsize = 2; | |
| 293 | } | ||
| 294 | |||
| 295 |
2/2✓ Branch 13 → 11 taken 186 times.
✓ Branch 13 → 14 taken 62 times.
|
248 | for (int p = 0; p < 3; p++) |
| 296 | { | ||
| 297 | 186 | fakePlanes[p] = GetPtrByIndex(p) + (x >> xSubSamplingShifts[p]) * pixelsize + (y >> ySubSamplingShifts[p]) * pitches[p]; | |
| 298 | } | ||
| 299 |
2/2✓ Branch 14 → 15 taken 6 times.
✓ Branch 14 → 17 taken 56 times.
|
62 | fakePlanes[3] = pitches[3] > 0 ? (GetPtrByIndex(3) + (x >> xSubSamplingShifts[3])*pixelsize + (y >> ySubSamplingShifts[3])*pitches[3]) : nullptr; |
| 300 | |||
| 301 | 62 | fake_w = new_w; | |
| 302 | 62 | fake_h = new_h; | |
| 303 | 62 | fake_x_accum += x; | |
| 304 | 62 | fake_y_accum += y; | |
| 305 | 62 | } | |
| 306 | |||
| 307 | // Accumulated luma x/y offset from all SubFrame calls since last ResetFake. | ||
| 308 | // Used by blend functions to compute the correct chroma column/row count via | ||
| 309 | // ceiling division when the starting position is not chroma-grid-aligned. | ||
| 310 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 9 times.
|
9 | int xAccum() const { return return_original ? 0 : fake_x_accum; } |
| 311 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 6 times.
|
6 | int yAccum() const { return return_original ? 0 : fake_y_accum; } |
| 312 | |||
| 313 | 47 | bool IsSizeZero() { | |
| 314 |
1/2✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 47 times.
|
47 | if (w()<=0) return true; |
| 315 |
1/2✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 47 times.
|
47 | if (h()<=0) return true; |
| 316 |
2/4✓ Branch 8 → 9 taken 47 times.
✗ Branch 8 → 10 not taken.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 47 times.
|
47 | if (!(pitch && origPlanes[0])) return true; |
| 317 | 47 | return false; | |
| 318 | } | ||
| 319 | |||
| 320 | 248 | void ReturnOriginal(bool shouldI) { | |
| 321 | 248 | return_original = shouldI; | |
| 322 | 248 | } | |
| 323 | |||
| 324 | 233 | void ResetFake() { | |
| 325 | 233 | return_original = true; | |
| 326 |
2/2✓ Branch 4 → 3 taken 932 times.
✓ Branch 4 → 5 taken 233 times.
|
1165 | for (int i = 0; i < 4; i++) |
| 327 | 932 | fakePlanes[i] = origPlanes[i]; | |
| 328 | 233 | fake_w = _w; | |
| 329 | 233 | fake_h = _h; | |
| 330 | 233 | fake_x_accum = 0; | |
| 331 | 233 | fake_y_accum = 0; | |
| 332 | 233 | } | |
| 333 | |||
| 334 | 109 | ~ImageOverlayInternal() { | |
| 335 |
1/2✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 109 times.
|
109 | if(maskChroma) |
| 336 | ✗ | Env->Free(maskChroma); | |
| 337 | 109 | } | |
| 338 | |||
| 339 | }; | ||
| 340 | |||
| 341 | |||
| 342 | #endif | ||
| 343 |