SimpleSample ============ I have rewritten SimpleSample for AviSynth 2.6. I changed it into an in place filter (so the source is overwritten). It draws a white square in the center of the clip and it supports all colorformats. Here's simplesample.cpp: :: #include #include "avisynth.h" class SimpleSample : public GenericVideoFilter { int SquareSize; public: SimpleSample(PClip _child, int _SquareSize, IScriptEnvironment* env); ~SimpleSample(); PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env); }; SimpleSample::SimpleSample(PClip _child, int _SquareSize, IScriptEnvironment* env) : GenericVideoFilter(_child), SquareSize(_SquareSize) { if (vi.widthThrowError("SimpleSample: square doesn't fit into the clip!"); } } SimpleSample::~SimpleSample() {} PVideoFrame __stdcall SimpleSample::GetFrame(int n, IScriptEnvironment* env) { PVideoFrame src = child->GetFrame(n, env); env->MakeWritable(&src); unsigned char* srcp = src->GetWritePtr(); int src_pitch = src->GetPitch(); int src_width = src->GetRowSize(); int src_height = src->GetHeight(); int w, h, woffset; if (vi.IsRGB24()) { srcp = srcp + (src_height/2 - SquareSize/2) * src_pitch; woffset = src_width/2 - 3*SquareSize/2; for (h=0; hGetWritePtr(); srcp = srcp + (src_height/2 - SquareSize/2) * src_pitch/4; woffset = src_width/8 - SquareSize/2; for (h=0; hGetWritePtr(planes[p]); src_pitch = src->GetPitch(planes[p]); src_width = src->GetRowSize(planes[p]); src_height = src->GetHeight(planes[p]); width_sub = vi.GetPlaneWidthSubsampling(planes[p]); height_sub = vi.GetPlaneHeightSubsampling(planes[p]); srcp = srcp + (src_height/2 - (SquareSize>>height_sub)/2) * src_pitch; woffset = src_width/2 - (SquareSize>>width_sub)/2; for (h=0; h<(SquareSize>>height_sub); h++) { for (w=0; w<(SquareSize>>width_sub); w++) { srcp[woffset + w] = square_value[p]; } srcp += src_pitch; } } } return src; } AVSValue __cdecl Create_SimpleSample(AVSValue args, void* user_data, IScriptEnvironment* env) { return new SimpleSample(args[0].AsClip(), args[1].AsInt(100), 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("SimpleSample", "c[size]i", Create_SimpleSample, 0); return "SimpleSample plugin"; } Compile this file into a DLL named InvertNeg.dll. See :doc:`compiling instructions `. Now create an Avisynth script which looks something like this: :: LoadPlugin("d:\path\simplesample.dll") Colorbars().Trim(0,1) ConvertTORGB32() # ConvertTOYV411() SimpleSample(100) Line by line breakdown ---------------------- Here's a line-by-line breakdown of simplesample.cpp. I won't repeat the comments in the previous example InvertNeg.cpp, so read that first if needed. The declaration of the class is as follows :: class SimpleSample : public GenericVideoFilter { int SquareSize; public: SimpleSample(PClip _child, int _SquareSize, IScriptEnvironment* env); ~SimpleSample(); PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env); }; With respect to our previous example there are two differences. It contains a private data member 'SquareSize' (members are private by default). Note that this is our parameter variable. These have to be declared here. The member function ~SimpleSample() is the declaration of the destructor. It fulfills the opposite functionality as the constructor. It is automatically called when the filter is destroyed. It is used to release allocated memory when the filter is destroyed. This is needed when there is memory allocated in the constructor. This is not the case in our filter, so we didn't need to declare it. :: SimpleSample::~SimpleSample() {} This is the actual destructor. You can release allocated memory here using the operator delete. :: SimpleSample::SimpleSample(PClip _child, int _SquareSize, IScriptEnvironment* env) : GenericVideoFilter(_child), SquareSize(_SquareSize) { if (vi.widthThrowError("SimpleSample: square doesn't fit into the clip!"); } } This is the constructor. It initializes the value of SquareSize with the parameter that is passed to it (which is called _SquareSize here). It also checks whether the square which will be drawn fits in the frame, otherwise it will return an error. :: unsigned char* srcp = src->GetWritePtr(); int src_pitch = src->GetPitch(); int src_width = src->GetRowSize(); int src_height = src->GetHeight(); The default value of plane is PLANAR_Y (= 0) for the functions GetReadPtr, GetWritePtr, GetPitch, GetRowSize and GetHeight. For planar formats this is the luma plane and for interleaved formats this is the whole frame. :: if (vi.IsRGB24()) { When the clip has color format RGB24 the code path is taken. For RGB24 each pixel is represented by three bytes, blue, green and red (in that order). RGB is up side down, so srcp[0] will be the bottom-left pixel. :: srcp = srcp + (src_height/2 - SquareSize/2) * src_pitch; The pointer is moved to the lower line of the square that will be drawn. Remember that src_height is the height in pixels. :: woffset = src_width/2 - 3*SquareSize/2; The offset is calculated of the left most byte of the square that will be drawn. Remember that src_width is the width in bytes. Since SquareSize is specified in pixels, the corresponding number of bytes is 3*SquareSize. :: for (h=0; hGetWritePtr(); srcp = srcp + (src_height/2 - SquareSize/2) * src_pitch/4; woffset = src_width/8 - SquareSize/2; for (h=0; hGetWritePtr(planes[p]); src_pitch = src->GetPitch(planes[p]); src_width = src->GetRowSize(planes[p]); src_height = src->GetHeight(planes[p]); width_sub = vi.GetPlaneWidthSubsampling(planes[p]); height_sub = vi.GetPlaneHeightSubsampling(planes[p]); Since the planes have unequal width (measured in bytes; same for height) we will need their subsampling. The functions :doc:`GetPlaneWidthSubsampling ` and :doc:`GetPlaneHeightSubsampling ` are new in the 2.6 api. They return the horizontal and vertical subsampling of the plane compared to the luma plane. The subsampling of the formats is: +--------------+------------------------------------+-------------------------------------+ | color format | GetPlaneWidthSubsampling(PLANAR_U) | GetPlaneHeightSubsampling(PLANAR_U) | +==============+====================================+=====================================+ | YV24 | 0 | 0 | +--------------+------------------------------------+-------------------------------------+ | YV16 | 1 | 0 | +--------------+------------------------------------+-------------------------------------+ | YV12 | 1 | 1 | +--------------+------------------------------------+-------------------------------------+ | YV411 | 2 | 1 | +--------------+------------------------------------+-------------------------------------+ | Y8 | 0 ? | 0 ? | +--------------+------------------------------------+-------------------------------------+ The chroma planes PLANAR_U and PLANAR_V have the same subsampling. Also GetPlaneWidthSubsampling(PLANAR_Y) = GetPlaneHeightSubsampling(PLANAR_Y) = 0 since PLANAR_Y is not subsampled. :: srcp = srcp + (src_height/2 - (SquareSize>>height_sub)/2) * src_pitch; Note SquareSize is specified in pixels and src_height the height of the samples of plane p. Let's look at YV12 for example. For the luma plane we have SquareSize>>0 = SquareSize since there is no subsampling and for the chroma planes we have SquareSize>>1 = SquareSize/2 since there are twice as many luma samples as chroma samples horizontally. :: woffset = src_width/2 - (SquareSize>>width_sub)/2; The vertical subsampling is taken into account again. :: for (h=0; h<(SquareSize>>height_sub); h++) { for (w=0; w<(SquareSize>>width_sub); w++) { srcp[woffset + w] = square_value[p]; } srcp += src_pitch; } } } Here the white square is drawn. The luma pixels will get the value square_value[PLANAR_Y] = 235, and the chroma pixels will get the value square_value[PLANAR_U] = 128 (idem for PLANAR_V). :: AVSValue __cdecl Create_SimpleSample(AVSValue args, void* user_data, IScriptEnvironment* env) { return new SimpleSample(args[0].AsClip(), args[1].AsInt(100), env); } args[0], args[1], ... are the arguments of the filter. Here there are two arguments. The first one is of type clip and the second one of type int (with default value 100). You can pre-process or post-process your variables (and clips) here too. See DirectShowSource for some examples. :: extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit3(IScriptEnvironment* env, const AVS_Linkage* const vectors) { AVS_linkage = vectors; env->AddFunction("SimpleSample", "c[size]i", Create_SimpleSample, 0); return "SimpleSample plugin"; } The :ref:`AddFunction ` has the following parameters: :: AddFunction(Filtername, Arguments, Function to call, 0); *Arguments* is a string that defines the types and optional names of the arguments for your filter. The possible types are: +---------------------------------+ | Argument type specifier strings | +=================================+ + c - clip + +---------------------------------+ + i - integer + +---------------------------------+ + f - float + +---------------------------------+ + s - string + +---------------------------------+ + b - boolean + +---------------------------------+ + . - Any type (dot) + +---------------------------------+ +-----------------------------------+ + Array Specifiers + +===================================+ + i* - Integer Array, zero or more + +-----------------------------------+ + i+ - Integer Array, one or more + +-----------------------------------+ + .* - Any type Array, zero or more + +-----------------------------------+ + .+ - Any type Array, one or more + +-----------------------------------+ (have a look at StackVertical and SelectEvery for example as to how access such arrays) The text inside the [ ] lets you used named parameters in your script. Thus :: clip = ... SimpleSample(clip, size=100) but :: clip = ... SimpleSample(clip, 100) works as well. See :doc:`InvertNeg` for more information. ____ Back to :doc:`FilterSDK` $Date: 2015/09/14 20:23:59 $