1 /*
2  * Simple morphological filters
3  *
4  * Copyright (c) 2014, Martin Herkt <lachs0r@srsfckn.biz>
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
11  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
12  * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
13  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
14  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
15  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16  * PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <math.h>
20 #include <stdio.h>
21 
22 #include "VapourSynth.h"
23 #include "VSHelper.h"
24 
25 #include "morpho.h"
26 #include "morpho_selems.h"
27 #include "morpho_filters.h"
28 
MorphoCreate(const VSMap * in,VSMap * out,void * userData,VSCore * core,const VSAPI * vsapi)29 static void VS_CC MorphoCreate(const VSMap *in, VSMap *out, void *userData,
30                                VSCore *core, const VSAPI *vsapi)
31 {
32     MorphoData d, *data;
33     char msg[80];
34     int err;
35 
36     int shapec;
37     for (shapec = 0; SElemFuncs[shapec + 1] != NULL; shapec++);
38 
39     d.node = vsapi->propGetNode(in, "clip", 0, 0);
40     d.vi = *vsapi->getVideoInfo(d.node);
41 
42     if (!d.vi.format) {
43         sprintf(msg, "Only constant format input supported");
44         goto error;
45     }
46 
47     if (d.vi.format->sampleType != stInteger ||
48         d.vi.format->bytesPerSample > 2) {
49 
50         sprintf(msg, "Only 8-16 bit int formats supported");
51         goto error;
52     }
53 
54     d.size = int64ToIntS(vsapi->propGetInt(in, "size", 0, &err));
55 
56     if (err)
57         d.size = 5;
58 
59     if (d.size < 2) {
60         sprintf(msg, "Structuring element size must be greater than 1");
61         goto error;
62     }
63 
64     d.shape = int64ToIntS(vsapi->propGetInt(in, "shape", 0, &err));
65 
66     if (err)
67         d.shape = 0;
68 
69     if (d.shape < 0 || d.shape > shapec) {
70         sprintf(msg, "Structuring element shape must be in range 0-%d",
71                 shapec);
72         goto error;
73     }
74 
75     d.filter = (uintptr_t)userData;
76 
77     data = malloc(sizeof(d));
78     *data = d;
79 
80     vsapi->createFilter(in, out, FilterNames[(uintptr_t)userData], MorphoInit,
81                         MorphoGetFrame, MorphoFree, fmParallel, 0, data, core);
82 
83     return;
84 
85 error:
86     vsapi->freeNode(d.node);
87     vsapi->setError(out, msg);
88 }
89 
MorphoInit(VSMap * in,VSMap * out,void ** instanceData,VSNode * node,VSCore * core,const VSAPI * vsapi)90 static void VS_CC MorphoInit(VSMap *in, VSMap *out, void **instanceData,
91                              VSNode *node, VSCore *core, const VSAPI *vsapi)
92 {
93     int pads;
94 
95     MorphoData *d = (MorphoData *) * instanceData;
96     vsapi->setVideoInfo(&d->vi, 1, node);
97 
98     pads = d->size + (d->size % 2 == 0);
99 
100     d->selem = calloc(1, sizeof(uint8_t) * pads * pads);
101     if (!d->selem) {
102         vsapi->setError(out, "Failed to allocate structuring element");
103         return;
104     }
105 
106     SElemFuncs[d->shape](d->selem, d->size);
107 }
108 
MorphoGetFrame(int n,int activationReason,void ** instanceData,void ** frameData,VSFrameContext * frameCtx,VSCore * core,const VSAPI * vsapi)109 static const VSFrameRef *VS_CC MorphoGetFrame(int n, int activationReason,
110                                               void **instanceData,
111                                               void **frameData,
112                                               VSFrameContext *frameCtx,
113                                               VSCore *core,
114                                               const VSAPI *vsapi)
115 {
116     MorphoData *d = (MorphoData *) * instanceData;
117 
118     if (activationReason == arInitial) {
119         vsapi->requestFrameFilter(n, d->node, frameCtx);
120     } else if (activationReason == arAllFramesReady) {
121         const VSFrameRef *src = vsapi->getFrameFilter(n, d->node, frameCtx);
122         VSFrameRef *dst = vsapi->newVideoFrame(d->vi.format, d->vi.width,
123                                                d->vi.height, src, core);
124 
125         int i;
126 
127         for (i = 0; i < d->vi.format->numPlanes; i++) {
128             const uint8_t *srcp = vsapi->getReadPtr(src, i);
129             uint8_t *dstp = vsapi->getWritePtr(dst, i);
130             int width = vsapi->getFrameWidth(src, i);
131             int height = vsapi->getFrameHeight(src, i);
132             int stride = vsapi->getStride(src, i);
133 
134             FilterFuncs[d->filter](srcp, dstp, width, height, stride, d);
135         }
136 
137         vsapi->freeFrame(src);
138 
139         return dst;
140     }
141 
142     return 0;
143 }
144 
MorphoFree(void * instanceData,VSCore * core,const VSAPI * vsapi)145 static void VS_CC MorphoFree(void *instanceData, VSCore *core,
146                              const VSAPI *vsapi)
147 {
148     MorphoData *d = (MorphoData *)instanceData;
149 
150     vsapi->freeNode(d->node);
151     free(d->selem);
152     free(d);
153 }
154 
VapourSynthPluginInit(VSConfigPlugin configFunc,VSRegisterFunction registerFunc,VSPlugin * plugin)155 VS_EXTERNAL_API(void) VapourSynthPluginInit(VSConfigPlugin configFunc,
156                                             VSRegisterFunction registerFunc,
157                                             VSPlugin *plugin)
158 {
159     int i;
160     static const char *params = "clip:clip;size:int:opt;shape:int:opt";
161 
162     configFunc("biz.srsfckn.morpho", "morpho",
163                "Simple morphological filters.",
164                VAPOURSYNTH_API_VERSION, 1, plugin);
165 
166     for (i = 0; FilterFuncs[i] && FilterNames[i]; i++) {
167         registerFunc(FilterNames[i], params, MorphoCreate,
168                      (void*)(uintptr_t)i, plugin);
169     }
170 }
171