Filter SDK/GradientMask

From Avisynth wiki
(Difference between revisions)
Jump to: navigation, search
(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";
}
Personal tools