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