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