vapoursynth-dotkill 3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,99 @@
1
+ name: Build
2
+
3
+ on: [push, pull_request, workflow_dispatch]
4
+
5
+ jobs:
6
+ build-non-windows-wheel:
7
+ strategy:
8
+ matrix:
9
+ include:
10
+ - arch: linux_x86_64
11
+ runs-on: ubuntu-latest
12
+ - arch: linux_aarch64
13
+ runs-on: ubuntu-24.04-arm
14
+ - arch: macosx_x86_64
15
+ runs-on: macos-26-intel
16
+ - arch: macosx_arm64
17
+ runs-on: macos-26
18
+ fail-fast: false
19
+
20
+ runs-on: ${{ matrix.runs-on }}
21
+
22
+ steps:
23
+ - uses: actions/checkout@v6
24
+
25
+ - uses: pypa/cibuildwheel@v3.4.0
26
+ env:
27
+ CIBW_BUILD: cp314-*
28
+ CIBW_BUILD_FRONTEND: uv
29
+ with:
30
+ output-dir: dist
31
+ extras: uv
32
+
33
+ - name: Fix tag
34
+ run: |
35
+ cd dist
36
+ for whl in *.whl; do
37
+ pipx run wheel tags --remove --python-tag py3 --abi-tag none $whl
38
+ done
39
+
40
+ - uses: actions/upload-artifact@v7
41
+ with:
42
+ name: descratch-${{ matrix.arch }}
43
+ path: dist
44
+
45
+ build-windows-wheel:
46
+ runs-on: windows-latest
47
+
48
+ defaults:
49
+ run:
50
+ shell: msys2 {0}
51
+
52
+ steps:
53
+ - uses: actions/checkout@v6
54
+
55
+ - uses: msys2/setup-msys2@v2
56
+ with:
57
+ msystem: UCRT64
58
+ update: true
59
+ install: >-
60
+ mingw-w64-ucrt-x86_64-git
61
+ mingw-w64-ucrt-x86_64-ninja
62
+ mingw-w64-ucrt-x86_64-toolchain
63
+ mingw-w64-ucrt-x86_64-uv
64
+
65
+ - name: Build
66
+ run: |
67
+ uv build -Csetup-args="-Dcpp_link_args=-static"
68
+
69
+ - name: Fix tag
70
+ run: |
71
+ cd dist
72
+ for whl in *.whl; do
73
+ uvx wheel tags --remove --python-tag py3 --abi-tag none $whl
74
+ done
75
+
76
+ - uses: actions/upload-artifact@v7
77
+ with:
78
+ name: descratch-sdist
79
+ path: dist/*.tar.gz
80
+
81
+ - uses: actions/upload-artifact@v7
82
+ with:
83
+ name: descratch-win_amd64
84
+ path: dist/*.whl
85
+
86
+ publish:
87
+ permissions:
88
+ id-token: write
89
+ needs: [build-non-windows-wheel, build-windows-wheel]
90
+ if: ${{ startsWith(github.ref, 'refs/tags/') }}
91
+ runs-on: ubuntu-latest
92
+
93
+ steps:
94
+ - uses: actions/download-artifact@v8
95
+ with:
96
+ path: dist
97
+ merge-multiple: true
98
+
99
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 Fredrik Mellbin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,85 @@
1
+ Metadata-Version: 2.4
2
+ Name: vapoursynth-dotkill
3
+ Version: 3.0
4
+ Summary: Rainbow and dotcrawl removal filter for VapourSynth
5
+ Keywords: video,rainbows,dotcrawl
6
+ Author-Email: Fredrik Mellbin <fredrik.mellbin@gmail.com>
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Environment :: Plugins
11
+ Classifier: Natural Language :: English
12
+ Classifier: Operating System :: MacOS
13
+ Classifier: Operating System :: Microsoft :: Windows
14
+ Classifier: Operating System :: POSIX
15
+ Classifier: Operating System :: Unix
16
+ Classifier: Programming Language :: C++
17
+ Classifier: Topic :: Multimedia :: Video
18
+ Project-URL: Repository, https://github.com/myrsloik/DotKill.git
19
+ Project-URL: Issues, https://github.com/myrsloik/DotKill/issues
20
+ Requires-Python: >=3.12
21
+ Requires-Dist: VapourSynth
22
+ Description-Content-Type: text/markdown
23
+
24
+ DotKill
25
+ =======
26
+
27
+ Spatio-temporal dotcrawl and rainbow remover for VapourSynth
28
+
29
+ Functions
30
+ =========
31
+
32
+ DotKillS
33
+ --------
34
+
35
+ dotkill.DotKillS(clip clip[, int iterations=1])
36
+
37
+ A purely spatial dotcrawl remover that can be safely used on most material after field matching.
38
+
39
+ iterations: The number of times to apply the internal filter. Usally a number between 1 and 4 will have the best results and using too high values may cause artifacting.
40
+
41
+ usematch: If true then matching hints from VFM are used when processing. This may or may not have a positive effect.
42
+
43
+ DotKillZ
44
+ --------
45
+
46
+ dotkill.DotKillZ(clip clip[, int order=0, int offset=0])
47
+
48
+ A pseudo-spatial dotcrawl and rainbow remover. It only works on NTSC content with rainbows added after 3:2 pulldown. This is true most of the time for anime.
49
+
50
+ Note that due to its nature only every other final frame will have dotcrawl and rainbows removed. Typically never artifacts if all requirements are met.
51
+
52
+ order: Field order. Usually 0, note that 1 hasn't been tested due to a lack of test material.
53
+
54
+ offset: The cycle offset for the pulldown pattern. A number between 0 and 4. Can only be determined by trial and error.
55
+
56
+ DotKillT
57
+ --------
58
+
59
+ dotkill.DotKillT(clip clip[, int order=0, int offset=0, int dupthresh=64, int tratio=3, bint show=False])
60
+
61
+ A full spatioi-temporal dotcrawl and rainbow remover. It only works on NTSC content with rainbows added after 3:2 pulldown. This is true most of the time for anime.
62
+
63
+ May produce extreme artifacting if dupthresh is set too high.
64
+
65
+ order: Field order. Usually 0, note that 1 hasn't been tested due to a lack of test material.
66
+
67
+ offset: The cycle offset for the pulldown pattern. A number between 0 and 4. Can only be determined by trial and error.
68
+
69
+ dupthresh: The threshold for determining if a block has changed between fields. Depending on the source material 32-128 are usually reasonable values. A value of 0 makes the function identical to DotKillZ.
70
+
71
+ tratio: If more than 1/tratio blocks have changed between fields then temporal filtering won't be considered in that direction. Higher values can make high motion sections less likely to artifact.
72
+
73
+ show: Shows which blocks have been determined to contain no change between fields and therefore will be blended to reduce artifacts. White square means that it will blend with the next frame and black square the previous.
74
+
75
+ Usage
76
+ =====
77
+
78
+ clip = core.dotkill.DotKillT(clip, offset=1, dupthresh=64)
79
+
80
+ clip = core.vivtc.VFM(clip)
81
+
82
+ clip = core.dotkill.DotKillS(clip, iterations=4)
83
+
84
+ clip = core.vivtc.VDecimate(clip)
85
+
@@ -0,0 +1,62 @@
1
+ DotKill
2
+ =======
3
+
4
+ Spatio-temporal dotcrawl and rainbow remover for VapourSynth
5
+
6
+ Functions
7
+ =========
8
+
9
+ DotKillS
10
+ --------
11
+
12
+ dotkill.DotKillS(clip clip[, int iterations=1])
13
+
14
+ A purely spatial dotcrawl remover that can be safely used on most material after field matching.
15
+
16
+ iterations: The number of times to apply the internal filter. Usally a number between 1 and 4 will have the best results and using too high values may cause artifacting.
17
+
18
+ usematch: If true then matching hints from VFM are used when processing. This may or may not have a positive effect.
19
+
20
+ DotKillZ
21
+ --------
22
+
23
+ dotkill.DotKillZ(clip clip[, int order=0, int offset=0])
24
+
25
+ A pseudo-spatial dotcrawl and rainbow remover. It only works on NTSC content with rainbows added after 3:2 pulldown. This is true most of the time for anime.
26
+
27
+ Note that due to its nature only every other final frame will have dotcrawl and rainbows removed. Typically never artifacts if all requirements are met.
28
+
29
+ order: Field order. Usually 0, note that 1 hasn't been tested due to a lack of test material.
30
+
31
+ offset: The cycle offset for the pulldown pattern. A number between 0 and 4. Can only be determined by trial and error.
32
+
33
+ DotKillT
34
+ --------
35
+
36
+ dotkill.DotKillT(clip clip[, int order=0, int offset=0, int dupthresh=64, int tratio=3, bint show=False])
37
+
38
+ A full spatioi-temporal dotcrawl and rainbow remover. It only works on NTSC content with rainbows added after 3:2 pulldown. This is true most of the time for anime.
39
+
40
+ May produce extreme artifacting if dupthresh is set too high.
41
+
42
+ order: Field order. Usually 0, note that 1 hasn't been tested due to a lack of test material.
43
+
44
+ offset: The cycle offset for the pulldown pattern. A number between 0 and 4. Can only be determined by trial and error.
45
+
46
+ dupthresh: The threshold for determining if a block has changed between fields. Depending on the source material 32-128 are usually reasonable values. A value of 0 makes the function identical to DotKillZ.
47
+
48
+ tratio: If more than 1/tratio blocks have changed between fields then temporal filtering won't be considered in that direction. Higher values can make high motion sections less likely to artifact.
49
+
50
+ show: Shows which blocks have been determined to contain no change between fields and therefore will be blended to reduce artifacts. White square means that it will blend with the next frame and black square the previous.
51
+
52
+ Usage
53
+ =====
54
+
55
+ clip = core.dotkill.DotKillT(clip, offset=1, dupthresh=64)
56
+
57
+ clip = core.vivtc.VFM(clip)
58
+
59
+ clip = core.dotkill.DotKillS(clip, iterations=4)
60
+
61
+ clip = core.vivtc.VDecimate(clip)
62
+
@@ -0,0 +1,27 @@
1
+ project('DotKill', 'cpp',
2
+ default_options: ['buildtype=release', 'warning_level=2', 'b_lto=true', 'b_ndebug=if-release', 'cpp_std=c++17'],
3
+ license: 'MIT',
4
+ license_files: 'LICENSE',
5
+ meson_version: '>=1.2.3',
6
+ version: '3.0',
7
+ )
8
+
9
+ incdir = include_directories(
10
+ run_command(
11
+ find_program('python', 'python3'),
12
+ '-c',
13
+ 'import vapoursynth as vs; print(vs.get_include())',
14
+ check: true,
15
+ ).stdout().strip(),
16
+ )
17
+
18
+ py = import('python').find_installation(pure: false)
19
+
20
+ shared_module('dotkill',
21
+ files('src/dotkill1.cpp'),
22
+ gnu_symbol_visibility: 'hidden',
23
+ include_directories: incdir,
24
+ install: true,
25
+ install_dir: py.get_install_dir() / 'vapoursynth/plugins',
26
+ name_prefix: '',
27
+ )
@@ -0,0 +1,35 @@
1
+ [build-system]
2
+ requires = ["meson-python", "VapourSynth>=74"]
3
+ build-backend = "mesonpy"
4
+
5
+ [project]
6
+ name = "vapoursynth-dotkill"
7
+ description = "Rainbow and dotcrawl removal filter for VapourSynth"
8
+ readme = "README.md"
9
+ requires-python = ">=3.12"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ authors = [
13
+ {name = "Fredrik Mellbin", email = "fredrik.mellbin@gmail.com"},
14
+ ]
15
+ keywords = ["video", "rainbows", "dotcrawl"]
16
+ classifiers = [
17
+ "Development Status :: 5 - Production/Stable",
18
+ "Environment :: Plugins",
19
+ "Natural Language :: English",
20
+ "Operating System :: MacOS",
21
+ "Operating System :: Microsoft :: Windows",
22
+ "Operating System :: POSIX",
23
+ "Operating System :: Unix",
24
+ "Programming Language :: C++",
25
+ "Topic :: Multimedia :: Video",
26
+ ]
27
+ dependencies = ["VapourSynth"]
28
+ dynamic = ["version"]
29
+
30
+ [project.urls]
31
+ Repository = "https://github.com/myrsloik/DotKill.git"
32
+ Issues = "https://github.com/myrsloik/DotKill/issues"
33
+
34
+ [tool.meson-python.wheel]
35
+ exclude = ["*/*.dll.a", "*/*.lib"]
@@ -0,0 +1,706 @@
1
+ #include "VapourSynth4.h"
2
+ #include "VSHelper4.h"
3
+ #include <algorithm>
4
+ #include <memory>
5
+ #include <cstdlib>
6
+ #include <vector>
7
+
8
+ ////////////////////////////////////////
9
+ // DotKillS
10
+
11
+ typedef struct {
12
+ VSNode *node;
13
+ const VSVideoInfo *vi;
14
+ int iterations;
15
+ } DotKillSData;
16
+
17
+ static void convHoriz(const uint8_t *src, ptrdiff_t srcStride, int16_t *dst, int width, int height) {
18
+ while (--height) {
19
+ dst[0] = 0;
20
+ dst[1] = 0;
21
+
22
+ for (int x = 2; x < width - 3; x++) {
23
+ int16_t temp = -(src[x - 2] + src[x - 1]) + 2 * (src[x] + src[x + 1]) - (src[x + 2] + src[x + 3]);
24
+ temp += -(src[x - 2 + srcStride] + src[x - 1 + srcStride]) + 2 * (src[x + srcStride] + src[x + 1 + srcStride]) - (src[x + 2 + srcStride] + src[x + 3 + srcStride]);
25
+ dst[x] = temp;
26
+ }
27
+
28
+ dst[width - 3] = 0;
29
+ dst[width - 2] = 0;
30
+ dst[width - 1] = 0;
31
+
32
+ src += srcStride;
33
+ dst += width;
34
+ }
35
+ memset(dst, 0, sizeof(int16_t) * width);
36
+ }
37
+
38
+ static void convVert(const uint8_t *src, ptrdiff_t srcStride, int16_t *dst, int width, int height) {
39
+ height -= 5;
40
+
41
+ memset(dst, 0, sizeof(int16_t) * width * 2);
42
+ src += 2 * srcStride;
43
+ dst += 2 * width;
44
+
45
+ while (height--) {
46
+ for (int x = 0; x < width - 1; x++) {
47
+ dst[x] = -(src[x - 2 * srcStride] + src[x - 2 * srcStride + 1] + (src[x - 1 * srcStride] + src[x - 1 * srcStride + 1]))
48
+ + 2 * (src[x] + src[x + 1] + (src[x + 1 * srcStride] + src[x + 1 * srcStride + 1]))
49
+ - (src[x + 2 * srcStride] + src[x + 2 * srcStride + 1] + (src[x + 3 * srcStride] + src[x + 3 * srcStride + 1]));
50
+ }
51
+
52
+ dst[width - 1] = 0;
53
+
54
+ src += srcStride;
55
+ dst += width;
56
+ }
57
+
58
+ memset(dst, 0, sizeof(int16_t) * width * 3);
59
+ }
60
+
61
+ static void applyMask(const int16_t *maskPtr, uint8_t *dst, ptrdiff_t dstStride, int width, int height, uint8_t *ppMask) {
62
+ maskPtr += width;
63
+ ppMask += width;
64
+ dst += dstStride;
65
+
66
+ int16_t sortArray[8];
67
+
68
+ for (int y = 1; y < height - 1; y++) {
69
+ for (int x = 1; x < width - 2; x++) {
70
+ sortArray[0] = maskPtr[x - width - 1];
71
+ sortArray[1] = maskPtr[x - width];
72
+ sortArray[2] = maskPtr[x - width + 1];
73
+ sortArray[3] = maskPtr[x - 1];
74
+ sortArray[4] = maskPtr[x + 1];
75
+ sortArray[5] = maskPtr[x + width - 1];
76
+ sortArray[6] = maskPtr[x + width];
77
+ sortArray[7] = maskPtr[x + width + 1];
78
+
79
+ std::sort(sortArray, sortArray + 8);
80
+
81
+ int16_t upper = sortArray[7];
82
+ int16_t lower = sortArray[0];
83
+
84
+ int16_t t = maskPtr[x] - std::clamp(maskPtr[x], lower, upper);
85
+
86
+ if (t >= 0)
87
+ t = (t + 4) / 8;
88
+ else
89
+ t = (t - 4) / 8;
90
+
91
+ if (std::abs(t) > 1) {
92
+ ppMask[x] = 255;
93
+
94
+ dst[x] = static_cast<uint8_t>(std::clamp(dst[x] - t, 16, 235));
95
+ dst[x + 1] = static_cast<uint8_t>(std::clamp(dst[x + 1] - t, 16, 235));
96
+ dst[x + dstStride] = static_cast<uint8_t>(std::clamp(dst[x + dstStride] - t, 16, 235));
97
+ dst[x + 1 + dstStride] = static_cast<uint8_t>(std::clamp(dst[x + 1 + dstStride] - t, 16, 235));
98
+ }
99
+ }
100
+
101
+ maskPtr += width;
102
+ ppMask += width;
103
+ dst += dstStride;
104
+ }
105
+ }
106
+
107
+ static const VSFrame *VS_CC dotKillSGetFrame(int n, int activationReason, void *instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) {
108
+ DotKillSData *d = reinterpret_cast<DotKillSData*>(instanceData);
109
+
110
+ if (activationReason == arInitial) {
111
+ vsapi->requestFrameFilter(n, d->node, frameCtx);
112
+ } else if (activationReason == arAllFramesReady) {
113
+ const VSFrame *inframe = vsapi->getFrameFilter(n, d->node, frameCtx);
114
+ VSFrame *outframe = vsapi->copyFrame(inframe, core);
115
+
116
+ int width = d->vi->width;
117
+ int height = d->vi->height;
118
+ ptrdiff_t dstStride = vsapi->getStride(outframe, 0);
119
+ uint8_t *dstPtr = vsapi->getWritePtr(outframe, 0);
120
+
121
+ int16_t *tempMask = new int16_t[width * height];
122
+ uint8_t *ppMask = new uint8_t[width * height]();
123
+
124
+ for (int i = 0; i < d->iterations; i++) {
125
+ convVert(dstPtr, dstStride, tempMask, width, height);
126
+ applyMask(tempMask, dstPtr, dstStride, width, height, ppMask);
127
+
128
+ convHoriz(dstPtr, dstStride, tempMask, width, height);
129
+ applyMask(tempMask, dstPtr, dstStride, width, height, ppMask);
130
+
131
+ }
132
+
133
+ delete[] tempMask;
134
+ delete[] ppMask;
135
+
136
+ return outframe;
137
+ }
138
+
139
+ return nullptr;
140
+ }
141
+
142
+ static void VS_CC dotKillSFree(void *instanceData, VSCore *core, const VSAPI *vsapi) {
143
+ DotKillSData *d = reinterpret_cast<DotKillSData*>(instanceData);
144
+ vsapi->freeNode(d->node);
145
+ delete d;
146
+ }
147
+
148
+ static void VS_CC dotKillSCreate(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) {
149
+ std::unique_ptr<DotKillSData> d(new DotKillSData());
150
+
151
+ int err;
152
+ d->node = vsapi->mapGetNode(in, "clip", 0, nullptr);
153
+ d->vi = vsapi->getVideoInfo(d->node);
154
+ d->iterations = vsapi->mapGetIntSaturated(in, "iterations", 0, &err);
155
+ if (d->iterations < 1)
156
+ d->iterations = 1;
157
+ if ((!vsh::isSameVideoPresetFormat(pfYUV420P8, &d->vi->format, core, vsapi) && !vsh::isSameVideoPresetFormat(pfGray8, &d->vi->format, core, vsapi)) || !vsh::isConstantVideoFormat(d->vi)) {
158
+ vsapi->mapSetError(out, "DotKillS: only constant dimension YUV420P8 and GRAY8 supported");
159
+ vsapi->freeNode(d->node);
160
+ return;
161
+ }
162
+
163
+ VSFilterDependency deps[] = { {d->node, rpStrictSpatial} };
164
+ vsapi->createVideoFilter(out, "DotKillS", d->vi, dotKillSGetFrame, dotKillSFree, fmParallel, deps, 1, d.get(), core);
165
+ d.release();
166
+ }
167
+
168
+ /////////////////////////////////////////////////////////////////////
169
+ // DotKillZ
170
+
171
+
172
+ static void applyFieldBlend(const VSFrame *srcc, const VSFrame *srcn, VSFrame *outframe, int order, VSCore *core, const VSAPI *vsapi) {
173
+ const VSVideoFormat *fi = vsapi->getVideoFrameFormat(srcc);
174
+ for (int plane = 0; plane < fi->numPlanes; plane++) {
175
+ int width = vsapi->getFrameWidth(srcc, plane);
176
+ int height = vsapi->getFrameHeight(srcc, plane);
177
+
178
+ ptrdiff_t stride = vsapi->getStride(outframe, plane);
179
+ uint8_t *dstp = vsapi->getWritePtr(outframe, plane);
180
+ const uint8_t *srccp = vsapi->getReadPtr(srcc, plane);
181
+ const uint8_t *srcnp = vsapi->getReadPtr(srcn, plane);
182
+
183
+ if (order) {
184
+ srccp += stride;
185
+ srcnp += stride;
186
+ dstp += stride;
187
+ }
188
+
189
+ for (int h = order; h < height; h += 2) {
190
+ for (int w = 0; w < width; w++)
191
+ dstp[w] = (srccp[w] + srcnp[w] + 1) / 2;
192
+
193
+ srccp += 2 * stride;
194
+ srcnp += 2 * stride;
195
+ dstp += 2 * stride;
196
+ }
197
+ }
198
+ }
199
+
200
+ static void applyDotcrawInverse(const VSFrame *srcc, const VSFrame *srcn, VSFrame *outframe, int order, VSCore *core, const VSAPI *vsapi) {
201
+ const VSVideoFormat *fi = vsapi->getVideoFrameFormat(srcc);
202
+ for (int plane = 0; plane < fi->numPlanes; plane++) {
203
+ int width = vsapi->getFrameWidth(srcc, plane);
204
+ int height = vsapi->getFrameHeight(srcc, plane);
205
+
206
+ ptrdiff_t stride = vsapi->getStride(outframe, plane);
207
+ uint8_t *dstp = vsapi->getWritePtr(outframe, plane);
208
+ const uint8_t *srccp = vsapi->getReadPtr(srcc, plane);
209
+ const uint8_t *srcnp = vsapi->getReadPtr(srcn, plane);
210
+
211
+ if (order) {
212
+ srccp += stride;
213
+ srcnp += stride;
214
+ dstp += stride;
215
+ }
216
+
217
+ for (int h = order; h < height; h += 2) {
218
+ for (int w = 0; w < width; w++) {
219
+ dstp[w] = (srccp[w] + srcnp[w] + 1) / 2;
220
+
221
+ if (h > 1) {
222
+ uint8_t l0val = dstp[w - 2 * stride];
223
+ uint8_t l2val = dstp[w];
224
+ int l0diff = dstp[w - 2 * stride] - srccp[w - 2 * stride];
225
+ int l2diff = dstp[w] - srccp[w];
226
+ if (plane == 0)
227
+ dstp[w - stride] = std::clamp(srccp[w - stride] + (order ? l0diff : l2diff), 16, 235);
228
+ else
229
+ dstp[w - stride] = (l0val + l2val + 1) / 2; // simply use some kind of interpolation and discard one field?
230
+ }
231
+ }
232
+
233
+ srccp += 2 * stride;
234
+ srcnp += 2 * stride;
235
+ dstp += 2 * stride;
236
+ }
237
+ }
238
+ }
239
+
240
+ typedef struct {
241
+ VSNode *node;
242
+ const VSVideoInfo *vi;
243
+ int order;
244
+ int offset;
245
+ } DotKillZData;
246
+
247
+ static const VSFrame *VS_CC dotKillZGetFrame(int n, int activationReason, void *instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) {
248
+ DotKillZData *d = reinterpret_cast<DotKillZData*>(instanceData);
249
+
250
+ if (activationReason == arInitial) {
251
+ vsapi->requestFrameFilter(std::max(n - 1, 0), d->node, frameCtx);
252
+ vsapi->requestFrameFilter(n, d->node, frameCtx);
253
+ vsapi->requestFrameFilter(n + 1, d->node, frameCtx);
254
+ } else if (activationReason == arAllFramesReady) {
255
+ const VSFrame *srcp = vsapi->getFrameFilter(std::max(n - 1, 0), d->node, frameCtx);
256
+ const VSFrame *srcc = vsapi->getFrameFilter(n, d->node, frameCtx);
257
+ const VSFrame *srcn = vsapi->getFrameFilter(n + 1, d->node, frameCtx);
258
+
259
+ /*
260
+ FIELD OFFSETS
261
+ -1 0 1 2 3
262
+ A1 B1 B1 C1 D1
263
+ A2 B2 C2 D2 D2
264
+ */
265
+
266
+ VSFrame *outframe = vsapi->copyFrame(srcc, core);
267
+ if ((n + d->offset) % 5 == 0) {
268
+ // current and next field are duplicates, complement field is from the same frame so do dotcrawl inverse on that as well
269
+ applyDotcrawInverse(srcc, srcn, outframe, d->order, core, vsapi);
270
+ } else if ((n + d->offset) % 5 == 1) {
271
+ // current and previous field are duplicates so blend them together
272
+ applyFieldBlend(srcc, srcp, outframe, d->order, core, vsapi);
273
+ } else if ((n + d->offset) % 5 == 2) {
274
+ // current and next complement field are duplicates so blend them together
275
+ applyFieldBlend(srcc, srcn, outframe, !d->order, core, vsapi);
276
+ } else if ((n + d->offset) % 5 == 3) {
277
+ // current and previous field are duplicates, complement field is from the same frame so do dotcrawl inverse on that as well
278
+ applyDotcrawInverse(srcc, srcp, outframe, !d->order, core, vsapi);
279
+ }
280
+
281
+ vsapi->freeFrame(srcp);
282
+ vsapi->freeFrame(srcc);
283
+ vsapi->freeFrame(srcn);
284
+
285
+ return outframe;
286
+ }
287
+
288
+ return nullptr;
289
+ }
290
+
291
+ static void VS_CC dotKillZFree(void *instanceData, VSCore *core, const VSAPI *vsapi) {
292
+ DotKillZData *d = reinterpret_cast<DotKillZData*>(instanceData);
293
+ vsapi->freeNode(d->node);
294
+ delete d;
295
+ }
296
+
297
+ static void VS_CC dotKillZCreate(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) {
298
+ std::unique_ptr<DotKillZData> d(new DotKillZData());
299
+
300
+ int err;
301
+ d->node = vsapi->mapGetNode(in, "clip", 0, nullptr);
302
+ d->vi = vsapi->getVideoInfo(d->node);
303
+ d->offset = vsapi->mapGetIntSaturated(in, "offset", 0, &err);
304
+ d->order = !!vsapi->mapGetInt(in, "order", 0, &err);
305
+
306
+ if (!vsh::isSameVideoPresetFormat(pfYUV420P8, &d->vi->format, core, vsapi) || !vsh::isConstantVideoFormat(d->vi)) {
307
+ vsapi->mapSetError(out, "DotKillZ: only constant dimension YUV420P8 supported");
308
+ vsapi->freeNode(d->node);
309
+ return;
310
+ }
311
+
312
+ VSFilterDependency deps[] = { {d->node, rpGeneral} };
313
+ vsapi->createVideoFilter(out, "DotKillZ", d->vi, dotKillZGetFrame, dotKillZFree, fmParallelRequests, deps, 1, d.get(), core);
314
+ d.release();
315
+ }
316
+
317
+ /////////////////////////////////////////////////////////////////////
318
+ // DotKillT
319
+
320
+ constexpr int blockx = 16;
321
+ constexpr int blocky = 8;
322
+
323
+ static void calcDiffMetric(const VSFrame *f1, const VSFrame *f2, int64_t *bdiffs, int nxblocks, int nyblocks,int field, const VSAPI *vsapi) {
324
+ for (int plane = 0; plane < 3; plane++) {
325
+ ptrdiff_t stride = vsapi->getStride(f1, plane);
326
+ const uint8_t *f1p = vsapi->getReadPtr(f1, plane);
327
+ const uint8_t *f2p = vsapi->getReadPtr(f2, plane);
328
+ const VSVideoFormat *fi = vsapi->getVideoFrameFormat(f1);
329
+
330
+ if (field) {
331
+ f1p += stride;
332
+ f2p += stride;
333
+ }
334
+
335
+ int width = vsapi->getFrameWidth(f1, plane);
336
+ int height = vsapi->getFrameHeight(f1, plane);
337
+ int hblockx = blockx / 2;
338
+ int hblocky = blocky / 2;
339
+ // adjust for subsampling
340
+ if (plane > 0) {
341
+ hblockx /= 1 << fi->subSamplingW;
342
+ hblocky /= 1 << fi->subSamplingH;
343
+ }
344
+
345
+ for (int y = 0; y < height / 2; y++) {
346
+ int ydest = y / hblocky;
347
+ int xdest = 0;
348
+
349
+ for (int x = 0; x < width; x += hblockx) {
350
+ int acc = 0;
351
+ int m = VSMIN(width, x + hblockx);
352
+ for (int xl = x; xl < m; xl++) {
353
+ int tmp = f1p[xl] - f2p[xl];
354
+ acc += tmp * tmp;
355
+ }
356
+ bdiffs[ydest * nxblocks + xdest] += acc;
357
+ xdest++;
358
+ }
359
+
360
+ f1p += stride * 2;
361
+ f2p += stride * 2;
362
+ }
363
+ }
364
+ }
365
+
366
+ static int64_t getMaxDiff(int i, int j, const int64_t *bdiffs1, int nxblocks, int nyblocks) {
367
+ int64_t tmp1 = bdiffs1[i * nxblocks + j] + bdiffs1[i * nxblocks + j + 1] + bdiffs1[(i + 1) * nxblocks + j] + bdiffs1[(i + 1) * nxblocks + j + 1];
368
+ int64_t tmp2 = bdiffs1[i * nxblocks + j] + bdiffs1[i * nxblocks + j - 1] + bdiffs1[(i + 1) * nxblocks + j] + bdiffs1[(i + 1) * nxblocks + j - 1];
369
+ int64_t tmp3 = bdiffs1[i * nxblocks + j] + bdiffs1[i * nxblocks - j + 1] + bdiffs1[(i - 1) * nxblocks + j] + bdiffs1[(i - 1) * nxblocks + j + 1];
370
+ int64_t tmp4 = bdiffs1[i * nxblocks + j] + bdiffs1[i * nxblocks - j - 1] + bdiffs1[(i - 1) * nxblocks + j] + bdiffs1[(i - 1) * nxblocks + j - 1];
371
+ return std::max({ tmp1, tmp2, tmp3, tmp4 });
372
+ }
373
+
374
+ static void diffMetricToMask(uint8_t *mask, const int64_t *bdiffs1, const int64_t *bdiffs2, int nxblocks, int nyblocks, int dupthresh, int tratio, const VSAPI *vsapi) {
375
+ int totdiff1 = 0;
376
+ int totdiff2 = 0;
377
+
378
+ for (int i = 1; i < nyblocks - 1; i++) {
379
+ for (int j = 1; j < nxblocks - 1; j++) {
380
+ int64_t diff1 = getMaxDiff(i, j, bdiffs1, nxblocks, nyblocks);
381
+ int64_t diff2 = getMaxDiff(i, j, bdiffs2, nxblocks, nyblocks);
382
+
383
+ if (diff1 >= dupthresh)
384
+ totdiff1++;
385
+ if (diff2 >= dupthresh)
386
+ totdiff2++;
387
+ }
388
+ }
389
+
390
+ // skip temporal processing if more than 1/tratio blocks have changed
391
+ bool skip1 = (totdiff1 * tratio > (nxblocks - 2) * (nyblocks - 2));
392
+ bool skip2 = (totdiff2 * tratio > (nxblocks - 2) * (nyblocks - 2));
393
+
394
+ for (int i = 1; i < nyblocks - 1; i++) {
395
+ for (int j = 1; j < nxblocks - 1; j++) {
396
+ int64_t diff1 = getMaxDiff(i, j, bdiffs1, nxblocks, nyblocks);
397
+ int64_t diff2 = getMaxDiff(i, j, bdiffs2, nxblocks, nyblocks);
398
+
399
+ if (!skip1 && diff1 <= diff2 && diff1 < dupthresh)
400
+ mask[nxblocks * i + j] = 1;
401
+ else if (!skip2 && diff2 < diff1 && diff2 < dupthresh)
402
+ mask[nxblocks * i + j] = 2;
403
+ else
404
+ mask[nxblocks * i + j] = 0;
405
+ }
406
+
407
+ // extend mask left and right
408
+ mask[nxblocks * i] = mask[nxblocks * i + 1];
409
+ mask[nxblocks * i + (nxblocks - 1)] = mask[nxblocks * i + (nxblocks - 2)];
410
+ }
411
+
412
+ // extend mask to top and bottom
413
+ memcpy(mask, mask + nxblocks, nxblocks);
414
+ memcpy(mask + nxblocks * (nyblocks - 1), mask + nxblocks * (nyblocks - 2), nxblocks);
415
+ }
416
+
417
+ static void applyTemporalMask(VSFrame *dst, const VSFrame *f0, const VSFrame *f1, const VSFrame *f2, uint8_t *mask, int nxblocks, int nyblocks, int field, bool show, const VSAPI *vsapi) {
418
+ for (int plane = 0; plane < 3; plane++) {
419
+ ptrdiff_t stride = vsapi->getStride(f1, plane);
420
+ const uint8_t *f0p = vsapi->getReadPtr(f0, plane);
421
+ const uint8_t *f1p = vsapi->getReadPtr(f1, plane);
422
+ const uint8_t *f2p = vsapi->getReadPtr(f2, plane);
423
+ uint8_t *dstp = vsapi->getWritePtr(dst, plane);
424
+ const VSVideoFormat *fi = vsapi->getVideoFrameFormat(f1);
425
+
426
+ if (field) {
427
+ f0p += stride;
428
+ f1p += stride;
429
+ f2p += stride;
430
+ dstp += stride;
431
+ }
432
+
433
+ int width = vsapi->getFrameWidth(f1, plane);
434
+ int height = vsapi->getFrameHeight(f1, plane);
435
+ int hblockx = blockx / 2;
436
+ int hblocky = blocky / 2;
437
+
438
+ if (plane > 0) {
439
+ hblockx /= 1 << fi->subSamplingW;
440
+ hblocky /= 1 << fi->subSamplingH;
441
+ }
442
+
443
+ for (int y = 0; y < height / 2; y++) {
444
+ int ydest = y / hblocky;
445
+ int xdest = 0;
446
+
447
+ for (int x = 0; x < width; x += hblockx) {
448
+ int m = VSMIN(width, x + hblockx);
449
+
450
+ for (int xl = x; xl < m; xl++) {
451
+ if (mask[ydest * nxblocks + xdest] == 1)
452
+ dstp[xl] = ((f1p[xl] + f0p[xl] + 1) / 2);
453
+ else if (mask[ydest * nxblocks + xdest] == 2)
454
+ dstp[xl] = ((f2p[xl] + f0p[xl] + 1) / 2);
455
+ else
456
+ dstp[xl] = dstp[xl];
457
+ }
458
+ xdest++;
459
+ }
460
+
461
+ f0p += stride * 2;
462
+ f1p += stride * 2;
463
+ f2p += stride * 2;
464
+ dstp += stride * 2;
465
+ }
466
+ }
467
+
468
+ // Horrible square drawing code
469
+ if (show) {
470
+ ptrdiff_t stride = vsapi->getStride(dst, 0);
471
+ uint8_t *dstp = vsapi->getWritePtr(dst, 0);
472
+
473
+ int width = vsapi->getFrameWidth(dst, 0);
474
+ int height = vsapi->getFrameHeight(dst, 0);
475
+ int hblockx = blockx / 2;
476
+
477
+ for (int y = 0; y < height; y++) {
478
+ int ydest = y / blocky;
479
+ int xdest = 0;
480
+
481
+ for (int x = 0; x < width; x += hblockx) {
482
+ int m = std::min(width, x + hblockx);
483
+
484
+ if (y % blocky == 0) {
485
+ for (int xl = x; xl < m; xl++) {
486
+ if (mask[ydest * nxblocks + xdest] == 1) {
487
+ dstp[xl] = 0;
488
+ } else if (mask[ydest * nxblocks + xdest] == 2) {
489
+ dstp[xl] = 255;
490
+ }
491
+ }
492
+ } else if (y % blocky == blocky - 1) {
493
+ for (int xl = x; xl < m; xl++) {
494
+ if (mask[ydest * nxblocks + xdest] == 1) {
495
+ dstp[xl] = 0;
496
+ } else if (mask[ydest * nxblocks + xdest] == 2) {
497
+ dstp[xl] = 255;
498
+ }
499
+ }
500
+ }
501
+
502
+ if (mask[ydest * nxblocks + xdest] == 1) {
503
+ dstp[x] = 0;
504
+ dstp[m - 1] = 0;
505
+ } else if (mask[ydest * nxblocks + xdest] == 2) {
506
+ dstp[x] = 255;
507
+ dstp[m - 1] = 255;
508
+ }
509
+
510
+ xdest++;
511
+ }
512
+
513
+ dstp += stride;
514
+ }
515
+ }
516
+ }
517
+
518
+ typedef struct {
519
+ VSNode *node;
520
+ const VSVideoInfo *vi;
521
+ int order;
522
+ int offset;
523
+ int dupthresh;
524
+ int tratio;
525
+ bool show;
526
+ } DotKillTData;
527
+
528
+ static const VSFrame *VS_CC dotKillTGetFrame(int n, int activationReason, void *instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) {
529
+ DotKillTData *d = reinterpret_cast<DotKillTData*>(instanceData);
530
+
531
+ int nxblocks = (d->vi->width + blockx / 2 - 1) / (blockx / 2);
532
+ int nyblocks = (d->vi->height + blocky / 2 - 1) / (blocky / 2);
533
+
534
+ if (activationReason == arInitial) {
535
+ vsapi->requestFrameFilter(std::max(n - 2, 0), d->node, frameCtx);
536
+ vsapi->requestFrameFilter(std::max(n - 1, 0), d->node, frameCtx);
537
+ vsapi->requestFrameFilter(n, d->node, frameCtx);
538
+ vsapi->requestFrameFilter(n + 1, d->node, frameCtx);
539
+ vsapi->requestFrameFilter(n + 2, d->node, frameCtx);
540
+ } else if (activationReason == arAllFramesReady) {
541
+ const VSFrame *srcpp = vsapi->getFrameFilter(std::max(n - 2, 0), d->node, frameCtx);
542
+ const VSFrame *srcp = vsapi->getFrameFilter(std::max(n - 1, 0), d->node, frameCtx);
543
+ const VSFrame *srcc = vsapi->getFrameFilter(n, d->node, frameCtx);
544
+ const VSFrame *srcn = vsapi->getFrameFilter(n + 1, d->node, frameCtx);
545
+ const VSFrame *srcnn = vsapi->getFrameFilter(n + 2, d->node, frameCtx);
546
+
547
+ // first two fields are duplicates, meaning that offset 0, 1 and 5, 6 are used in the various calculations
548
+ // fields 2, 3 and 4 needs to have determined how many blocks are consecutively static
549
+ // note that comparisons are only run on a single since we can in most comparisons can eliminate
550
+ // the dotcrawl from the equation
551
+
552
+ // 0-1 2 3 4 5-6
553
+
554
+ // the complementary field can likewise be used for movement detection
555
+
556
+ /*
557
+ FIELD OFFSETS
558
+ + - + - + -
559
+ -1 0 1 2 3 4
560
+ A1 B1 B1 C1 D1 E1
561
+ A2 B2 C2 D2 D2 E2
562
+ */
563
+
564
+ VSFrame *outframe = vsapi->copyFrame(srcc, core);
565
+
566
+ vsapi->mapSetInt(vsapi->getFramePropertiesRW(outframe), "DotKillTOffset", (n + d->offset) % 5, maReplace);
567
+
568
+ if ((n + d->offset) % 5 == 0) {
569
+ // 1
570
+ applyDotcrawInverse(srcc, srcn, outframe, d->order, core, vsapi);
571
+
572
+ // 2
573
+ std::vector<int64_t> maskprev1(nxblocks * nyblocks);
574
+ std::vector<int64_t> masknext1(nxblocks * nyblocks);
575
+ std::vector<uint8_t> mask(nxblocks * nyblocks);
576
+ calcDiffMetric(srcp, srcn, maskprev1.data(), nxblocks, nyblocks, d->order, vsapi);
577
+ calcDiffMetric(srcc, srcnn, masknext1.data(), nxblocks, nyblocks, d->order, vsapi);
578
+
579
+ diffMetricToMask(mask.data(), maskprev1.data(), masknext1.data(), nxblocks, nyblocks, d->dupthresh, d->tratio, vsapi);
580
+
581
+ applyTemporalMask(outframe, srcc, srcp, srcn, mask.data(), nxblocks, nyblocks, !d->order, d->show, vsapi);
582
+ } else if ((n + d->offset) % 5 == 1) {
583
+ // 1
584
+ applyFieldBlend(srcc, srcp, outframe, d->order, core, vsapi);
585
+
586
+ // 2
587
+ std::vector<int64_t> maskprev1(nxblocks * nyblocks);
588
+ std::vector<int64_t> masknext1(nxblocks * nyblocks);
589
+ std::vector<uint8_t> mask(nxblocks * nyblocks);
590
+ calcDiffMetric(srcp, srcn, maskprev1.data(), nxblocks, nyblocks, d->order, vsapi);
591
+ calcDiffMetric(srcc, srcnn, masknext1.data(), nxblocks, nyblocks, !d->order, vsapi);
592
+
593
+ diffMetricToMask(mask.data(), maskprev1.data(), masknext1.data(), nxblocks, nyblocks, d->dupthresh, d->tratio, vsapi);
594
+
595
+ applyTemporalMask(outframe, srcc, srcp, srcn, mask.data(), nxblocks, nyblocks, !d->order, d->show, vsapi);
596
+ } else if ((n + d->offset) % 5 == 2) {
597
+ // 1
598
+ std::vector<int64_t> maskprev1(nxblocks * nyblocks);
599
+ std::vector<int64_t> masknext1(nxblocks * nyblocks);
600
+ std::vector<uint8_t> mask(nxblocks * nyblocks);
601
+ calcDiffMetric(srcc, srcpp, maskprev1.data(), nxblocks, nyblocks, d->order, vsapi);
602
+ calcDiffMetric(srcp, srcn, masknext1.data(), nxblocks, nyblocks, !d->order, vsapi);
603
+
604
+ diffMetricToMask(mask.data(), maskprev1.data(), masknext1.data(), nxblocks, nyblocks, d->dupthresh, d->tratio, vsapi);
605
+
606
+ applyTemporalMask(outframe, srcc, srcp, srcn, mask.data(), nxblocks, nyblocks, d->order, d->show, vsapi);
607
+
608
+ // 2
609
+ applyFieldBlend(srcc, srcn, outframe, !d->order, core, vsapi);
610
+ } else if ((n + d->offset) % 5 == 3) {
611
+ // 2
612
+ applyDotcrawInverse(srcc, srcp, outframe, !d->order, core, vsapi);
613
+
614
+ // 1
615
+ std::vector<int64_t> maskprev1(nxblocks * nyblocks);
616
+ std::vector<int64_t> masknext1(nxblocks * nyblocks);
617
+ std::vector<uint8_t> mask(nxblocks * nyblocks);
618
+ calcDiffMetric(srcc, srcpp, maskprev1.data(), nxblocks, nyblocks, !d->order, vsapi);
619
+ calcDiffMetric(srcp, srcn, masknext1.data(), nxblocks, nyblocks, !d->order, vsapi);
620
+
621
+ diffMetricToMask(mask.data(), maskprev1.data(), masknext1.data(), nxblocks, nyblocks, d->dupthresh, d->tratio, vsapi);
622
+
623
+ applyTemporalMask(outframe, srcc, srcp, srcn, mask.data(), nxblocks, nyblocks, d->order, d->show, vsapi);
624
+ } else if ((n + d->offset) % 5 == 4) {
625
+ // 1
626
+ {
627
+ std::vector<int64_t> maskprev1(nxblocks * nyblocks);
628
+ std::vector<int64_t> masknext1(nxblocks * nyblocks);
629
+ std::vector<uint8_t> mask(nxblocks * nyblocks);
630
+ calcDiffMetric(srcc, srcpp, maskprev1.data(), nxblocks, nyblocks, !d->order, vsapi);
631
+ calcDiffMetric(srcc, srcnn, masknext1.data(), nxblocks, nyblocks, d->order, vsapi);
632
+
633
+ diffMetricToMask(mask.data(), maskprev1.data(), masknext1.data(), nxblocks, nyblocks, d->dupthresh, d->tratio, vsapi);
634
+
635
+ applyTemporalMask(outframe, srcc, srcp, srcn, mask.data(), nxblocks, nyblocks, d->order, d->show, vsapi);
636
+ }
637
+
638
+ // 2
639
+ {
640
+ std::vector<int64_t> maskprev1(nxblocks * nyblocks);
641
+ std::vector<int64_t> masknext1(nxblocks * nyblocks);
642
+ std::vector<uint8_t> mask(nxblocks * nyblocks);
643
+ calcDiffMetric(srcpp, srcc, maskprev1.data(), nxblocks, nyblocks, !d->order, vsapi);
644
+ calcDiffMetric(srcc, srcnn, masknext1.data(), nxblocks, nyblocks, d->order, vsapi);
645
+
646
+ diffMetricToMask(mask.data(), maskprev1.data(), masknext1.data(), nxblocks, nyblocks, d->dupthresh, d->tratio, vsapi);
647
+
648
+ applyTemporalMask(outframe, srcc, srcp, srcn, mask.data(), nxblocks, nyblocks, !d->order, d->show, vsapi);
649
+ }
650
+ }
651
+
652
+ vsapi->freeFrame(srcpp);
653
+ vsapi->freeFrame(srcp);
654
+ vsapi->freeFrame(srcc);
655
+ vsapi->freeFrame(srcn);
656
+ vsapi->freeFrame(srcnn);
657
+
658
+ return outframe;
659
+ }
660
+
661
+ return nullptr;
662
+ }
663
+
664
+ static void VS_CC dotKillTFree(void *instanceData, VSCore *core, const VSAPI *vsapi) {
665
+ DotKillTData *d = reinterpret_cast<DotKillTData*>(instanceData);
666
+ vsapi->freeNode(d->node);
667
+ delete d;
668
+ }
669
+
670
+ static void VS_CC dotKillTCreate(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) {
671
+ std::unique_ptr<DotKillTData> d(new DotKillTData());
672
+
673
+ int err;
674
+ d->node = vsapi->mapGetNode(in, "clip", 0, nullptr);
675
+ d->vi = vsapi->getVideoInfo(d->node);
676
+ d->offset = vsapi->mapGetIntSaturated(in, "offset", 0, &err);
677
+ d->order = !!vsapi->mapGetInt(in, "order", 0, &err);
678
+ d->dupthresh = vsapi->mapGetIntSaturated(in, "dupthresh", 0, &err);
679
+ if (err || d->dupthresh < 0)
680
+ d->dupthresh = 64;
681
+ d->dupthresh *= d->dupthresh;
682
+ d->tratio = vsapi->mapGetIntSaturated(in, "tratio", 0, &err);
683
+ if (err || d->tratio < 1)
684
+ d->tratio = 3;
685
+ d->show = !!vsapi->mapGetInt(in, "show", 0, &err);
686
+
687
+ if (!vsh::isSameVideoPresetFormat(pfYUV420P8, &d->vi->format, core, vsapi) || !vsh::isConstantVideoFormat(d->vi)) {
688
+ vsapi->mapSetError(out, "DotKillT: only constant dimension YUV420P8 supported");
689
+ vsapi->freeNode(d->node);
690
+ return;
691
+ }
692
+
693
+ VSFilterDependency deps[] = { {d->node, rpGeneral} };
694
+ vsapi->createVideoFilter(out, "DotKillT", d->vi, dotKillTGetFrame, dotKillTFree, fmParallelRequests, deps, 1, d.get(), core);
695
+ d.release();
696
+ }
697
+
698
+ //////////////////////////////////////////
699
+ // Init
700
+
701
+ VS_EXTERNAL_API(void) VapourSynthPluginInit2(VSPlugin *plugin, const VSPLUGINAPI *vspapi) {
702
+ vspapi->configPlugin("com.vapoursynth.dotkill", "dotkill", "VapourSynth DotKill", VS_MAKE_VERSION(4, 0), VAPOURSYNTH_API_VERSION, 0, plugin);
703
+ vspapi->registerFunction("DotKillS", "clip:vnode;iterations:int:opt;", "clip:vnode", dotKillSCreate, 0, plugin);
704
+ vspapi->registerFunction("DotKillZ", "clip:vnode;order:int:opt;offset:int:opt;", "clip:vnode", dotKillZCreate, 0, plugin);
705
+ vspapi->registerFunction("DotKillT", "clip:vnode;order:int:opt;offset:int:opt;dupthresh:int:opt;tratio:int:opt;show:int:opt;", "clip:vnode", dotKillTCreate, 0, plugin);
706
+ }