Filter SDK/GradientMask
(Created page with "I'll start off with a simple source filter. It's called "GradientMask", and it produces a gradient. Here's GradientMask.cpp: #include <windows.h> #include "avisynth.h" ...") |
Revision as of 19:19, 5 June 2015
I'll start off with a simple source filter. It's called "GradientMask", and it produces a gradient.
Here's GradientMask.cpp:
#include <windows.h>
#include "avisynth.h"
class GradientMask : public IClip {
public:
GradientMask(int _w, int _h, const char* _pixel_type, int _color, IScriptEnvironment* env);
PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env);
void __stdcall GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env) {};
bool __stdcall GetParity(int n) { return false; };
int __stdcall SetCacheHints(int cachehints, int frame_range) { return 0; };
const VideoInfo& __stdcall GetVideoInfo() { return vi; };
private:
int w;
int h;
const char* pixel_type;
int color;
VideoInfo vi;
PVideoFrame frame;
};
GradientMask::GradientMask(int _w, int _h, const char* _pixel_type, int _color, IScriptEnvironment* env) :
w(_w), h(_h), pixel_type(_pixel_type), color(_color) {
memset(&vi, 0, sizeof(VideoInfo));
vi.width = w;
vi.height = h;
vi.fps_numerator = 30000;
vi.fps_denominator = 1001;
vi.num_frames = 1;
if (lstrcmpi(pixel_type, "RGB") == 0) {
vi.pixel_type = VideoInfo::CS_BGR24;
} else {
env->ThrowError("GradientMask: pixel_type must be \"RGB\"");
}
if (color > 0xffffff) {
env->ThrowError("GradientMask: color must be between 0 and %d($ffffff)", 0xffffff);
}
// the frame is filled here in the constructor since the clip consists of only one frame.
// if it would have multiple different frames you should fill them in GetFrame()
frame = env->NewVideoFrame(vi);
int y, i;
BYTE* p;
int fpitch, fheight, fwidth;
/*
y' = (r+g+b)/3
b = y + (b-y')
g = y + (g-y')
r = y + (r-y')
*/
static const int col[] = {(color >> 16) & 0xff, (color >> 8) & 0xff, (color >> 0) & 0xff};
float luma = float((col[0]+col[1]+col[2])/3.0);
/*
left pixel (i=0): [fully saturated]
blue = b - luma
green = g - luma
red = r - luma
right pixel (i=3*639-2): [almost white]
blue = i*255/(3*640) + b - luma -> 0
green = i*255/(3*640) + g - luma
red = i*255/(3*640) + r - luma
*/
p = frame->GetWritePtr();
fpitch = frame->GetPitch();
fwidth = frame->GetRowSize(); // in bytes
fheight = frame->GetHeight(); // in pixels
for (y=0; y<fheight; y++) {
for (i=0; i<fwidth; i+=3) {
p[i] = int(min(i * 255.0/float(fwidth) + max(col[2] - luma,0),255));
p[i+1] = int(min(i * 255.0/float(fwidth) + max(col[1] - luma,0),255));
p[i+2] = int(min(i * 255.0/float(fwidth) + max(col[0] - luma,0),255));
}
p += fpitch;
}
}
PVideoFrame __stdcall GradientMask::GetFrame(int n, IScriptEnvironment* env) { return frame; }
static AVSValue __cdecl Create_GradientMask(AVSValue args, void* user_data, IScriptEnvironment* env) {
return new GradientMask(args[0].AsInt(640), args[1].AsInt(480), args[2].AsString("RGB"), args[3].AsInt(0), env);
}
const AVS_Linkage *AVS_linkage = 0;
extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit3(IScriptEnvironment* env, const AVS_Linkage* const vectors) {
AVS_linkage = vectors;
env->AddFunction("GradientMask", "[width]i[height]i[pixel_type]s[color]i", Create_GradientMask, 0);
return "GradientMask sample plugin";
}
Line by line breakdown
Here's a line-by-line breakdown of GradientMask.cpp.
As explained in the InvertNeg example, an Avisynth filter is simply a C++ class implementing the IClip interface. The class GenericVideoFilter is a simple do-nothing filter defined in avisynth.h. It derives from IClip and implements all four methods (being being GetFrame, GetAudio, GetParity and SetCacheHints). Filters that have a parent clip and thus can inherit from GenericVideoFilter rather than directly from IClip; this saves you from having to implement methods that you don't care about. Source filters don't have a parent clip so you need to derive them from IClip in that case.
So since we are developing a source filter we need to derive from IClip, fill out a VideoInfo and implement all four methods (instead of overriding the ones we need).
In the example below a gradientmask is created. It fades from a fully saturated color to white. The gradientmask will be created in the constructor and returned only once.
#include <windows.h>
#include "avisynth.h"
class GradientMask : public IClip {
public:
GradientMask(int _w, int _h, const char* _pixel_type, int _color, IScriptEnvironment* env);
PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env);
void __stdcall GetAudio(void* buf, __int64 start, __int64 count, IScriptEnvironment* env) {};
bool __stdcall GetParity(int n) { return false; };
int __stdcall SetCacheHints(int cachehints, int frame_range) { return 0; };
const VideoInfo& __stdcall GetVideoInfo() { return vi; };
private:
int w;
int h;
const char* pixel_type;
int color;
VideoInfo vi;
PVideoFrame frame;
};
GradientMask::GradientMask(int _w, int _h, const char* _pixel_type, int _color, IScriptEnvironment* env) :
w(_w), h(_h), pixel_type(_pixel_type), color(_color) {
memset(&vi, 0, sizeof(VideoInfo));
vi.width = w;
vi.height = h;
vi.fps_numerator = 30000;
vi.fps_denominator = 1001;
vi.num_frames = 1;
if (lstrcmpi(pixel_type, "RGB") == 0) {
vi.pixel_type = VideoInfo::CS_BGR24;
} else {
env->ThrowError("GradientMask: pixel_type must be \"RGB\"");
}
if (color > 0xffffff) {
env->ThrowError("GradientMask: color must be between 0 and %d($ffffff)", 0xffffff);
}
// the frame is filled here in the constructor since the clip consists of only one frame.
// if it would have multiple different frames you should fill them in GetFrame()
frame = env->NewVideoFrame(vi);
int y, i;
BYTE* p;
int fpitch, fheight, fwidth;
/*
y' = (r+g+b)/3
b = y + (b-y')
g = y + (g-y')
r = y + (r-y')
*/
static const int col[] = {(color >> 16) & 0xff, (color >> 8) & 0xff, (color >> 0) & 0xff};
float luma = float((col[0]+col[1]+col[2])/3.0);
/*
left pixel (i=0): [fully saturated]
blue = b - luma
green = g - luma
red = r - luma
right pixel (i=3*639-2): [almost white]
blue = i*255/(3*640) + b - luma -> 0
green = i*255/(3*640) + g - luma
red = i*255/(3*640) + r - luma
*/
p = frame->GetWritePtr();
fpitch = frame->GetPitch();
fwidth = frame->GetRowSize(); // in bytes
fheight = frame->GetHeight(); // in pixels
for (y=0; y<fheight; y++) {
for (i=0; i<fwidth; i+=3) {
p[i] = int(min(i * 255.0/float(fwidth) + max(col[2] - luma,0),255));
p[i+1] = int(min(i * 255.0/float(fwidth) + max(col[1] - luma,0),255));
p[i+2] = int(min(i * 255.0/float(fwidth) + max(col[0] - luma,0),255));
}
p += fpitch;
}
}
PVideoFrame __stdcall GradientMask::GetFrame(int n, IScriptEnvironment* env) { return frame; }
static AVSValue __cdecl Create_GradientMask(AVSValue args, void* user_data, IScriptEnvironment* env) {
return new GradientMask(args[0].AsInt(640), args[1].AsInt(480), args[2].AsString("RGB"), args[3].AsInt(0), env);
}
const AVS_Linkage *AVS_linkage = 0;
extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit3(IScriptEnvironment* env, const AVS_Linkage* const vectors) {
AVS_linkage = vectors;
env->AddFunction("GradientMask", "[width]i[height]i[pixel_type]s[color]i", Create_GradientMask, 0);
return "GradientMask sample plugin";
}