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" ...") |
(→Line by line breakdown) |
||
Line 103: | Line 103: | ||
Here's a line-by-line breakdown of GradientMask.cpp. | 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. | + | As explained in the [[Filter_SDK/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). | 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). |
Revision as of 19:20, 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 Filter_SDK/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"; }