1 /*
2 VapourSynth adaption by Fredrik Mellbin
3
4 Copyright(c) 2013 Victor Efimov
5
6 Permission is hereby granted, free of charge, to any person
7 obtaining a copy of this software and associated documentation
8 files(the "Software"), to deal in the Software without
9 restriction, including without limitation the rights to use,
10 copy, modify, merge, publish, distribute, sublicense, and / or sell
11 copies of the Software, and to permit persons to whom the
12 Software is furnished to do so, subject to the following
13 conditions :
14
15 The above copyright notice and this permission notice shall be
16 included in all copies or substantial portions of the Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25 OTHER DEALINGS IN THE SOFTWARE.
26 */
27
28 #include <limits>
29 #include "shared.h"
30
31 #define CLENSE_RETERROR(x) do { vsapi->setError(out, (x)); vsapi->freeNode(d.cnode); vsapi->freeNode(d.pnode); vsapi->freeNode(d.nnode); return; } while (0)
32 #define CLAMP(value, lower, upper) do { if (value < lower) value = lower; else if (value > upper) value = upper; } while(0)
33
34 typedef struct {
35 VSNodeRef *cnode;
36 VSNodeRef *pnode;
37 VSNodeRef *nnode;
38 const VSVideoInfo *vi;
39 int mode;
40 int process[3];
41 } ClenseData;
42
43
clenseInit(VSMap * in,VSMap * out,void ** instanceData,VSNode * node,VSCore * core,const VSAPI * vsapi)44 static void VS_CC clenseInit(VSMap *in, VSMap *out, void **instanceData, VSNode *node, VSCore *core, const VSAPI *vsapi) {
45 ClenseData *d = static_cast<ClenseData *>(*instanceData);
46 vsapi->setVideoInfo(d->vi, 1, node);
47 }
48
49 struct PlaneProc {
50 template<typename T>
clenseProcessPlanePlaneProc51 static void clenseProcessPlane(T* VS_RESTRICT pDst, const T* VS_RESTRICT pSrc, const T* VS_RESTRICT pRef1, const T* VS_RESTRICT pRef2, int stride, int width, int height) {
52 for (int y = 0; y < height; ++y) {
53 for (int x = 0; x < width; ++x)
54 pDst[x] = std::min(std::max(pSrc[x], std::min(pRef1[x], pRef2[x])), std::max(pRef1[x], pRef2[x]));
55 pDst += stride;
56 pSrc += stride;
57 pRef1 += stride;
58 pRef2 += stride;
59 }
60 }
61 };
62
63 struct PlaneProcFB {
64 template<typename T>
clenseProcessPlanePlaneProcFB65 static void clenseProcessPlane(T* VS_RESTRICT pDst, const T* VS_RESTRICT pSrc, const T* VS_RESTRICT pRef1, const T* VS_RESTRICT pRef2, int stride, int width, int height) {
66 for (int y = 0; y < height; ++y) {
67 for (int x = 0; x < width; ++x) {
68 T minref = std::min(pRef1[x], pRef2[x]);
69 T maxref = std::max(pRef1[x], pRef2[x]);
70 int lowref = minref * 2 - pRef2[x];
71 int upref = maxref * 2 - pRef2[x];
72 T src = pSrc[x];
73 CLAMP(src, std::max<int>(lowref, std::numeric_limits<T>::min()), std::min<int>(upref, std::numeric_limits<T>::max()));
74 pDst[x] = src;
75 }
76
77 pDst += stride;
78 pSrc += stride;
79 pRef1 += stride;
80 pRef2 += stride;
81 }
82 }
83 };
84
85 template<typename T, typename Processor>
clenseGetFrame(int n,int activationReason,void ** instanceData,void ** frameData,VSFrameContext * frameCtx,VSCore * core,const VSAPI * vsapi)86 static const VSFrameRef *VS_CC clenseGetFrame(int n, int activationReason, void **instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) {
87 ClenseData *d = static_cast<ClenseData *>(*instanceData);
88
89 if (activationReason == arInitial) {
90 if (d->mode == cmNormal) {
91 if (n >= 1 && (!d->vi->numFrames || n <= d->vi->numFrames - 2)) {
92 *frameData = reinterpret_cast<void *>(1);
93 vsapi->requestFrameFilter(n - 1, d->pnode, frameCtx);
94 vsapi->requestFrameFilter(n, d->cnode, frameCtx);
95 vsapi->requestFrameFilter(n + 1, d->nnode, frameCtx);
96 } else {
97 vsapi->requestFrameFilter(n, d->cnode, frameCtx);
98 }
99 } else if (d->mode == cmForward) {
100 vsapi->requestFrameFilter(n, d->cnode, frameCtx);
101 if (!d->vi->numFrames || n <= d->vi->numFrames - 3) {
102 *frameData = reinterpret_cast<void *>(1);
103 vsapi->requestFrameFilter(n + 1, d->cnode, frameCtx);
104 vsapi->requestFrameFilter(n + 2, d->cnode, frameCtx);
105 }
106 } else if (d->mode == cmBackward) {
107 if (n >= 2) {
108 *frameData = reinterpret_cast<void *>(1);
109 vsapi->requestFrameFilter(n - 2, d->cnode, frameCtx);
110 vsapi->requestFrameFilter(n - 1, d->cnode, frameCtx);
111 }
112 vsapi->requestFrameFilter(n, d->cnode, frameCtx);
113 }
114 } else if (activationReason == arAllFramesReady) {
115 const VSFrameRef *src = nullptr, *frame1 = nullptr, *frame2 = nullptr;
116
117 if (!*frameData) // skip processing on first/last frames
118 return vsapi->getFrameFilter(n, d->cnode, frameCtx);
119
120 if (d->mode == cmNormal) {
121 frame1 = vsapi->getFrameFilter(n - 1, d->pnode, frameCtx);
122 src = vsapi->getFrameFilter(n, d->cnode, frameCtx);
123 frame2 = vsapi->getFrameFilter(n + 1, d->nnode, frameCtx);
124 } else if (d->mode == cmForward) {
125 src = vsapi->getFrameFilter(n, d->cnode, frameCtx);
126 frame1 = vsapi->getFrameFilter(n + 1, d->cnode, frameCtx);
127 frame2 = vsapi->getFrameFilter(n + 2, d->cnode, frameCtx);
128 } else if (d->mode == cmBackward) {
129 frame2 = vsapi->getFrameFilter(n - 2, d->cnode, frameCtx);
130 frame1 = vsapi->getFrameFilter(n - 1, d->cnode, frameCtx);
131 src = vsapi->getFrameFilter(n, d->cnode, frameCtx);
132 }
133
134 const int pl[] = { 0, 1, 2 };
135 const VSFrameRef *fr[] = { d->process[0] ? nullptr : src, d->process[1] ? nullptr : src, d->process[2] ? nullptr : src };
136
137 VSFrameRef *dst = vsapi->newVideoFrame2(d->vi->format, d->vi->width, d->vi->height, fr, pl, src, core);
138
139 int numPlanes = d->vi->format->numPlanes;
140 for (int i = 0; i < numPlanes; i++) {
141 if (d->process[i]) {
142 Processor::template clenseProcessPlane<T>(
143 reinterpret_cast<T *>(vsapi->getWritePtr(dst, i)),
144 reinterpret_cast<const T *>(vsapi->getReadPtr(src, i)),
145 reinterpret_cast<const T *>(vsapi->getReadPtr(frame1, i)),
146 reinterpret_cast<const T *>(vsapi->getReadPtr(frame2, i)),
147 vsapi->getStride(dst, i)/sizeof(T),
148 vsapi->getFrameWidth(dst, i),
149 vsapi->getFrameHeight(dst, i));
150 }
151 }
152
153 vsapi->freeFrame(src);
154 vsapi->freeFrame(frame1);
155 vsapi->freeFrame(frame2);
156
157 return dst;
158 }
159
160 return nullptr;
161 }
162
clenseFree(void * instanceData,VSCore * core,const VSAPI * vsapi)163 static void VS_CC clenseFree(void *instanceData, VSCore *core, const VSAPI *vsapi) {
164 ClenseData *d = static_cast<ClenseData *>(instanceData);
165 vsapi->freeNode(d->cnode);
166 vsapi->freeNode(d->pnode);
167 vsapi->freeNode(d->nnode);
168 delete d;
169 }
170
clenseCreate(const VSMap * in,VSMap * out,void * userData,VSCore * core,const VSAPI * vsapi)171 void VS_CC clenseCreate(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) {
172 ClenseData d = {};
173 ClenseData *data;
174 int err;
175 int n, m, o;
176 int i;
177
178 d.mode = int64ToIntS(reinterpret_cast<intptr_t>(userData));
179 d.cnode = vsapi->propGetNode(in, "clip", 0, nullptr);
180 d.vi = vsapi->getVideoInfo(d.cnode);
181 if (!isConstantFormat(d.vi))
182 CLENSE_RETERROR("Clense: only constant format input supported");
183
184 if (d.mode == cmNormal) {
185 d.pnode = vsapi->propGetNode(in, "previous", 0, &err);
186 if (err)
187 d.pnode = vsapi->cloneNodeRef(d.cnode);
188 d.nnode = vsapi->propGetNode(in, "next", 0, &err);
189 if (err)
190 d.nnode = vsapi->cloneNodeRef(d.cnode);
191 }
192
193 if (d.pnode && !isSameFormat(d.vi, vsapi->getVideoInfo(d.pnode)))
194 CLENSE_RETERROR("Clense: previous clip doesn't have the same format as the main clip");
195
196 if (d.nnode && !isSameFormat(d.vi, vsapi->getVideoInfo(d.nnode)))
197 CLENSE_RETERROR("Clense: previous clip doesn't have the same format as the main clip");
198
199 n = d.vi->format->numPlanes;
200 m = vsapi->propNumElements(in, "planes");
201
202 for (i = 0; i < 3; i++)
203 d.process[i] = m <= 0;
204
205 for (i = 0; i < m; i++) {
206 o = int64ToIntS(vsapi->propGetInt(in, "planes", i, nullptr));
207
208 if (o < 0 || o >= n)
209 CLENSE_RETERROR("Clense: plane index out of range");
210
211 if (d.process[o])
212 CLENSE_RETERROR("Clense: plane specified twice");
213
214 d.process[o] = 1;
215 }
216
217 VSFilterGetFrame getFrameFunc = nullptr;
218 if (d.vi->format->sampleType == stInteger) {
219 if (d.mode == cmNormal) {
220 switch (d.vi->format->bitsPerSample) {
221 case 8: getFrameFunc = clenseGetFrame<uint8_t, PlaneProc>; break;
222 case 16: getFrameFunc = clenseGetFrame<uint16_t, PlaneProc>; break;
223 }
224 } else {
225 switch (d.vi->format->bitsPerSample) {
226 case 8: getFrameFunc = clenseGetFrame<uint8_t, PlaneProcFB>; break;
227 case 16: getFrameFunc = clenseGetFrame<uint16_t, PlaneProcFB>; break;
228 }
229 }
230 }
231
232 if (!getFrameFunc)
233 CLENSE_RETERROR("Clense: only 8 and 16 bit integer input supported");
234
235 data = new ClenseData(d);
236
237 vsapi->createFilter(in, out, "Clense", clenseInit, getFrameFunc, clenseFree, fmParallel, 0, data, core);
238 }
239