church_archive_script/Programs/sdk/examples/invert_example.c
2023-11-29 16:12:35 +00:00

173 lines
8 KiB
C

//////////////////////////////////////////
// This file contains a simple invert
// filter that's commented to show
// the basics of the filter api.
// This file may make more sense when
// read from the bottom and up.
#include <stdlib.h>
#include "VapourSynth4.h"
#include "VSHelper4.h"
typedef struct {
VSNode *node;
int enabled;
} InvertData;
// This is the main function that gets called when a frame should be produced. It will, in most cases, get
// called several times to produce one frame. This state is being kept track of by the value of
// activationReason. The first call to produce a certain frame n is always arInitial. In this state
// you should request all the input frames you need. Always do it in ascending order to play nice with the
// upstream filters.
// Once all frames are ready, the filter will be called with arAllFramesReady. It is now time to
// do the actual processing.
static const VSFrame *VS_CC invertGetFrame(int n, int activationReason, void *instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) {
InvertData *d = (InvertData *)instanceData;
if (activationReason == arInitial) {
// Request the source frame on the first call
vsapi->requestFrameFilter(n, d->node, frameCtx);
} else if (activationReason == arAllFramesReady) {
const VSFrame *src = vsapi->getFrameFilter(n, d->node, frameCtx);
// The reason we query this on a per frame basis is because we want our filter
// to accept clips with varying dimensions. If we reject such content using d->vi
// would be easier.
const VSVideoFormat *fi = vsapi->getVideoFrameFormat(src);
int height = vsapi->getFrameHeight(src, 0);
int width = vsapi->getFrameWidth(src, 0);
// When creating a new frame for output it is VERY EXTREMELY SUPER IMPORTANT to
// supply the "dominant" source frame to copy properties from. Frame props
// are an essential part of the filter chain and you should NEVER break it.
VSFrame *dst = vsapi->newVideoFrame(fi, width, height, src, core);
// It's processing loop time!
// Loop over all the planes
int plane;
for (plane = 0; plane < fi->numPlanes; plane++) {
const uint8_t *srcp = vsapi->getReadPtr(src, plane);
ptrdiff_t src_stride = vsapi->getStride(src, plane);
uint8_t *dstp = vsapi->getWritePtr(dst, plane);
ptrdiff_t dst_stride = vsapi->getStride(dst, plane); // note that if a frame has the same dimensions and format, the stride is guaranteed to be the same. int dst_stride = src_stride would be fine too in this filter.
// Since planes may be subsampled you have to query the height of them individually
int h = vsapi->getFrameHeight(src, plane);
int y;
int w = vsapi->getFrameWidth(src, plane);
int x;
for (y = 0; y < h; y++) {
for (x = 0; x < w; x++)
dstp[x] = ~srcp[x];
dstp += dst_stride;
srcp += src_stride;
}
}
// Release the source frame
vsapi->freeFrame(src);
// A reference is consumed when it is returned, so saving the dst reference somewhere
// and reusing it is not allowed.
return dst;
}
return NULL;
}
// Free all allocated data on filter destruction
static void VS_CC invertFree(void *instanceData, VSCore *core, const VSAPI *vsapi) {
InvertData *d = (InvertData *)instanceData;
vsapi->freeNode(d->node);
free(d);
}
// This function is responsible for validating arguments and creating a new filter
static void VS_CC invertCreate(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) {
InvertData d;
InvertData *data;
int err;
// Get a clip reference from the input arguments. This must be freed later.
d.node = vsapi->mapGetNode(in, "clip", 0, 0);
const VSVideoInfo *vi = vsapi->getVideoInfo(d.node);
// In this first version we only want to handle 8bit integer formats. Note that
// vi->format can be 0 if the input clip can change format midstream.
if (!vsh_isConstantVideoFormat(vi) || vi->format.sampleType != stInteger || vi->format.bitsPerSample != 8) {
vsapi->mapSetError(out, "Invert: only constant format 8bit integer input supported");
vsapi->freeNode(d.node);
return;
}
// If a property read fails for some reason (index out of bounds/wrong type)
// then err will have flags set to indicate why and 0 will be returned. This
// can be very useful to know when having optional arguments. Since we have
// strict checking because of what we wrote in the argument string, the only
// reason this could fail is when the value wasn't set by the user.
// And when it's not set we want it to default to enabled.
d.enabled = !!vsapi->mapGetInt(in, "enable", 0, &err);
if (err)
d.enabled = 1;
// Let's pretend the only allowed values are 1 or 0...
if (d.enabled < 0 || d.enabled > 1) {
vsapi->mapSetError(out, "Invert: enabled must be 0 or 1");
vsapi->freeNode(d.node);
return;
}
// I usually keep the filter data struct on the stack and don't allocate it
// until all the input validation is done.
data = (InvertData *)malloc(sizeof(d));
*data = d;
// Creates a new filter and returns a reference to it. Always pass on the in and out
// arguments or unexpected things may happen. The name should be something that's
// easy to connect to the filter, like its function name.
// The three function pointers handle initialization, frame processing and filter destruction.
// The filtermode is very important to get right as it controls how threading of the filter
// is handled. In general you should only use fmParallel whenever possible. This is if you
// need to modify no shared data at all when the filter is running.
// For more complicated filters, fmParallelRequests is usually easier to achieve as it can
// be prefetched in parallel but the actual processing is serialized.
// The others can be considered special cases where fmFrameState is useful to source filters and
// fmUnordered is useful when a filter's state may change even when deciding which frames to
// prefetch (such as a cache filter).
VSFilterDependency deps[] = {{d.node, rpStrictSpatial}};
vsapi->createVideoFilter(out, "Invert", vi, invertGetFrame, invertFree, fmParallel, deps, 1, data, core);
}
//////////////////////////////////////////
// Init
// This is the entry point that is called when a plugin is loaded. You are only supposed
// to call the two provided functions here.
// configFunc sets the id, namespace, and long name of the plugin (the last 3 arguments
// never need to be changed for a normal plugin).
//
// id: Needs to be a "reverse" url and unique among all plugins.
// It is inspired by how android packages identify themselves.
// If you don't own a domain then make one up that's related
// to the plugin name.
//
// namespace: Should only use [a-z_] and not be too long.
//
// full name: Any name that describes the plugin nicely.
//
// registerFunction is called once for each function you want to register. Function names
// should be CamelCase. The argument string has this format:
// name:type; or name:type:flag1:flag2....;
// All argument name should be lowercase and only use [a-z_].
// The valid types are int,float,data,clip,frame,func. [] can be appended to allow arrays
// of type to be passed (numbers:int[])
// The available flags are opt, to make an argument optional, empty, which controls whether
// or not empty arrays are accepted
VS_EXTERNAL_API(void) VapourSynthPluginInit2(VSPlugin *plugin, const VSPLUGINAPI *vspapi) {
vspapi->configPlugin("com.example.invert", "invert", "VapourSynth Invert Example", VS_MAKE_VERSION(1, 0), VAPOURSYNTH_API_VERSION, 0, plugin);
vspapi->registerFunction("Filter", "clip:vnode;enabled:int:opt;", "clip:vnode;", invertCreate, NULL, plugin);
}