diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..58c417a --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,67 @@ +cmake_minimum_required (VERSION 3.15) +project (neo_tmedian CXX) +file(GLOB CODE "src/*.cpp") +file(GLOB SSE41_CODE_IMPL "src/*SSE41.cpp") +file(GLOB AVX2_CODE_IMPL "src/*AVX2.cpp") +file(GLOB AVX512_CODE_IMPL "src/*AVX512.cpp") +add_library(neo-tmedian SHARED main.cpp src/version.rc ${CODE} ${CODE_IMPL}) +set_property(TARGET neo-tmedian PROPERTY CXX_STANDARD 17) + +find_package(Git REQUIRED) +execute_process(COMMAND ${GIT_EXECUTABLE} describe --first-parent --tags --always OUTPUT_VARIABLE GIT_REPO_VERSION) +string(REGEX REPLACE "(r[0-9]+).*" "\\1" VERSION ${GIT_REPO_VERSION}) + +configure_file ( + "${PROJECT_SOURCE_DIR}/src/version.hpp.in" + "${PROJECT_SOURCE_DIR}/src/version.hpp" +) +configure_file ( + "${PROJECT_SOURCE_DIR}/src/version.rc.in" + "${PROJECT_SOURCE_DIR}/src/version.rc" +) + +find_package(PkgConfig REQUIRED) + +pkg_check_modules(AVISYNTH avisynth) +if(AVISYNTH_FOUND) + include_directories(${AVISYNTH_INCLUDE_DIRS}) +else() + include_directories(include/avisynth) +endif() + +pkg_check_modules(VAPOURSYNTH vapoursynth) +if(VAPOURSYNTH_FOUND) + include_directories(${VAPOURSYNTH_INCLUDE_DIRS}) +else() + include_directories(include/vapoursynth) +endif() + +include_directories(.) +include_directories(include/dualsynth) + +if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set_source_files_properties(${SSE41_CODE_IMPL} PROPERTIES COMPILE_FLAGS "/arch:SSE2") + set_source_files_properties(${AVX2_CODE_IMPL} PROPERTIES COMPILE_FLAGS "/arch:AVX2") + set_source_files_properties(${AVX512_CODE_IMPL} PROPERTIES COMPILE_FLAGS "/arch:AVX512") + + if (CMAKE_GENERATOR_TOOLSET MATCHES "v[0-9]*_xp") + target_compile_definitions(neo-tmedian PRIVATE WINVER=0x502 _WIN32_WINNT=0x502) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:threadSafeInit-") + endif() + +elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Intel") + set_source_files_properties(${SSE41_CODE_IMPL} PROPERTIES COMPILE_FLAGS "/arch:SSE2") + set_source_files_properties(${AVX2_CODE_IMPL} PROPERTIES COMPILE_FLAGS "/arch:AVX2") + set_source_files_properties(${AVX512_CODE_IMPL} PROPERTIES COMPILE_FLAGS "/QxCOMMON-AVX512") + +else() + set_source_files_properties(${SSE41_CODE_IMPL} PROPERTIES COMPILE_FLAGS "-msse4.1") + set_source_files_properties(${AVX2_CODE_IMPL} PROPERTIES COMPILE_FLAGS "-mavx2") + set_source_files_properties(${AVX512_CODE_IMPL} PROPERTIES COMPILE_FLAGS "-mavx512bw") + +endif() + +add_custom_command( + TARGET neo-tmedian POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy $ "../Release_${VERSION}/${_DIR}/$" +) diff --git a/include/avisynth/avisynth.h b/include/avisynth/avisynth.h new file mode 100644 index 0000000..49a20df --- /dev/null +++ b/include/avisynth/avisynth.h @@ -0,0 +1,1394 @@ +// Avisynth v2.5. Copyright 2002 Ben Rudiak-Gould et al. +// Avisynth v2.6. Copyright 2006 Klaus Post. +// Avisynth v2.6. Copyright 2009 Ian Brabham. +// Avisynth+ project +// 20160613: new 16 bit planar pixel_type constants go live! +// 20160725: pixel_type constants 10-12-14 bit + planar RGB + BRG48/64 +// 20161005: Fallback of VideoInfo functions to defaults if no function exists +// 20170117: global variables for VfW output OPT_xxxx +// 20170310: new MT mode: MT_SPECIAL_MT +// 20171103: (test with SIZETMOD define: Videoframe offsets to size_t, may affect x64) +// 20171207: C++ Standard Conformance (no change for plugin writers) +// 20180525: AVS_UNUSED define to supress parameter not used warnings + +// http://www.avisynth.org + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit +// http://www.gnu.org/copyleft/gpl.html . +// +// Linking Avisynth statically or dynamically with other modules is making +// a combined work based on Avisynth. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// +// As a special exception, the copyright holders of Avisynth give you +// permission to link Avisynth with independent modules that communicate +// with Avisynth solely through the interfaces defined in avisynth.h, +// regardless of the license terms of these independent modules, and to +// copy and distribute the resulting combined work under terms of your +// choice, provided that every copy of the combined work is accompanied +// by a complete copy of the source code of Avisynth (the version of +// Avisynth used to produce the combined work), being distributed under +// the terms of the GNU General Public License plus this exception. An +// independent module is a module which is not derived from or based on +// Avisynth, such as 3rd-party filters, import and export plugins, or +// graphical user interfaces. + +#ifdef AVS_POSIX +# include "avs/posix.h" +#endif + + + +#ifndef __AVISYNTH_6_H__ +#define __AVISYNTH_6_H__ + +#include "avs/config.h" +#include "avs/capi.h" +#include "avs/types.h" + +#if defined(AVS_POSIX) +#define __stdcall +#define __cdecl +#endif + +enum { AVISYNTH_INTERFACE_VERSION = 6 }; + + +/* Compiler-specific crap */ + +// Tell MSVC to stop precompiling here +#if defined(_MSC_VER) && !defined(__clang__) + #pragma hdrstop +#endif + +// Set up debugging macros for MS compilers; for others, step down to the +// standard interface +#ifdef _MSC_VER + #include +#else + #undef _RPT0 + #undef _RPT1 + #undef _RPT2 + #undef _RPT3 + #undef _RPT4 + #undef _RPT5 + #define _RPT0(a,b) ((void)0) + #define _RPT1(a,b,c) ((void)0) + #define _RPT2(a,b,c,d) ((void)0) + #define _RPT3(a,b,c,d,e) ((void)0) + #define _RPT4(a,b,c,d,e,f) ((void)0) + #define _RPT5(a,b,c,d,e,f,g) ((void)0) + + #include + #undef _ASSERTE + #undef _ASSERT + #define _ASSERTE(x) assert(x) + #define _ASSERT(x) assert(x) +#endif + + + +// I had problems with Premiere wanting 1-byte alignment for its structures, +// so I now set the Avisynth struct alignment explicitly here. +#pragma pack(push,8) + +// The VideoInfo struct holds global information about a clip (i.e. +// information that does not depend on the frame number). The GetVideoInfo +// method in IClip returns this struct. + +enum {SAMPLE_INT8 = 1<<0, + SAMPLE_INT16 = 1<<1, + SAMPLE_INT24 = 1<<2, // Int24 is a very stupid thing to code, but it's supported by some hardware. + SAMPLE_INT32 = 1<<3, + SAMPLE_FLOAT = 1<<4}; + +enum { + PLANAR_Y=1<<0, + PLANAR_U=1<<1, + PLANAR_V=1<<2, + PLANAR_ALIGNED=1<<3, + PLANAR_Y_ALIGNED=PLANAR_Y|PLANAR_ALIGNED, + PLANAR_U_ALIGNED=PLANAR_U|PLANAR_ALIGNED, + PLANAR_V_ALIGNED=PLANAR_V|PLANAR_ALIGNED, + PLANAR_A=1<<4, + PLANAR_R=1<<5, + PLANAR_G=1<<6, + PLANAR_B=1<<7, + PLANAR_A_ALIGNED=PLANAR_A|PLANAR_ALIGNED, + PLANAR_R_ALIGNED=PLANAR_R|PLANAR_ALIGNED, + PLANAR_G_ALIGNED=PLANAR_G|PLANAR_ALIGNED, + PLANAR_B_ALIGNED=PLANAR_B|PLANAR_ALIGNED, + }; + +class AvisynthError /* exception */ { +public: + const char* const msg; + AvisynthError(const char* _msg) : msg(_msg) {} + +// Ensure AvisynthError cannot be publicly assigned! +private: + AvisynthError& operator=(const AvisynthError&); +}; // end class AvisynthError + + +/* Forward references */ +#if defined(MSVC) + #define SINGLE_INHERITANCE __single_inheritance +#else + #define SINGLE_INHERITANCE +#endif +struct SINGLE_INHERITANCE VideoInfo; +class SINGLE_INHERITANCE VideoFrameBuffer; +class SINGLE_INHERITANCE VideoFrame; +class IClip; +class SINGLE_INHERITANCE PClip; +class SINGLE_INHERITANCE PVideoFrame; +class IScriptEnvironment; +class SINGLE_INHERITANCE AVSValue; + + +/* + * Avisynth C++ plugin API code function pointers. + * + * In order to maintain binary compatibility with + * future version do not change the order of the + * existing function pointers. It will be baked + * into all existing plugins. + * + * Add new function pointers to the end of the + * structure. The linkage macros generate some + * protection code to ensure newer plugin do not + * call non-existing functions in an older host. + */ + +struct AVS_Linkage { + + int Size; + +/**********************************************************************/ + +// struct VideoInfo + bool (VideoInfo::*HasVideo)() const; + bool (VideoInfo::*HasAudio)() const; + bool (VideoInfo::*IsRGB)() const; + bool (VideoInfo::*IsRGB24)() const; + bool (VideoInfo::*IsRGB32)() const; + bool (VideoInfo::*IsYUV)() const; + bool (VideoInfo::*IsYUY2)() const; + bool (VideoInfo::*IsYV24)() const; + bool (VideoInfo::*IsYV16)() const; + bool (VideoInfo::*IsYV12)() const; + bool (VideoInfo::*IsYV411)() const; + bool (VideoInfo::*IsY8)() const; + bool (VideoInfo::*IsColorSpace)(int c_space) const; + bool (VideoInfo::*Is)(int property) const; + bool (VideoInfo::*IsPlanar)() const; + bool (VideoInfo::*IsFieldBased)() const; + bool (VideoInfo::*IsParityKnown)() const; + bool (VideoInfo::*IsBFF)() const; + bool (VideoInfo::*IsTFF)() const; + bool (VideoInfo::*IsVPlaneFirst)() const; + int (VideoInfo::*BytesFromPixels)(int pixels) const; + int (VideoInfo::*RowSize)(int plane) const; + int (VideoInfo::*BMPSize)() const; + int64_t (VideoInfo::*AudioSamplesFromFrames)(int frames) const; + int (VideoInfo::*FramesFromAudioSamples)(int64_t samples) const; + int64_t (VideoInfo::*AudioSamplesFromBytes)(int64_t bytes) const; + int64_t (VideoInfo::*BytesFromAudioSamples)(int64_t samples) const; + int (VideoInfo::*AudioChannels)() const; + int (VideoInfo::*SampleType)() const; + bool (VideoInfo::*IsSampleType)(int testtype) const; + int (VideoInfo::*SamplesPerSecond)() const; + int (VideoInfo::*BytesPerAudioSample)() const; + void (VideoInfo::*SetFieldBased)(bool isfieldbased); + void (VideoInfo::*Set)(int property); + void (VideoInfo::*Clear)(int property); + int (VideoInfo::*GetPlaneWidthSubsampling)(int plane) const; + int (VideoInfo::*GetPlaneHeightSubsampling)(int plane) const; + int (VideoInfo::*BitsPerPixel)() const; + int (VideoInfo::*BytesPerChannelSample)() const; + void (VideoInfo::*SetFPS)(unsigned numerator, unsigned denominator); + void (VideoInfo::*MulDivFPS)(unsigned multiplier, unsigned divisor); + bool (VideoInfo::*IsSameColorspace)(const VideoInfo& vi) const; +// end struct VideoInfo + +/**********************************************************************/ + +// class VideoFrameBuffer + const BYTE* (VideoFrameBuffer::*VFBGetReadPtr)() const; + BYTE* (VideoFrameBuffer::*VFBGetWritePtr)(); +#ifdef SIZETMOD + size_t (VideoFrameBuffer::*GetDataSize)() const; +#else + int (VideoFrameBuffer::*GetDataSize)() const; +#endif + int (VideoFrameBuffer::*GetSequenceNumber)() const; + int (VideoFrameBuffer::*GetRefcount)() const; +// end class VideoFrameBuffer + +/**********************************************************************/ + +// class VideoFrame + int (VideoFrame::*GetPitch)(int plane) const; + int (VideoFrame::*GetRowSize)(int plane) const; + int (VideoFrame::*GetHeight)(int plane) const; + VideoFrameBuffer* (VideoFrame::*GetFrameBuffer)() const; +#ifdef SIZETMOD + size_t (VideoFrame::*GetOffset)(int plane) const; +#else + int (VideoFrame::*GetOffset)(int plane) const; +#endif + const BYTE* (VideoFrame::*VFGetReadPtr)(int plane) const; + bool (VideoFrame::*IsWritable)() const; + BYTE* (VideoFrame::*VFGetWritePtr)(int plane) const; + void (VideoFrame::*VideoFrame_DESTRUCTOR)(); +// end class VideoFrame + +/**********************************************************************/ + +// class IClip + /* nothing */ +// end class IClip + +/**********************************************************************/ + +// class PClip + void (PClip::*PClip_CONSTRUCTOR0)(); + void (PClip::*PClip_CONSTRUCTOR1)(const PClip& x); + void (PClip::*PClip_CONSTRUCTOR2)(IClip* x); + void (PClip::*PClip_OPERATOR_ASSIGN0)(IClip* x); + void (PClip::*PClip_OPERATOR_ASSIGN1)(const PClip& x); + void (PClip::*PClip_DESTRUCTOR)(); +// end class PClip + +/**********************************************************************/ + +// class PVideoFrame + void (PVideoFrame::*PVideoFrame_CONSTRUCTOR0)(); + void (PVideoFrame::*PVideoFrame_CONSTRUCTOR1)(const PVideoFrame& x); + void (PVideoFrame::*PVideoFrame_CONSTRUCTOR2)(VideoFrame* x); + void (PVideoFrame::*PVideoFrame_OPERATOR_ASSIGN0)(VideoFrame* x); + void (PVideoFrame::*PVideoFrame_OPERATOR_ASSIGN1)(const PVideoFrame& x); + void (PVideoFrame::*PVideoFrame_DESTRUCTOR)(); +// end class PVideoFrame + +/**********************************************************************/ + +// class AVSValue + void (AVSValue::*AVSValue_CONSTRUCTOR0)(); + void (AVSValue::*AVSValue_CONSTRUCTOR1)(IClip* c); + void (AVSValue::*AVSValue_CONSTRUCTOR2)(const PClip& c); + void (AVSValue::*AVSValue_CONSTRUCTOR3)(bool b); + void (AVSValue::*AVSValue_CONSTRUCTOR4)(int i); + void (AVSValue::*AVSValue_CONSTRUCTOR5)(float f); + void (AVSValue::*AVSValue_CONSTRUCTOR6)(double f); + void (AVSValue::*AVSValue_CONSTRUCTOR7)(const char* s); + void (AVSValue::*AVSValue_CONSTRUCTOR8)(const AVSValue* a, int size); + void (AVSValue::*AVSValue_CONSTRUCTOR9)(const AVSValue& v); + void (AVSValue::*AVSValue_DESTRUCTOR)(); + AVSValue& (AVSValue::*AVSValue_OPERATOR_ASSIGN)(const AVSValue& v); + const AVSValue& (AVSValue::*AVSValue_OPERATOR_INDEX)(int index) const; + bool (AVSValue::*Defined)() const; + bool (AVSValue::*IsClip)() const; + bool (AVSValue::*IsBool)() const; + bool (AVSValue::*IsInt)() const; + bool (AVSValue::*IsFloat)() const; + bool (AVSValue::*IsString)() const; + bool (AVSValue::*IsArray)() const; + PClip (AVSValue::*AsClip)() const; + bool (AVSValue::*AsBool1)() const; + int (AVSValue::*AsInt1)() const; + const char* (AVSValue::*AsString1)() const; + double (AVSValue::*AsFloat1)() const; + bool (AVSValue::*AsBool2)(bool def) const; + int (AVSValue::*AsInt2)(int def) const; + double (AVSValue::*AsDblDef)(double def) const; + double (AVSValue::*AsFloat2)(float def) const; + const char* (AVSValue::*AsString2)(const char* def) const; + int (AVSValue::*ArraySize)() const; +// end class AVSValue + +/**********************************************************************/ + // Reserve pointer space so that we can keep compatibility with Avs "classic" even if it adds functions on its own + void (VideoInfo::*reserved[32])(); +/**********************************************************************/ + // AviSynth+ additions + int (VideoInfo::*NumComponents)() const; + int (VideoInfo::*ComponentSize)() const; + int (VideoInfo::*BitsPerComponent)() const; + bool (VideoInfo::*Is444)() const; + bool (VideoInfo::*Is422)() const; + bool (VideoInfo::*Is420)() const; + bool (VideoInfo::*IsY)() const; + bool (VideoInfo::*IsRGB48)() const; + bool (VideoInfo::*IsRGB64)() const; + bool (VideoInfo::*IsYUVA)() const; + bool (VideoInfo::*IsPlanarRGB)() const; + bool (VideoInfo::*IsPlanarRGBA)() const; + /**********************************************************************/ +}; + +#ifdef BUILDING_AVSCORE +/* Macro resolution for code inside Avisynth.dll */ +# define AVS_BakedCode(arg) ; +# define AVS_LinkCall(arg) +# define AVS_LinkCallV(arg) +# define AVS_LinkCallOpt(arg, argOpt) AVSLinkCall(arg) +# define AVS_LinkCallOptDefault(arg, argDefaultValue) AVSLinkCall(arg()) +# define CALL_MEMBER_FN(object,ptrToMember) + +#else +/* Macro resolution for code inside user plugin */ +# ifdef AVS_LINKAGE_DLLIMPORT +extern __declspec(dllimport) const AVS_Linkage* const AVS_linkage; +# else +extern const AVS_Linkage* AVS_linkage; +# endif + +# ifndef offsetof +# include +# endif + +# define AVS_BakedCode(arg) { arg ; } +# define AVS_LinkCall(arg) !AVS_linkage || offsetof(AVS_Linkage, arg) >= AVS_linkage->Size ? 0 : (this->*(AVS_linkage->arg)) +# define AVS_LinkCall_Void(arg) !AVS_linkage || offsetof(AVS_Linkage, arg) >= AVS_linkage->Size ? (void)0 : (this->*(AVS_linkage->arg)) +# define AVS_LinkCallV(arg) !AVS_linkage || offsetof(AVS_Linkage, arg) >= AVS_linkage->Size ? *this : (this->*(AVS_linkage->arg)) +// Helper macros for fallback option when a function does not exists +#define CALL_MEMBER_FN(object,ptrToMember) ((object)->*(ptrToMember)) +#define AVS_LinkCallOpt(arg, argOpt) !AVS_linkage ? 0 : \ + ( offsetof(AVS_Linkage, arg) >= AVS_linkage->Size ? \ + (offsetof(AVS_Linkage, argOpt) >= AVS_linkage->Size ? 0 : CALL_MEMBER_FN(this, AVS_linkage->argOpt)() ) : \ + CALL_MEMBER_FN(this, AVS_linkage->arg)() ) +// AVS_LinkCallOptDefault puts automatically () only after arg +# define AVS_LinkCallOptDefault(arg, argDefaultValue) !AVS_linkage || offsetof(AVS_Linkage, arg) >= AVS_linkage->Size ? (argDefaultValue) : ((this->*(AVS_linkage->arg))()) + +#endif + +struct VideoInfo { + int width, height; // width=0 means no video + unsigned fps_numerator, fps_denominator; + int num_frames; + // This is more extensible than previous versions. More properties can be added seeminglesly. + + // Colorspace properties. +/* + +Planar match mask 1111.1000.0000.0111.0000.0111.0000.0111 +Planar signature 10xx.1000.0000.00xx.0000.00xx.00xx.00xx ? +Planar signature 10xx.1000.0000.0xxx.0000.00xx.000x.x0xx ? *new +Planar filter mask 1111.1111.1111.1111.1111.1111.1110.0111 (typo from old header fixed) + +pixel_type mapping +================== +pixel_type bit-map PIYB.Z000.0???.0SSS.0000.0???.????.???? + planar YUV CCC HHH.000u.vWWW + planar RGB(A) CCC AR + nonplanar CCC 000.00wx xyAR +Legend +====== +Planar YUV: + Code Bits Remark + W 0-2 Planar Width Subsampling bits + Use (X+1) & 3 for GetPlaneWidthSubsampling + 000 => 1 YV12, YV16, YUV420, YUV422 + 001 => 2 YV411, YUV9 + 010 => reserved + 011 => 0 YV24, YUV444, RGBP + 1xx => reserved + v 3 VPlaneFirst YV12, YV16, YV24, YV411, YUV9 + u 4 UPlaneFirst I420 + H 7-9 Planar Height Subsampling bits + Use ((X>>8)+1) & 3 for GetPlaneHeightSubsampling + 000 => 1 YV12, YUV420 + 001 => 2 YUV9 + 010 => reserved + 011 => 0 YV16, YV24, YV411, YUV422, YUV444, RGBP + 1xx => reserved + +Planar RGB + Code Bits Remark + R 0 BGR, (with SSS bits for 8/16 bit/sample or float) + A 1 BGRA, (with SSS bits for 8/16 bit/sample or float) + + +Not Planar, Interleaved (I flag) +Code Bits Remark + R 0 BGR24, and BGRx in future (with SSS bits for 8/16 bit/sample or float) + A 1 BGR32, and BGRAx in future (with SSS bits for 8/16 bit/sample or float) + y 2 YUY2 + x 3-4 reserved + w 5 Raw32 + +General +Code Bits Remark + S 16-18 Sample resolution bits + 000 => 8 + 001 => 16 + 010 => 32 (float) + 011,100 => reserved + 101 => 10 bits + 110 => 12 bits + 111 => 14 bits +for packed RGB(A): only 8 and 16 bits are valid + +Other YV12 specific (not used?) + C 20-22 Chroma Placement values 0-4 see CS_xxx_CHROMA_PLACEMENT + +Color family and layout + Packed Planar Planar Planar +Code Bits Remark RGB/RGBA YUV YUY2 Y_Grey RGB/RGBA YUVA + R 0 1/0 - 0 - 1/0 - + A 1 0/1 - 0 - 0/1 - + y 2 - - 1 - 0 - + Z 27 YUVA 0 0 0 0 1 1 + B 28 BGR 1 0 0 0 1* 0 + Y 29 YUV 0 1 1 1 0 0 + I 30 Interleaved 1 0 1 1 0 0 + P 31 Planar 0 1 0 1 1 1 +* Planar RGB plane order: G,B,R(,A) + +*/ +enum { + CS_YUVA = 1<<27, + CS_BGR = 1<<28, + CS_YUV = 1<<29, + CS_INTERLEAVED = 1<<30, + CS_PLANAR = 1<<31, + + CS_Shift_Sub_Width = 0, + CS_Shift_Sub_Height = 8, + CS_Shift_Sample_Bits = 16, + + CS_Sub_Width_Mask = 7 << CS_Shift_Sub_Width, + CS_Sub_Width_1 = 3 << CS_Shift_Sub_Width, // YV24 + CS_Sub_Width_2 = 0 << CS_Shift_Sub_Width, // YV12, I420, YV16 + CS_Sub_Width_4 = 1 << CS_Shift_Sub_Width, // YUV9, YV411 + + CS_VPlaneFirst = 1 << 3, // YV12, YV16, YV24, YV411, YUV9 + CS_UPlaneFirst = 1 << 4, // I420 + + CS_Sub_Height_Mask = 7 << CS_Shift_Sub_Height, + CS_Sub_Height_1 = 3 << CS_Shift_Sub_Height, // YV16, YV24, YV411 + CS_Sub_Height_2 = 0 << CS_Shift_Sub_Height, // YV12, I420 + CS_Sub_Height_4 = 1 << CS_Shift_Sub_Height, // YUV9 + + CS_Sample_Bits_Mask = 7 << CS_Shift_Sample_Bits, + CS_Sample_Bits_8 = 0 << CS_Shift_Sample_Bits, + CS_Sample_Bits_10 = 5 << CS_Shift_Sample_Bits, + CS_Sample_Bits_12 = 6 << CS_Shift_Sample_Bits, + CS_Sample_Bits_14 = 7 << CS_Shift_Sample_Bits, + CS_Sample_Bits_16 = 1 << CS_Shift_Sample_Bits, + CS_Sample_Bits_32 = 2 << CS_Shift_Sample_Bits, + + CS_PLANAR_MASK = CS_PLANAR | CS_INTERLEAVED | CS_YUV | CS_BGR | CS_YUVA | CS_Sample_Bits_Mask + | CS_Sub_Height_Mask | CS_Sub_Width_Mask, + CS_PLANAR_FILTER = ~( CS_VPlaneFirst | CS_UPlaneFirst ), + + CS_RGB_TYPE = 1 << 0, + CS_RGBA_TYPE = 1 << 1, + + // Specific colorformats + CS_UNKNOWN = 0, + + CS_BGR24 = CS_RGB_TYPE | CS_BGR | CS_INTERLEAVED, + CS_BGR32 = CS_RGBA_TYPE | CS_BGR | CS_INTERLEAVED, + CS_YUY2 = 1<<2 | CS_YUV | CS_INTERLEAVED, + // CS_YV12 = 1<<3 Reserved + // CS_I420 = 1<<4 Reserved + CS_RAW32 = 1<<5 | CS_INTERLEAVED, + + // YV12 must be 0xA000008 2.5 Baked API will see all new planar as YV12 + // I420 must be 0xA000010 + + CS_GENERIC_YUV420 = CS_PLANAR | CS_YUV | CS_VPlaneFirst | CS_Sub_Height_2 | CS_Sub_Width_2, // 4:2:0 planar + CS_GENERIC_YUV422 = CS_PLANAR | CS_YUV | CS_VPlaneFirst | CS_Sub_Height_1 | CS_Sub_Width_2, // 4:2:2 planar + CS_GENERIC_YUV444 = CS_PLANAR | CS_YUV | CS_VPlaneFirst | CS_Sub_Height_1 | CS_Sub_Width_1, // 4:4:4 planar + CS_GENERIC_Y = CS_PLANAR | CS_INTERLEAVED | CS_YUV, // Y only (4:0:0) + CS_GENERIC_RGBP = CS_PLANAR | CS_BGR | CS_RGB_TYPE, // planar RGB. Though name is RGB but plane order G,B,R + CS_GENERIC_RGBAP = CS_PLANAR | CS_BGR | CS_RGBA_TYPE, // planar RGBA + CS_GENERIC_YUVA420 = CS_PLANAR | CS_YUVA | CS_VPlaneFirst | CS_Sub_Height_2 | CS_Sub_Width_2, // 4:2:0:A planar + CS_GENERIC_YUVA422 = CS_PLANAR | CS_YUVA | CS_VPlaneFirst | CS_Sub_Height_1 | CS_Sub_Width_2, // 4:2:2:A planar + CS_GENERIC_YUVA444 = CS_PLANAR | CS_YUVA | CS_VPlaneFirst | CS_Sub_Height_1 | CS_Sub_Width_1, // 4:4:4:A planar + + CS_YV24 = CS_GENERIC_YUV444 | CS_Sample_Bits_8, // YVU 4:4:4 planar + CS_YV16 = CS_GENERIC_YUV422 | CS_Sample_Bits_8, // YVU 4:2:2 planar + CS_YV12 = CS_GENERIC_YUV420 | CS_Sample_Bits_8, // YVU 4:2:0 planar + CS_I420 = CS_PLANAR | CS_YUV | CS_Sample_Bits_8 | CS_UPlaneFirst | CS_Sub_Height_2 | CS_Sub_Width_2, // YUV 4:2:0 planar + CS_IYUV = CS_I420, + CS_YUV9 = CS_PLANAR | CS_YUV | CS_Sample_Bits_8 | CS_VPlaneFirst | CS_Sub_Height_4 | CS_Sub_Width_4, // YUV 4:1:0 planar + CS_YV411 = CS_PLANAR | CS_YUV | CS_Sample_Bits_8 | CS_VPlaneFirst | CS_Sub_Height_1 | CS_Sub_Width_4, // YUV 4:1:1 planar + + CS_Y8 = CS_GENERIC_Y | CS_Sample_Bits_8, // Y 4:0:0 planar + + //------------------------- + // AVS16: new planar constants go live! Experimental PF 160613 + // 10-12-14 bit + planar RGB + BRG48/64 160725 + + CS_YUV444P10 = CS_GENERIC_YUV444 | CS_Sample_Bits_10, // YUV 4:4:4 10bit samples + CS_YUV422P10 = CS_GENERIC_YUV422 | CS_Sample_Bits_10, // YUV 4:2:2 10bit samples + CS_YUV420P10 = CS_GENERIC_YUV420 | CS_Sample_Bits_10, // YUV 4:2:0 10bit samples + CS_Y10 = CS_GENERIC_Y | CS_Sample_Bits_10, // Y 4:0:0 10bit samples + + CS_YUV444P12 = CS_GENERIC_YUV444 | CS_Sample_Bits_12, // YUV 4:4:4 12bit samples + CS_YUV422P12 = CS_GENERIC_YUV422 | CS_Sample_Bits_12, // YUV 4:2:2 12bit samples + CS_YUV420P12 = CS_GENERIC_YUV420 | CS_Sample_Bits_12, // YUV 4:2:0 12bit samples + CS_Y12 = CS_GENERIC_Y | CS_Sample_Bits_12, // Y 4:0:0 12bit samples + + CS_YUV444P14 = CS_GENERIC_YUV444 | CS_Sample_Bits_14, // YUV 4:4:4 14bit samples + CS_YUV422P14 = CS_GENERIC_YUV422 | CS_Sample_Bits_14, // YUV 4:2:2 14bit samples + CS_YUV420P14 = CS_GENERIC_YUV420 | CS_Sample_Bits_14, // YUV 4:2:0 14bit samples + CS_Y14 = CS_GENERIC_Y | CS_Sample_Bits_14, // Y 4:0:0 14bit samples + + CS_YUV444P16 = CS_GENERIC_YUV444 | CS_Sample_Bits_16, // YUV 4:4:4 16bit samples + CS_YUV422P16 = CS_GENERIC_YUV422 | CS_Sample_Bits_16, // YUV 4:2:2 16bit samples + CS_YUV420P16 = CS_GENERIC_YUV420 | CS_Sample_Bits_16, // YUV 4:2:0 16bit samples + CS_Y16 = CS_GENERIC_Y | CS_Sample_Bits_16, // Y 4:0:0 16bit samples + + // 32 bit samples (float) + CS_YUV444PS = CS_GENERIC_YUV444 | CS_Sample_Bits_32, // YUV 4:4:4 32bit samples + CS_YUV422PS = CS_GENERIC_YUV422 | CS_Sample_Bits_32, // YUV 4:2:2 32bit samples + CS_YUV420PS = CS_GENERIC_YUV420 | CS_Sample_Bits_32, // YUV 4:2:0 32bit samples + CS_Y32 = CS_GENERIC_Y | CS_Sample_Bits_32, // Y 4:0:0 32bit samples + + // RGB packed + CS_BGR48 = CS_RGB_TYPE | CS_BGR | CS_INTERLEAVED | CS_Sample_Bits_16, // BGR 3x16 bit + CS_BGR64 = CS_RGBA_TYPE | CS_BGR | CS_INTERLEAVED | CS_Sample_Bits_16, // BGR 4x16 bit + // no packed 32 bit (float) support for these legacy types + + // RGB planar + CS_RGBP = CS_GENERIC_RGBP | CS_Sample_Bits_8, // Planar RGB 8 bit samples + CS_RGBP10 = CS_GENERIC_RGBP | CS_Sample_Bits_10, // Planar RGB 10bit samples + CS_RGBP12 = CS_GENERIC_RGBP | CS_Sample_Bits_12, // Planar RGB 12bit samples + CS_RGBP14 = CS_GENERIC_RGBP | CS_Sample_Bits_14, // Planar RGB 14bit samples + CS_RGBP16 = CS_GENERIC_RGBP | CS_Sample_Bits_16, // Planar RGB 16bit samples + CS_RGBPS = CS_GENERIC_RGBP | CS_Sample_Bits_32, // Planar RGB 32bit samples + + // RGBA planar + CS_RGBAP = CS_GENERIC_RGBAP | CS_Sample_Bits_8, // Planar RGBA 8 bit samples + CS_RGBAP10 = CS_GENERIC_RGBAP | CS_Sample_Bits_10, // Planar RGBA 10bit samples + CS_RGBAP12 = CS_GENERIC_RGBAP | CS_Sample_Bits_12, // Planar RGBA 12bit samples + CS_RGBAP14 = CS_GENERIC_RGBAP | CS_Sample_Bits_14, // Planar RGBA 14bit samples + CS_RGBAP16 = CS_GENERIC_RGBAP | CS_Sample_Bits_16, // Planar RGBA 16bit samples + CS_RGBAPS = CS_GENERIC_RGBAP | CS_Sample_Bits_32, // Planar RGBA 32bit samples + + // Planar YUVA + CS_YUVA444 = CS_GENERIC_YUVA444 | CS_Sample_Bits_8, // YUVA 4:4:4 8bit samples + CS_YUVA422 = CS_GENERIC_YUVA422 | CS_Sample_Bits_8, // YUVA 4:2:2 8bit samples + CS_YUVA420 = CS_GENERIC_YUVA420 | CS_Sample_Bits_8, // YUVA 4:2:0 8bit samples + + CS_YUVA444P10 = CS_GENERIC_YUVA444 | CS_Sample_Bits_10, // YUVA 4:4:4 10bit samples + CS_YUVA422P10 = CS_GENERIC_YUVA422 | CS_Sample_Bits_10, // YUVA 4:2:2 10bit samples + CS_YUVA420P10 = CS_GENERIC_YUVA420 | CS_Sample_Bits_10, // YUVA 4:2:0 10bit samples + + CS_YUVA444P12 = CS_GENERIC_YUVA444 | CS_Sample_Bits_12, // YUVA 4:4:4 12bit samples + CS_YUVA422P12 = CS_GENERIC_YUVA422 | CS_Sample_Bits_12, // YUVA 4:2:2 12bit samples + CS_YUVA420P12 = CS_GENERIC_YUVA420 | CS_Sample_Bits_12, // YUVA 4:2:0 12bit samples + + CS_YUVA444P14 = CS_GENERIC_YUVA444 | CS_Sample_Bits_14, // YUVA 4:4:4 14bit samples + CS_YUVA422P14 = CS_GENERIC_YUVA422 | CS_Sample_Bits_14, // YUVA 4:2:2 14bit samples + CS_YUVA420P14 = CS_GENERIC_YUVA420 | CS_Sample_Bits_14, // YUVA 4:2:0 14bit samples + + CS_YUVA444P16 = CS_GENERIC_YUVA444 | CS_Sample_Bits_16, // YUVA 4:4:4 16bit samples + CS_YUVA422P16 = CS_GENERIC_YUVA422 | CS_Sample_Bits_16, // YUVA 4:2:2 16bit samples + CS_YUVA420P16 = CS_GENERIC_YUVA420 | CS_Sample_Bits_16, // YUVA 4:2:0 16bit samples + + CS_YUVA444PS = CS_GENERIC_YUVA444 | CS_Sample_Bits_32, // YUVA 4:4:4 32bit samples + CS_YUVA422PS = CS_GENERIC_YUVA422 | CS_Sample_Bits_32, // YUVA 4:2:2 32bit samples + CS_YUVA420PS = CS_GENERIC_YUVA420 | CS_Sample_Bits_32, // YUVA 4:2:0 32bit samples + + }; + + int pixel_type; // changed to int as of 2.5 + + + int audio_samples_per_second; // 0 means no audio + int sample_type; // as of 2.5 + int64_t num_audio_samples; // changed as of 2.5 + int nchannels; // as of 2.5 + + // Imagetype properties + + int image_type; + + enum { + IT_BFF = 1<<0, + IT_TFF = 1<<1, + IT_FIELDBASED = 1<<2 + }; + + // Chroma placement bits 20 -> 23 ::FIXME:: Really want a Class to support this + enum { + CS_UNKNOWN_CHROMA_PLACEMENT = 0 << 20, + CS_MPEG1_CHROMA_PLACEMENT = 1 << 20, + CS_MPEG2_CHROMA_PLACEMENT = 2 << 20, + CS_YUY2_CHROMA_PLACEMENT = 3 << 20, + CS_TOPLEFT_CHROMA_PLACEMENT = 4 << 20 + }; + + // useful functions of the above + bool HasVideo() const AVS_BakedCode(return AVS_LinkCall(HasVideo)()) + bool HasAudio() const AVS_BakedCode(return AVS_LinkCall(HasAudio)()) + bool IsRGB() const AVS_BakedCode(return AVS_LinkCall(IsRGB)()) + bool IsRGB24() const AVS_BakedCode(return AVS_LinkCall(IsRGB24)()) + bool IsRGB32() const AVS_BakedCode(return AVS_LinkCall(IsRGB32)()) + bool IsYUV() const AVS_BakedCode(return AVS_LinkCall(IsYUV)()) + bool IsYUY2() const AVS_BakedCode(return AVS_LinkCall(IsYUY2)()) + + bool IsYV24() const AVS_BakedCode(return AVS_LinkCall(IsYV24)()) + bool IsYV16() const AVS_BakedCode(return AVS_LinkCall(IsYV16)()) + bool IsYV12() const AVS_BakedCode(return AVS_LinkCall(IsYV12)()) + bool IsYV411() const AVS_BakedCode(return AVS_LinkCall(IsYV411)()) + //bool IsYUV9() const; + bool IsY8() const AVS_BakedCode(return AVS_LinkCall(IsY8)()) + + bool IsColorSpace(int c_space) const AVS_BakedCode(return AVS_LinkCall(IsColorSpace)(c_space)) + + bool Is(int property) const AVS_BakedCode(return AVS_LinkCall(Is)(property)) + bool IsPlanar() const AVS_BakedCode(return AVS_LinkCall(IsPlanar)()) + bool IsFieldBased() const AVS_BakedCode(return AVS_LinkCall(IsFieldBased)()) + bool IsParityKnown() const AVS_BakedCode(return AVS_LinkCall(IsParityKnown)()) + bool IsBFF() const AVS_BakedCode(return AVS_LinkCall(IsBFF)()) + bool IsTFF() const AVS_BakedCode(return AVS_LinkCall(IsTFF)()) + + bool IsVPlaneFirst() const AVS_BakedCode(return AVS_LinkCall(IsVPlaneFirst)()) // Don't use this + // Will not work on planar images, but will return only luma planes + int BytesFromPixels(int pixels) const AVS_BakedCode(return AVS_LinkCall(BytesFromPixels)(pixels)) + int RowSize(int plane = 0) const AVS_BakedCode(return AVS_LinkCall(RowSize)(plane)) + int BMPSize() const AVS_BakedCode(return AVS_LinkCall(BMPSize)()) + + int64_t AudioSamplesFromFrames(int frames) const AVS_BakedCode(return AVS_LinkCall(AudioSamplesFromFrames)(frames)) + int FramesFromAudioSamples(int64_t samples) const AVS_BakedCode(return AVS_LinkCall(FramesFromAudioSamples)(samples)) + int64_t AudioSamplesFromBytes(int64_t bytes) const AVS_BakedCode(return AVS_LinkCall(AudioSamplesFromBytes)(bytes)) + int64_t BytesFromAudioSamples(int64_t samples) const AVS_BakedCode(return AVS_LinkCall(BytesFromAudioSamples)(samples)) + int AudioChannels() const AVS_BakedCode(return AVS_LinkCall(AudioChannels)()) + int SampleType() const AVS_BakedCode(return AVS_LinkCall(SampleType)()) + bool IsSampleType(int testtype) const AVS_BakedCode(return AVS_LinkCall(IsSampleType)(testtype)) + int SamplesPerSecond() const AVS_BakedCode(return AVS_LinkCall(SamplesPerSecond)()) + int BytesPerAudioSample() const AVS_BakedCode(return AVS_LinkCall(BytesPerAudioSample)()) + void SetFieldBased(bool isfieldbased) AVS_BakedCode(AVS_LinkCall_Void(SetFieldBased)(isfieldbased)) + void Set(int property) AVS_BakedCode(AVS_LinkCall_Void(Set)(property)) + void Clear(int property) AVS_BakedCode(AVS_LinkCall_Void(Clear)(property)) + // Subsampling in bitshifts! + int GetPlaneWidthSubsampling(int plane) const AVS_BakedCode(return AVS_LinkCall(GetPlaneWidthSubsampling)(plane)) + int GetPlaneHeightSubsampling(int plane) const AVS_BakedCode(return AVS_LinkCall(GetPlaneHeightSubsampling)(plane)) + int BitsPerPixel() const AVS_BakedCode(return AVS_LinkCall(BitsPerPixel)()) + + int BytesPerChannelSample() const AVS_BakedCode(return AVS_LinkCall(BytesPerChannelSample)()) + + // useful mutator + void SetFPS(unsigned numerator, unsigned denominator) AVS_BakedCode(AVS_LinkCall_Void(SetFPS)(numerator, denominator)) + + // Range protected multiply-divide of FPS + void MulDivFPS(unsigned multiplier, unsigned divisor) AVS_BakedCode(AVS_LinkCall_Void(MulDivFPS)(multiplier, divisor)) + + // Test for same colorspace + bool IsSameColorspace(const VideoInfo& vi) const AVS_BakedCode(return AVS_LinkCall(IsSameColorspace)(vi)) + + // AVS+ extensions + // 20161005: + // Mapping of AVS+ extensions to classic 2.6 functions. + // In order to use these extended AVS+ functions for plugins that should work + // either with AVS+ or with Classic (8 bit) Avs 2.6 ans earlier AVS+ versions, there is an + // automatic fallback mechanism. + // From AVS+'s point of view these are not "baked" codes, the primary functions should exist. + // Examples: + // Is444() is mapped to IsYV24() for classic AVS2.6 + // ComponentSize() returns constant 1 (1 bytes per pixel component) + // BitsPerComponent() returns constant 8 (Classic AVS2.6 is 8 bit only) + + // Returns the number of color channels or planes in a frame + int NumComponents() const AVS_BakedCode(return AVS_LinkCallOptDefault(NumComponents, (((AVS_LinkCall(IsYUV)()) && !(AVS_LinkCall(IsY8)())) ? 3 : AVS_LinkCall(BytesFromPixels)(1)) ) ) + + // Returns the size in bytes of a single component of a pixel + int ComponentSize() const AVS_BakedCode(return AVS_LinkCallOptDefault(ComponentSize, 1)) + + // Returns the bit depth of a single component of a pixel + int BitsPerComponent() const AVS_BakedCode(return AVS_LinkCallOptDefault(BitsPerComponent, 8)) + + // like IsYV24, but bit-depth independent also for YUVA + bool Is444() const AVS_BakedCode(return AVS_LinkCallOpt(Is444, IsYV24) ) + + // like IsYV16, but bit-depth independent also for YUVA + bool Is422() const AVS_BakedCode(return AVS_LinkCallOpt(Is422, IsYV16) ) + + // like IsYV12, but bit-depth independent also for YUVA + bool Is420() const AVS_BakedCode( return AVS_LinkCallOpt(Is420, IsYV12) ) + + // like IsY8, but bit-depth independent + bool IsY() const AVS_BakedCode( return AVS_LinkCallOpt(IsY, IsY8) ) + + // like IsRGB24 for 16 bit samples + bool IsRGB48() const AVS_BakedCode( return AVS_LinkCallOptDefault(IsRGB48, false) ) + + // like IsRGB32 for 16 bit samples + bool IsRGB64() const AVS_BakedCode( return AVS_LinkCallOptDefault(IsRGB64, false) ) + + // YUVA? + bool IsYUVA() const AVS_BakedCode( return AVS_LinkCallOptDefault(IsYUVA, false) ) + + // Planar RGB? + bool IsPlanarRGB() const AVS_BakedCode( return AVS_LinkCallOptDefault(IsPlanarRGB, false) ) + + // Planar RGBA? + bool IsPlanarRGBA() const AVS_BakedCode( return AVS_LinkCallOptDefault(IsPlanarRGBA, false) ) + +}; // end struct VideoInfo + + + + +// VideoFrameBuffer holds information about a memory block which is used +// for video data. For efficiency, instances of this class are not deleted +// when the refcount reaches zero; instead they're stored in a linked list +// to be reused. The instances are deleted when the corresponding AVS +// file is closed. + +class VideoFrameBuffer { + BYTE* data; +#ifdef SIZETMOD + size_t data_size; +#else + int data_size; +#endif + // sequence_number is incremented every time the buffer is changed, so + // that stale views can tell they're no longer valid. + volatile long sequence_number; + + friend class VideoFrame; + friend class Cache; + friend class ScriptEnvironment; + volatile long refcount; + +protected: +#ifdef SIZETMOD + VideoFrameBuffer(size_t size); +#else + VideoFrameBuffer(int size); +#endif + VideoFrameBuffer(); + ~VideoFrameBuffer(); + +public: + const BYTE* GetReadPtr() const AVS_BakedCode( return AVS_LinkCall(VFBGetReadPtr)() ) + BYTE* GetWritePtr() AVS_BakedCode( return AVS_LinkCall(VFBGetWritePtr)() ) +#ifdef SIZETMOD + size_t GetDataSize() const AVS_BakedCode(return AVS_LinkCall(GetDataSize)()) +#else + int GetDataSize() const AVS_BakedCode( return AVS_LinkCall(GetDataSize)() ) +#endif + int GetSequenceNumber() const AVS_BakedCode( return AVS_LinkCall(GetSequenceNumber)() ) + int GetRefcount() const AVS_BakedCode( return AVS_LinkCall(GetRefcount)() ) + +// Ensure VideoFrameBuffer cannot be publicly assigned +private: + VideoFrameBuffer& operator=(const VideoFrameBuffer&); + +}; // end class VideoFrameBuffer + + +// VideoFrame holds a "window" into a VideoFrameBuffer. Operator new +// is overloaded to recycle class instances. + +class VideoFrame { + volatile long refcount; + VideoFrameBuffer* vfb; + + // Due to technical reasons these members are not const, but should be treated as such. + // That means do not modify them once the class has been constructed. +#ifdef SIZETMOD + size_t offset; +#else + int offset; +#endif + int pitch, row_size, height; +#ifdef SIZETMOD + size_t offsetU, offsetV; // U&V offsets are from top of picture. +#else + int offsetU, offsetV; // U&V offsets are from top of picture. +#endif + int pitchUV, row_sizeUV, heightUV; // for Planar RGB offsetU, offsetV is for the 2nd and 3rd Plane. + // for Planar RGB pitchUV and row_sizeUV = 0, because when no VideoInfo (MakeWriteable) + // the decision on existance of UV is checked by zero pitch + // AVS+ extension, does not break plugins if appended here +#ifdef SIZETMOD + size_t offsetA; +#else + int offsetA; +#endif + int pitchA, row_sizeA; // 4th alpha plane support, pitch and row_size is 0 is none + + friend class PVideoFrame; + void AddRef(); + void Release(); + + friend class ScriptEnvironment; + friend class Cache; + +#ifdef SIZETMOD + VideoFrame(VideoFrameBuffer* _vfb, size_t _offset, int _pitch, int _row_size, int _height); + VideoFrame(VideoFrameBuffer* _vfb, size_t _offset, int _pitch, int _row_size, int _height, size_t _offsetU, size_t _offsetV, int _pitchUV, int _row_sizeUV, int _heightUV); + // for Alpha + VideoFrame(VideoFrameBuffer* _vfb, size_t _offset, int _pitch, int _row_size, int _height, size_t _offsetU, size_t _offsetV, int _pitchUV, int _row_sizeUV, int _heightUV, size_t _offsetA); +#else + VideoFrame(VideoFrameBuffer* _vfb, int _offset, int _pitch, int _row_size, int _height); + VideoFrame(VideoFrameBuffer* _vfb, int _offset, int _pitch, int _row_size, int _height, int _offsetU, int _offsetV, int _pitchUV, int _row_sizeUV, int _heightUV); + // for Alpha + VideoFrame(VideoFrameBuffer* _vfb, int _offset, int _pitch, int _row_size, int _height, int _offsetU, int _offsetV, int _pitchUV, int _row_sizeUV, int _heightUV, int _offsetA); +#endif + + void* operator new(size_t size); +// TESTME: OFFSET U/V may be switched to what could be expected from AVI standard! +public: + int GetPitch(int plane=0) const AVS_BakedCode( return AVS_LinkCall(GetPitch)(plane) ) + int GetRowSize(int plane=0) const AVS_BakedCode( return AVS_LinkCall(GetRowSize)(plane) ) + int GetHeight(int plane=0) const AVS_BakedCode( return AVS_LinkCall(GetHeight)(plane) ) + + // generally you shouldn't use these three + VideoFrameBuffer* GetFrameBuffer() const AVS_BakedCode( return AVS_LinkCall(GetFrameBuffer)() ) +#ifdef SIZETMOD + size_t GetOffset(int plane = 0) const AVS_BakedCode(return AVS_LinkCall(GetOffset)(plane)) +#else + int GetOffset(int plane=0) const AVS_BakedCode( return AVS_LinkCall(GetOffset)(plane) ) +#endif + + // in plugins use env->SubFrame() -- because implementation code is only available inside avisynth.dll. Doh! + VideoFrame* Subframe(int rel_offset, int new_pitch, int new_row_size, int new_height) const; + VideoFrame* Subframe(int rel_offset, int new_pitch, int new_row_size, int new_height, int rel_offsetU, int rel_offsetV, int pitchUV) const; + // for Alpha + VideoFrame* Subframe(int rel_offset, int new_pitch, int new_row_size, int new_height, int rel_offsetU, int rel_offsetV, int pitchUV, int rel_offsetA) const; + + const BYTE* GetReadPtr(int plane=0) const AVS_BakedCode( return AVS_LinkCall(VFGetReadPtr)(plane) ) + bool IsWritable() const AVS_BakedCode( return AVS_LinkCall(IsWritable)() ) + BYTE* GetWritePtr(int plane=0) const AVS_BakedCode( return AVS_LinkCall(VFGetWritePtr)(plane) ) + + ~VideoFrame() AVS_BakedCode( AVS_LinkCall_Void(VideoFrame_DESTRUCTOR)() ) +#ifdef BUILDING_AVSCORE +public: + void DESTRUCTOR(); /* Damn compiler won't allow taking the address of reserved constructs, make a dummy interlude */ +#endif + +// Ensure VideoFrame cannot be publicly assigned +private: + VideoFrame& operator=(const VideoFrame&); + +}; // end class VideoFrame + +enum CachePolicyHint { + // Values 0 to 5 are reserved for old 2.5 plugins + // do not use them in new plugins + + // New 2.6 explicitly defined cache hints. + CACHE_NOTHING=10, // Do not cache video. + CACHE_WINDOW=11, // Hard protect upto X frames within a range of X from the current frame N. + CACHE_GENERIC=12, // LRU cache upto X frames. + CACHE_FORCE_GENERIC=13, // LRU cache upto X frames, override any previous CACHE_WINDOW. + + CACHE_GET_POLICY=30, // Get the current policy. + CACHE_GET_WINDOW=31, // Get the current window h_span. + CACHE_GET_RANGE=32, // Get the current generic frame range. + + CACHE_AUDIO=50, // Explicitly cache audio, X byte cache. + CACHE_AUDIO_NOTHING=51, // Explicitly do not cache audio. + CACHE_AUDIO_NONE=52, // Audio cache off (auto mode), X byte intial cache. + CACHE_AUDIO_AUTO=53, // Audio cache on (auto mode), X byte intial cache. + + CACHE_GET_AUDIO_POLICY=70, // Get the current audio policy. + CACHE_GET_AUDIO_SIZE=71, // Get the current audio cache size. + + CACHE_PREFETCH_FRAME=100, // Queue request to prefetch frame N. + CACHE_PREFETCH_GO=101, // Action video prefetches. + + CACHE_PREFETCH_AUDIO_BEGIN=120, // Begin queue request transaction to prefetch audio (take critical section). + CACHE_PREFETCH_AUDIO_STARTLO=121, // Set low 32 bits of start. + CACHE_PREFETCH_AUDIO_STARTHI=122, // Set high 32 bits of start. + CACHE_PREFETCH_AUDIO_COUNT=123, // Set low 32 bits of length. + CACHE_PREFETCH_AUDIO_COMMIT=124, // Enqueue request transaction to prefetch audio (release critical section). + CACHE_PREFETCH_AUDIO_GO=125, // Action audio prefetches. + + CACHE_GETCHILD_CACHE_MODE=200, // Cache ask Child for desired video cache mode. + CACHE_GETCHILD_CACHE_SIZE=201, // Cache ask Child for desired video cache size. + CACHE_GETCHILD_AUDIO_MODE=202, // Cache ask Child for desired audio cache mode. + CACHE_GETCHILD_AUDIO_SIZE=203, // Cache ask Child for desired audio cache size. + + CACHE_GETCHILD_COST=220, // Cache ask Child for estimated processing cost. + CACHE_COST_ZERO=221, // Child response of zero cost (ptr arithmetic only). + CACHE_COST_UNIT=222, // Child response of unit cost (less than or equal 1 full frame blit). + CACHE_COST_LOW=223, // Child response of light cost. (Fast) + CACHE_COST_MED=224, // Child response of medium cost. (Real time) + CACHE_COST_HI=225, // Child response of heavy cost. (Slow) + + CACHE_GETCHILD_THREAD_MODE=240, // Cache ask Child for thread safetyness. + CACHE_THREAD_UNSAFE=241, // Only 1 thread allowed for all instances. 2.5 filters default! + CACHE_THREAD_CLASS=242, // Only 1 thread allowed for each instance. 2.6 filters default! + CACHE_THREAD_SAFE=243, // Allow all threads in any instance. + CACHE_THREAD_OWN=244, // Safe but limit to 1 thread, internally threaded. + + CACHE_GETCHILD_ACCESS_COST=260, // Cache ask Child for preferred access pattern. + CACHE_ACCESS_RAND=261, // Filter is access order agnostic. + CACHE_ACCESS_SEQ0=262, // Filter prefers sequential access (low cost) + CACHE_ACCESS_SEQ1=263, // Filter needs sequential access (high cost) + + CACHE_AVSPLUS_CONSTANTS = 500, // Smaller values are reserved for classic Avisynth + + CACHE_DONT_CACHE_ME, // Filters that don't need caching (eg. trim, cache etc.) should return 1 to this request + CACHE_SET_MIN_CAPACITY, + CACHE_SET_MAX_CAPACITY, + CACHE_GET_MIN_CAPACITY, + CACHE_GET_MAX_CAPACITY, + CACHE_GET_SIZE, + CACHE_GET_REQUESTED_CAP, + CACHE_GET_CAPACITY, + CACHE_GET_MTMODE, + + CACHE_IS_CACHE_REQ, + CACHE_IS_CACHE_ANS, + CACHE_IS_MTGUARD_REQ, + CACHE_IS_MTGUARD_ANS, + + CACHE_USER_CONSTANTS = 1000 // Smaller values are reserved for the core + +}; + +// Base class for all filters. +class IClip { + friend class PClip; + friend class AVSValue; + volatile long refcnt; + void AddRef(); + void Release(); +public: + IClip() : refcnt(0) {} + virtual int __stdcall GetVersion() { return AVISYNTH_INTERFACE_VERSION; } + virtual PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env) = 0; + virtual bool __stdcall GetParity(int n) = 0; // return field parity if field_based, else parity of first field in frame + virtual void __stdcall GetAudio(void* buf, int64_t start, int64_t count, IScriptEnvironment* env) = 0; // start and count are in samples + /* Need to check GetVersion first, pre v5 will return random crap from EAX reg. */ + virtual int __stdcall SetCacheHints(int cachehints,int frame_range) = 0 ; // We do not pass cache requests upwards, only to the next filter. + virtual const VideoInfo& __stdcall GetVideoInfo() = 0; + virtual AVSC_CC ~IClip() {} +}; // end class IClip + + +// smart pointer to IClip +class PClip { + + IClip* p; + + IClip* GetPointerWithAddRef() const; + friend class AVSValue; + friend class VideoFrame; + + void Init(IClip* x); + void Set(IClip* x); + +public: + PClip() AVS_BakedCode( AVS_LinkCall_Void(PClip_CONSTRUCTOR0)() ) + PClip(const PClip& x) AVS_BakedCode( AVS_LinkCall_Void(PClip_CONSTRUCTOR1)(x) ) + PClip(IClip* x) AVS_BakedCode( AVS_LinkCall_Void(PClip_CONSTRUCTOR2)(x) ) + void operator=(IClip* x) AVS_BakedCode( AVS_LinkCall_Void(PClip_OPERATOR_ASSIGN0)(x) ) + void operator=(const PClip& x) AVS_BakedCode( AVS_LinkCall_Void(PClip_OPERATOR_ASSIGN1)(x) ) + + IClip* operator->() const { return p; } + + // useful in conditional expressions + operator void*() const { return p; } + bool operator!() const { return !p; } + + ~PClip() AVS_BakedCode( AVS_LinkCall_Void(PClip_DESTRUCTOR)() ) +#ifdef BUILDING_AVSCORE +public: + void CONSTRUCTOR0(); /* Damn compiler won't allow taking the address of reserved constructs, make a dummy interlude */ + void CONSTRUCTOR1(const PClip& x); + void CONSTRUCTOR2(IClip* x); + void OPERATOR_ASSIGN0(IClip* x); + void OPERATOR_ASSIGN1(const PClip& x); + void DESTRUCTOR(); +#endif +}; // end class PClip + + +// smart pointer to VideoFrame +class PVideoFrame { + + VideoFrame* p; + + void Init(VideoFrame* x); + void Set(VideoFrame* x); + +public: + PVideoFrame() AVS_BakedCode( AVS_LinkCall_Void(PVideoFrame_CONSTRUCTOR0)() ) + PVideoFrame(const PVideoFrame& x) AVS_BakedCode( AVS_LinkCall_Void(PVideoFrame_CONSTRUCTOR1)(x) ) + PVideoFrame(VideoFrame* x) AVS_BakedCode( AVS_LinkCall_Void(PVideoFrame_CONSTRUCTOR2)(x) ) + void operator=(VideoFrame* x) AVS_BakedCode( AVS_LinkCall_Void(PVideoFrame_OPERATOR_ASSIGN0)(x) ) + void operator=(const PVideoFrame& x) AVS_BakedCode( AVS_LinkCall_Void(PVideoFrame_OPERATOR_ASSIGN1)(x) ) + + VideoFrame* operator->() const { return p; } + + // for conditional expressions + operator void*() const { return p; } + bool operator!() const { return !p; } + + ~PVideoFrame() AVS_BakedCode( AVS_LinkCall_Void(PVideoFrame_DESTRUCTOR)() ) +#ifdef BUILDING_AVSCORE +public: + void CONSTRUCTOR0(); /* Damn compiler won't allow taking the address of reserved constructs, make a dummy interlude */ + void CONSTRUCTOR1(const PVideoFrame& x); + void CONSTRUCTOR2(VideoFrame* x); + void OPERATOR_ASSIGN0(VideoFrame* x); + void OPERATOR_ASSIGN1(const PVideoFrame& x); + void DESTRUCTOR(); +#endif +}; // end class PVideoFrame + + +class AVSValue { +public: + + AVSValue() AVS_BakedCode( AVS_LinkCall_Void(AVSValue_CONSTRUCTOR0)() ) + AVSValue(IClip* c) AVS_BakedCode( AVS_LinkCall_Void(AVSValue_CONSTRUCTOR1)(c) ) + AVSValue(const PClip& c) AVS_BakedCode( AVS_LinkCall_Void(AVSValue_CONSTRUCTOR2)(c) ) + AVSValue(bool b) AVS_BakedCode( AVS_LinkCall_Void(AVSValue_CONSTRUCTOR3)(b) ) + AVSValue(int i) AVS_BakedCode( AVS_LinkCall_Void(AVSValue_CONSTRUCTOR4)(i) ) +// AVSValue(int64_t l); + AVSValue(float f) AVS_BakedCode( AVS_LinkCall_Void(AVSValue_CONSTRUCTOR5)(f) ) + AVSValue(double f) AVS_BakedCode( AVS_LinkCall_Void(AVSValue_CONSTRUCTOR6)(f) ) + AVSValue(const char* s) AVS_BakedCode( AVS_LinkCall_Void(AVSValue_CONSTRUCTOR7)(s) ) + AVSValue(const AVSValue* a, int size) AVS_BakedCode( AVS_LinkCall_Void(AVSValue_CONSTRUCTOR8)(a, size) ) + AVSValue(const AVSValue& a, int size) AVS_BakedCode( AVS_LinkCall_Void(AVSValue_CONSTRUCTOR8)(&a, size) ) + AVSValue(const AVSValue& v) AVS_BakedCode( AVS_LinkCall_Void(AVSValue_CONSTRUCTOR9)(v) ) + + ~AVSValue() AVS_BakedCode( AVS_LinkCall_Void(AVSValue_DESTRUCTOR)() ) + AVSValue& operator=(const AVSValue& v) AVS_BakedCode( return AVS_LinkCallV(AVSValue_OPERATOR_ASSIGN)(v) ) + + // Note that we transparently allow 'int' to be treated as 'float'. + // There are no int<->bool conversions, though. + + bool Defined() const AVS_BakedCode( return AVS_LinkCall(Defined)() ) + bool IsClip() const AVS_BakedCode( return AVS_LinkCall(IsClip)() ) + bool IsBool() const AVS_BakedCode( return AVS_LinkCall(IsBool)() ) + bool IsInt() const AVS_BakedCode( return AVS_LinkCall(IsInt)() ) +// bool IsLong() const; + bool IsFloat() const AVS_BakedCode( return AVS_LinkCall(IsFloat)() ) + bool IsString() const AVS_BakedCode( return AVS_LinkCall(IsString)() ) + bool IsArray() const AVS_BakedCode( return AVS_LinkCall(IsArray)() ) + + PClip AsClip() const AVS_BakedCode( return AVS_LinkCall(AsClip)() ) + bool AsBool() const AVS_BakedCode( return AVS_LinkCall(AsBool1)() ) + int AsInt() const AVS_BakedCode( return AVS_LinkCall(AsInt1)() ) +// int AsLong() const; + const char* AsString() const AVS_BakedCode( return AVS_LinkCall(AsString1)() ) + double AsFloat() const AVS_BakedCode( return AVS_LinkCall(AsFloat1)() ) + float AsFloatf() const AVS_BakedCode( return float( AVS_LinkCall(AsFloat1)() ) ) + + bool AsBool(bool def) const AVS_BakedCode( return AVS_LinkCall(AsBool2)(def) ) + int AsInt(int def) const AVS_BakedCode( return AVS_LinkCall(AsInt2)(def) ) + double AsDblDef(double def) const AVS_BakedCode( return AVS_LinkCall(AsDblDef)(def) ) // Value is still a float + double AsFloat(float def) const AVS_BakedCode( return AVS_LinkCall(AsFloat2)(def) ) + float AsFloatf(float def) const AVS_BakedCode( return float( AVS_LinkCall(AsFloat2)(def) ) ) + const char* AsString(const char* def) const AVS_BakedCode( return AVS_LinkCall(AsString2)(def) ) + + int ArraySize() const AVS_BakedCode( return AVS_LinkCall(ArraySize)() ) + + const AVSValue& operator[](int index) const AVS_BakedCode( return AVS_LinkCallV(AVSValue_OPERATOR_INDEX)(index) ) + +private: + + short type; // 'a'rray, 'c'lip, 'b'ool, 'i'nt, 'f'loat, 's'tring, 'v'oid, or RFU: 'l'ong ('d'ouble) + short array_size; + union { + IClip* clip; + bool boolean; + int integer; + float floating_pt; + const char* string; + const AVSValue* array; + #ifdef X86_64 + // if ever, only x64 will support. It breaks struct size on 32 bit + int64_t longlong; // 8 bytes + double double_pt; // 8 bytes + #endif + }; + + void Assign(const AVSValue* src, bool init); +#ifdef BUILDING_AVSCORE +public: + void CONSTRUCTOR0(); /* Damn compiler won't allow taking the address of reserved constructs, make a dummy interlude */ + void CONSTRUCTOR1(IClip* c); + void CONSTRUCTOR2(const PClip& c); + void CONSTRUCTOR3(bool b); + void CONSTRUCTOR4(int i); + void CONSTRUCTOR5(float f); + void CONSTRUCTOR6(double f); + void CONSTRUCTOR7(const char* s); + void CONSTRUCTOR8(const AVSValue* a, int size); + void CONSTRUCTOR9(const AVSValue& v); + void DESTRUCTOR(); + AVSValue& OPERATOR_ASSIGN(const AVSValue& v); + const AVSValue& OPERATOR_INDEX(int index) const; + + bool AsBool1() const; + int AsInt1() const; + const char* AsString1() const; + double AsFloat1() const; + + bool AsBool2(bool def) const; + int AsInt2(int def) const; + double AsFloat2(float def) const; + const char* AsString2(const char* def) const; + +#ifdef NEW_AVSVALUE + void MarkArrayAsC(); + void CONSTRUCTOR10(const AVSValue& v, bool c_arrays); + AVSValue(const AVSValue& v, bool c_arrays); + void Assign2(const AVSValue* src, bool init, bool c_arrays); +#endif + +#endif +}; // end class AVSValue + +#undef CALL_MEMBER_FN +#undef AVS_LinkCallOptDefault +#undef AVS_LinkCallOpt +#undef AVS_LinkCallV +#undef AVS_LinkCall +#undef AVS_BakedCode + +#define AVS_UNUSED(x) (void)(x) + +// instantiable null filter +class GenericVideoFilter : public IClip { +protected: + PClip child; + VideoInfo vi; +public: + GenericVideoFilter(PClip _child) : child(_child) { vi = child->GetVideoInfo(); } + PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* env) { return child->GetFrame(n, env); } + void __stdcall GetAudio(void* buf, int64_t start, int64_t count, IScriptEnvironment* env) { child->GetAudio(buf, start, count, env); } + const VideoInfo& __stdcall GetVideoInfo() { return vi; } + bool __stdcall GetParity(int n) { return child->GetParity(n); } + int __stdcall SetCacheHints(int cachehints, int frame_range) { AVS_UNUSED(cachehints); AVS_UNUSED(frame_range); return 0; }; // We do not pass cache requests upwards, only to the next filter. +}; + + + + +#include "avs/cpuid.h" + + + +class IScriptEnvironment { +public: + virtual AVSC_CC ~IScriptEnvironment() {} + + virtual /*static*/ int __stdcall GetCPUFlags() = 0; + + virtual char* __stdcall SaveString(const char* s, int length = -1) = 0; + virtual char* __stdcall Sprintf(const char* fmt, ...) = 0; + // note: val is really a va_list; I hope everyone typedefs va_list to a pointer + virtual char* __stdcall VSprintf(const char* fmt, void* val) = 0; + +#ifdef AVS_WINDOWS + __declspec(noreturn) virtual void __stdcall ThrowError(const char* fmt, ...) = 0; +#else + virtual void __stdcall ThrowError(const char* fmt, ...) = 0; +#endif + + class NotFound /*exception*/ {}; // thrown by Invoke and GetVar + + typedef AVSValue (__cdecl *ApplyFunc)(AVSValue args, void* user_data, IScriptEnvironment* env); + + virtual void __stdcall AddFunction(const char* name, const char* params, ApplyFunc apply, void* user_data) = 0; + virtual bool __stdcall FunctionExists(const char* name) = 0; + virtual AVSValue __stdcall Invoke(const char* name, const AVSValue args, const char* const* arg_names=0) = 0; + + virtual AVSValue __stdcall GetVar(const char* name) = 0; + virtual bool __stdcall SetVar(const char* name, const AVSValue& val) = 0; + virtual bool __stdcall SetGlobalVar(const char* name, const AVSValue& val) = 0; + + virtual void __stdcall PushContext(int level=0) = 0; + virtual void __stdcall PopContext() = 0; + + // align should be 4 or 8 + virtual PVideoFrame __stdcall NewVideoFrame(const VideoInfo& vi, int align=FRAME_ALIGN) = 0; + + virtual bool __stdcall MakeWritable(PVideoFrame* pvf) = 0; + + virtual void __stdcall BitBlt(BYTE* dstp, int dst_pitch, const BYTE* srcp, int src_pitch, int row_size, int height) = 0; + + typedef void (__cdecl *ShutdownFunc)(void* user_data, IScriptEnvironment* env); + virtual void __stdcall AtExit(ShutdownFunc function, void* user_data) = 0; + + virtual void __stdcall CheckVersion(int version = AVISYNTH_INTERFACE_VERSION) = 0; + + virtual PVideoFrame __stdcall Subframe(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, int new_height) = 0; + + virtual int __stdcall SetMemoryMax(int mem) = 0; + + virtual int __stdcall SetWorkingDir(const char * newdir) = 0; + + virtual void* __stdcall ManageCache(int key, void* data) = 0; + + enum PlanarChromaAlignmentMode { + PlanarChromaAlignmentOff, + PlanarChromaAlignmentOn, + PlanarChromaAlignmentTest }; + + virtual bool __stdcall PlanarChromaAlignment(PlanarChromaAlignmentMode key) = 0; + + virtual PVideoFrame __stdcall SubframePlanar(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, + int new_height, int rel_offsetU, int rel_offsetV, int new_pitchUV) = 0; + + virtual void __stdcall DeleteScriptEnvironment() = 0; + + virtual void __stdcall ApplyMessage(PVideoFrame* frame, const VideoInfo& vi, const char* message, int size, + int textcolor, int halocolor, int bgcolor) = 0; + + virtual const AVS_Linkage* const __stdcall GetAVSLinkage() = 0; + + // noThrow version of GetVar + virtual AVSValue __stdcall GetVarDef(const char* name, const AVSValue& def = AVSValue()) = 0; + +}; // end class IScriptEnvironment + + +enum MtMode +{ + MT_INVALID = 0, + MT_NICE_FILTER = 1, + MT_MULTI_INSTANCE = 2, + MT_SERIALIZED = 3, + MT_SPECIAL_MT = 4, + MT_MODE_COUNT = 5 +}; + +class IJobCompletion +{ +public: + + virtual __stdcall ~IJobCompletion() {} + virtual void __stdcall Wait() = 0; + virtual AVSValue __stdcall Get(size_t i) = 0; + virtual size_t __stdcall Size() const = 0; + virtual size_t __stdcall Capacity() const = 0; + virtual void __stdcall Reset() = 0; + virtual void __stdcall Destroy() = 0; +}; + +class IScriptEnvironment2; +class Prefetcher; +typedef AVSValue (*ThreadWorkerFuncPtr)(IScriptEnvironment2* env, void* data); + +enum AvsEnvProperty +{ + AEP_PHYSICAL_CPUS = 1, + AEP_LOGICAL_CPUS = 2, + AEP_THREADPOOL_THREADS = 3, + AEP_FILTERCHAIN_THREADS = 4, + AEP_THREAD_ID = 5, + AEP_VERSION = 6 +}; + +enum AvsAllocType +{ + AVS_NORMAL_ALLOC = 1, + AVS_POOLED_ALLOC = 2 +}; + +/* ----------------------------------------------------------------------------- + Note to plugin authors: The interface in IScriptEnvironment2 is + preliminary / under construction / only for testing / non-final etc.! + As long as you see this note here, IScriptEnvironment2 might still change, + in which case your plugin WILL break. This also means that you are welcome + to test it and give your feedback about any ideas, improvements, or issues + you might have. + ----------------------------------------------------------------------------- */ +class AVSFunction; +class IScriptEnvironment2 : public IScriptEnvironment{ +public: + virtual ~IScriptEnvironment2() {} + + // Generic system to ask for various properties + virtual size_t __stdcall GetProperty(AvsEnvProperty prop) = 0; + + // Returns TRUE and the requested variable. If the method fails, returns FALSE and does not touch 'val'. + virtual bool __stdcall GetVar(const char* name, AVSValue *val) const = 0; + + // Return the value of the requested variable. + // If the variable was not found or had the wrong type, + // return the supplied default value. + virtual bool __stdcall GetVar(const char* name, bool def) const = 0; + virtual int __stdcall GetVar(const char* name, int def) const = 0; + virtual double __stdcall GetVar(const char* name, double def) const = 0; + virtual const char* __stdcall GetVar(const char* name, const char* def) const = 0; + + // Plugin functions + virtual bool __stdcall LoadPlugin(const char* filePath, bool throwOnError, AVSValue *result) = 0; + virtual void __stdcall AddAutoloadDir(const char* dirPath, bool toFront) = 0; + virtual void __stdcall ClearAutoloadDirs() = 0; + virtual void __stdcall AutoloadPlugins() = 0; + virtual void __stdcall AddFunction(const char* name, const char* params, ApplyFunc apply, void* user_data, const char *exportVar) = 0; + virtual bool __stdcall InternalFunctionExists(const char* name) = 0; + + // Threading + virtual void __stdcall SetFilterMTMode(const char* filter, MtMode mode, bool force) = 0; // If filter is "DEFAULT_MT_MODE", sets the default MT mode + virtual IJobCompletion* __stdcall NewCompletion(size_t capacity) = 0; + virtual void __stdcall ParallelJob(ThreadWorkerFuncPtr jobFunc, void* jobData, IJobCompletion* completion) = 0; + + // This version of Invoke will return false instead of throwing NotFound(). + virtual bool __stdcall Invoke(AVSValue *result, const char* name, const AVSValue& args, const char* const* arg_names=0) = 0; + + // Support functions + virtual void* __stdcall Allocate(size_t nBytes, size_t alignment, AvsAllocType type) = 0; + virtual void __stdcall Free(void* ptr) = 0; + + // These lines are needed so that we can overload the older functions from IScriptEnvironment. + using IScriptEnvironment::Invoke; + using IScriptEnvironment::AddFunction; + using IScriptEnvironment::GetVar; + + virtual PVideoFrame __stdcall SubframePlanarA(PVideoFrame src, int rel_offset, int new_pitch, int new_row_size, + int new_height, int rel_offsetU, int rel_offsetV, int new_pitchUV, int rel_offsetA) = 0; + +}; // end class IScriptEnvironment2 + + +// avisynth.dll exports this; it's a way to use it as a library, without +// writing an AVS script or without going through AVIFile. +AVSC_API(IScriptEnvironment*, CreateScriptEnvironment)(int version = AVISYNTH_INTERFACE_VERSION); + + +// These are some global variables you can set in your script to change AviSynth's behavior. +#define VARNAME_AllowFloatAudio "OPT_AllowFloatAudio" // Allow WAVE_FORMAT_IEEE_FLOAT audio output +#define VARNAME_VDubPlanarHack "OPT_VDubPlanarHack" // Hack YV16 and YV24 chroma plane order for old VDub's +#define VARNAME_AVIPadScanlines "OPT_AVIPadScanlines" // Have scanlines mod4 padded in all pixel formats +#define VARNAME_UseWaveExtensible "OPT_UseWaveExtensible" // Use WAVEFORMATEXTENSIBLE when describing audio to Windows +#define VARNAME_dwChannelMask "OPT_dwChannelMask" // Integer audio channel mask. See description of WAVEFORMATEXTENSIBLE for more info. +#define VARNAME_Enable_V210 "OPT_Enable_V210" // AVS+ use V210 instead of P210 (VfW) +#define VARNAME_Enable_Y3_10_10 "OPT_Enable_Y3_10_10" // AVS+ use Y3[10][10] instead of P210 (VfW) +#define VARNAME_Enable_Y3_10_16 "OPT_Enable_Y3_10_16" // AVS+ use Y3[10][16] instead of P216 (VfW) +#define VARNAME_Enable_b64a "OPT_Enable_b64a" // AVS+ use b64a instead of BRA[64] (VfW) +#define VARNAME_Enable_PlanarToPackedRGB "OPT_Enable_PlanarToPackedRGB" // AVS+ convert Planar RGB to packed RGB (VfW) + +// C exports +#include "avs/capi.h" +AVSC_API(IScriptEnvironment2*, CreateScriptEnvironment2)(int version = AVISYNTH_INTERFACE_VERSION); + +#ifndef BUILDING_AVSCORE +#undef AVS_UNUSED +#endif + +#pragma pack(pop) + +#endif //__AVISYNTH_6_H__ diff --git a/include/avisynth/avs/alignment.h b/include/avisynth/avs/alignment.h new file mode 100644 index 0000000..170d9cc --- /dev/null +++ b/include/avisynth/avs/alignment.h @@ -0,0 +1,134 @@ +// Avisynth C Interface Version 0.20 +// Copyright 2003 Kevin Atkinson + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit +// http://www.gnu.org/copyleft/gpl.html . +// +// As a special exception, I give you permission to link to the +// Avisynth C interface with independent modules that communicate with +// the Avisynth C interface solely through the interfaces defined in +// avisynth_c.h, regardless of the license terms of these independent +// modules, and to copy and distribute the resulting combined work +// under terms of your choice, provided that every copy of the +// combined work is accompanied by a complete copy of the source code +// of the Avisynth C interface and Avisynth itself (with the version +// used to produce the combined work), being distributed under the +// terms of the GNU General Public License plus this exception. An +// independent module is a module which is not derived from or based +// on Avisynth C Interface, such as 3rd-party filters, import and +// export plugins, or graphical user interfaces. + +#ifndef AVS_ALIGNMENT_H +#define AVS_ALIGNMENT_H + +// Functions and macros to help work with alignment requirements. + +// Tells if a number is a power of two. +#define IS_POWER2(n) ((n) && !((n) & ((n) - 1))) + +// Tells if the pointer "ptr" is aligned to "align" bytes. +#define IS_PTR_ALIGNED(ptr, align) (((uintptr_t)ptr & ((uintptr_t)(align-1))) == 0) + +// Rounds up the number "n" to the next greater multiple of "align" +#define ALIGN_NUMBER(n, align) (((n) + (align)-1) & (~((align)-1))) + +// Rounds up the pointer address "ptr" to the next greater multiple of "align" +#define ALIGN_POINTER(ptr, align) (((uintptr_t)(ptr) + (align)-1) & (~(uintptr_t)((align)-1))) + +#ifdef __cplusplus + +#include +#include +#include +#include "config.h" + +#if defined(MSVC) && _MSC_VER<1400 + // needed for VS2013, otherwise C++11 'alignas' works + #define avs_alignas(x) __declspec(align(x)) +#else + // assumes C++11 support + #define avs_alignas(x) alignas(x) +#endif + +template +static bool IsPtrAligned(T* ptr, size_t align) +{ + assert(IS_POWER2(align)); + return (bool)IS_PTR_ALIGNED(ptr, align); +} + +template +static T AlignNumber(T n, T align) +{ + assert(IS_POWER2(align)); + return ALIGN_NUMBER(n, align); +} + +template +static T* AlignPointer(T* ptr, size_t align) +{ + assert(IS_POWER2(align)); + return (T*)ALIGN_POINTER(ptr, align); +} + +extern "C" +{ +#else +#include +#endif // __cplusplus + +// Returns a new buffer that is at least the size "nbytes". +// The buffer will be aligned to "align" bytes. +// Returns NULL on error. On successful allocation, +// the returned buffer must be freed using "avs_free". +inline void* avs_malloc(size_t nbytes, size_t align) +{ + if (!IS_POWER2(align)) + return NULL; + + size_t offset = sizeof(void*) + align - 1; + + void *orig = malloc(nbytes + offset); + if (orig == NULL) + return NULL; + + void **aligned = (void**)(((uintptr_t)orig + (uintptr_t)offset) & (~(uintptr_t)(align-1))); + aligned[-1] = orig; + return aligned; +} + +// Buffers allocated using "avs_malloc" must be freed +// using "avs_free" instead of "free". +inline void avs_free(void *ptr) +{ + // Mirroring free()'s semantic requires us to accept NULLs + if (ptr == NULL) + return; + + free(((void**)ptr)[-1]); +} + +#ifdef __cplusplus +} // extern "C" + +// The point of these undef's is to force using the template functions +// if we are in C++ mode. For C, the user can rely only on the macros. +#undef IS_PTR_ALIGNED +#undef ALIGN_NUMBER +#undef ALIGN_POINTER + +#endif // __cplusplus + +#endif //AVS_ALIGNMENT_H diff --git a/include/avisynth/avs/capi.h b/include/avisynth/avs/capi.h new file mode 100644 index 0000000..624de0b --- /dev/null +++ b/include/avisynth/avs/capi.h @@ -0,0 +1,111 @@ +// Avisynth C Interface Version 0.20 +// Copyright 2003 Kevin Atkinson + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit +// http://www.gnu.org/copyleft/gpl.html . +// +// As a special exception, I give you permission to link to the +// Avisynth C interface with independent modules that communicate with +// the Avisynth C interface solely through the interfaces defined in +// avisynth_c.h, regardless of the license terms of these independent +// modules, and to copy and distribute the resulting combined work +// under terms of your choice, provided that every copy of the +// combined work is accompanied by a complete copy of the source code +// of the Avisynth C interface and Avisynth itself (with the version +// used to produce the combined work), being distributed under the +// terms of the GNU General Public License plus this exception. An +// independent module is a module which is not derived from or based +// on Avisynth C Interface, such as 3rd-party filters, import and +// export plugins, or graphical user interfaces. + +#ifndef AVS_CAPI_H +#define AVS_CAPI_H + +#include "config.h" + +#ifdef AVS_POSIX +// this is also defined in avs/posix.h, but that header is for the core, +// not external programs +#define __declspec(x) +#endif + +#ifdef __cplusplus +# define EXTERN_C extern "C" +#else +# define EXTERN_C +#endif + +#ifdef AVS_WINDOWS +#ifdef BUILDING_AVSCORE +# if defined(GCC) && defined(X86_32) +# define AVSC_CC +# else // MSVC builds and 64-bit GCC +# ifndef AVSC_USE_STDCALL +# define AVSC_CC __cdecl +# else +# define AVSC_CC __stdcall +# endif +# endif +#else // needed for programs that talk to AviSynth+ +# ifndef AVSC_WIN32_GCC32 // see comment below +# ifndef AVSC_USE_STDCALL +# define AVSC_CC __cdecl +# else +# define AVSC_CC __stdcall +# endif +# else +# define AVSC_CC +# endif +#endif +# else +# define AVSC_CC +#endif + +// On 64-bit Windows, there's only one calling convention, +// so there is no difference between MSVC and GCC. On 32-bit, +// this isn't true. The convention that GCC needs to use to +// even build AviSynth+ as 32-bit makes anything that uses +// it incompatible with 32-bit MSVC builds of AviSynth+. +// The AVSC_WIN32_GCC32 define is meant to provide a user +// switchable way to make builds of FFmpeg to test 32-bit +// GCC builds of AviSynth+ without having to screw around +// with alternate headers, while still default to the usual +// situation of using 32-bit MSVC builds of AviSynth+. + +// Hopefully, this situation will eventually be resolved +// and a broadly compatible solution will arise so the +// same 32-bit FFmpeg build can handle either MSVC or GCC +// builds of AviSynth+. + +#define AVSC_INLINE static __inline + +#ifdef BUILDING_AVSCORE +#ifdef AVS_WINDOWS +# define AVSC_EXPORT __declspec(dllexport) +# define AVSC_API(ret, name) EXTERN_C AVSC_EXPORT ret AVSC_CC name +#else +# define AVSC_EXPORT EXTERN_C +# define AVSC_API(ret, name) EXTERN_C ret AVSC_CC name +#endif +#else +# define AVSC_EXPORT EXTERN_C __declspec(dllexport) +# ifndef AVSC_NO_DECLSPEC +# define AVSC_API(ret, name) EXTERN_C __declspec(dllimport) ret AVSC_CC name +# else +# define AVSC_API(ret, name) typedef ret (AVSC_CC *name##_func) +# endif +#endif + +#endif //AVS_CAPI_H diff --git a/include/avisynth/avs/config.h b/include/avisynth/avs/config.h new file mode 100644 index 0000000..8daf5a7 --- /dev/null +++ b/include/avisynth/avs/config.h @@ -0,0 +1,105 @@ +// Avisynth C Interface Version 0.20 +// Copyright 2003 Kevin Atkinson + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit +// http://www.gnu.org/copyleft/gpl.html . +// +// As a special exception, I give you permission to link to the +// Avisynth C interface with independent modules that communicate with +// the Avisynth C interface solely through the interfaces defined in +// avisynth_c.h, regardless of the license terms of these independent +// modules, and to copy and distribute the resulting combined work +// under terms of your choice, provided that every copy of the +// combined work is accompanied by a complete copy of the source code +// of the Avisynth C interface and Avisynth itself (with the version +// used to produce the combined work), being distributed under the +// terms of the GNU General Public License plus this exception. An +// independent module is a module which is not derived from or based +// on Avisynth C Interface, such as 3rd-party filters, import and +// export plugins, or graphical user interfaces. + +#ifndef AVS_CONFIG_H +#define AVS_CONFIG_H + +// Undefine this to get cdecl calling convention +#define AVSC_USE_STDCALL 1 + +// NOTE TO PLUGIN AUTHORS: +// Because FRAME_ALIGN can be substantially higher than the alignment +// a plugin actually needs, plugins should not use FRAME_ALIGN to check for +// alignment. They should always request the exact alignment value they need. +// This is to make sure that plugins work over the widest range of AviSynth +// builds possible. +#define FRAME_ALIGN 64 + +#if defined(_M_AMD64) || defined(__x86_64) +# define X86_64 +#elif defined(_M_IX86) || defined(__i386__) +# define X86_32 +#else +# error Unsupported CPU architecture. +#endif + +// VC++ LLVM-Clang-cl MinGW-Gnu +// MSVC x x +// MSVC_PURE x +// CLANG x +// GCC x + +#if defined(__clang__) +// Check clang first. clang-cl also defines __MSC_VER +// We set MSVC because they are mostly compatible +# define CLANG +#if defined(_MSC_VER) +# define MSVC +# define AVS_FORCEINLINE __attribute__((always_inline)) +#else +# define GCC +# define AVS_FORCEINLINE __attribute__((always_inline)) inline +#endif +#elif defined(_MSC_VER) +# define MSVC +# define MSVC_PURE +# define AVS_FORCEINLINE __forceinline +#elif defined(__GNUC__) +# define GCC +# define AVS_FORCEINLINE __attribute__((always_inline)) inline +#else +# error Unsupported compiler. +# define AVS_FORCEINLINE inline +# undef __forceinline +# define __forceinline inline +#endif + +#if defined(_WIN32) +# define AVS_WINDOWS +#elif defined(__linux__) +# define AVS_LINUX +# define AVS_POSIX +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) +# define AVS_BSD +# define AVS_POSIX +#elif defined(__APPLE__) +# define AVS_MACOS +# define AVS_POSIX +#else +# error Operating system unsupported. +#endif + +#if defined(AVS_POSIX) +#define NEW_AVSVALUE +#endif + +#endif //AVS_CONFIG_H diff --git a/include/avisynth/avs/cpuid.h b/include/avisynth/avs/cpuid.h new file mode 100644 index 0000000..b635444 --- /dev/null +++ b/include/avisynth/avs/cpuid.h @@ -0,0 +1,80 @@ +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit +// http://www.gnu.org/copyleft/gpl.html . +// +// Linking Avisynth statically or dynamically with other modules is making a +// combined work based on Avisynth. Thus, the terms and conditions of the GNU +// General Public License cover the whole combination. +// +// As a special exception, the copyright holders of Avisynth give you +// permission to link Avisynth with independent modules that communicate with +// Avisynth solely through the interfaces defined in avisynth.h, regardless of the license +// terms of these independent modules, and to copy and distribute the +// resulting combined work under terms of your choice, provided that +// every copy of the combined work is accompanied by a complete copy of +// the source code of Avisynth (the version of Avisynth used to produce the +// combined work), being distributed under the terms of the GNU General +// Public License plus this exception. An independent module is a module +// which is not derived from or based on Avisynth, such as 3rd-party filters, +// import and export plugins, or graphical user interfaces. + +#ifndef AVSCORE_CPUID_H +#define AVSCORE_CPUID_H + +// For GetCPUFlags. These are backwards-compatible with those in VirtualDub. +// ending with SSE4_2 +// For emulation see https://software.intel.com/en-us/articles/intel-software-development-emulator +enum { + /* oldest CPU to support extension */ + CPUF_FORCE = 0x01, // N/A + CPUF_FPU = 0x02, // 386/486DX + CPUF_MMX = 0x04, // P55C, K6, PII + CPUF_INTEGER_SSE = 0x08, // PIII, Athlon + CPUF_SSE = 0x10, // PIII, Athlon XP/MP + CPUF_SSE2 = 0x20, // PIV, K8 + CPUF_3DNOW = 0x40, // K6-2 + CPUF_3DNOW_EXT = 0x80, // Athlon + CPUF_X86_64 = 0xA0, // Hammer (note: equiv. to 3DNow + SSE2, which + // only Hammer will have anyway) + CPUF_SSE3 = 0x100, // PIV+, K8 Venice + CPUF_SSSE3 = 0x200, // Core 2 + CPUF_SSE4 = 0x400, + CPUF_SSE4_1 = 0x400, // Penryn, Wolfdale, Yorkfield + CPUF_AVX = 0x800, // Sandy Bridge, Bulldozer + CPUF_SSE4_2 = 0x1000, // Nehalem + // AVS+ + CPUF_AVX2 = 0x2000, // Haswell + CPUF_FMA3 = 0x4000, + CPUF_F16C = 0x8000, + CPUF_MOVBE = 0x10000, // Big Endian move + CPUF_POPCNT = 0x20000, + CPUF_AES = 0x40000, + CPUF_FMA4 = 0x80000, + + CPUF_AVX512F = 0x100000, // AVX-512 Foundation. + CPUF_AVX512DQ = 0x200000, // AVX-512 DQ (Double/Quad granular) Instructions + CPUF_AVX512PF = 0x400000, // AVX-512 Prefetch + CPUF_AVX512ER = 0x800000, // AVX-512 Exponential and Reciprocal + CPUF_AVX512CD = 0x1000000, // AVX-512 Conflict Detection + CPUF_AVX512BW = 0x2000000, // AVX-512 BW (Byte/Word granular) Instructions + CPUF_AVX512VL = 0x4000000, // AVX-512 VL (128/256 Vector Length) Extensions + CPUF_AVX512IFMA = 0x8000000, // AVX-512 IFMA integer 52 bit + CPUF_AVX512VBMI = 0x10000000,// AVX-512 VBMI +}; + +#ifdef BUILDING_AVSCORE +int GetCPUFlags(); +#endif + +#endif // AVSCORE_CPUID_H diff --git a/include/avisynth/avs/minmax.h b/include/avisynth/avs/minmax.h new file mode 100644 index 0000000..521ea58 --- /dev/null +++ b/include/avisynth/avs/minmax.h @@ -0,0 +1,54 @@ +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit +// http://www.gnu.org/copyleft/gpl.html . +// +// Linking Avisynth statically or dynamically with other modules is making a +// combined work based on Avisynth. Thus, the terms and conditions of the GNU +// General Public License cover the whole combination. +// +// As a special exception, the copyright holders of Avisynth give you +// permission to link Avisynth with independent modules that communicate with +// Avisynth solely through the interfaces defined in avisynth.h, regardless of the license +// terms of these independent modules, and to copy and distribute the +// resulting combined work under terms of your choice, provided that +// every copy of the combined work is accompanied by a complete copy of +// the source code of Avisynth (the version of Avisynth used to produce the +// combined work), being distributed under the terms of the GNU General +// Public License plus this exception. An independent module is a module +// which is not derived from or based on Avisynth, such as 3rd-party filters, +// import and export plugins, or graphical user interfaces. + +#ifndef AVSCORE_MINMAX_H +#define AVSCORE_MINMAX_H + +template +T min(T v1, T v2) +{ + return v1 < v2 ? v1 : v2; +} + +template +T max(T v1, T v2) +{ + return v1 > v2 ? v1 : v2; +} + +template +T clamp(T n, T min, T max) +{ + n = n > max ? max : n; + return n < min ? min : n; +} + +#endif // AVSCORE_MINMAX_H diff --git a/include/avisynth/avs/posix.h b/include/avisynth/avs/posix.h new file mode 100644 index 0000000..9c938b2 --- /dev/null +++ b/include/avisynth/avs/posix.h @@ -0,0 +1,111 @@ +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit +// http://www.gnu.org/copyleft/gpl.html . +// +// Linking Avisynth statically or dynamically with other modules is making a +// combined work based on Avisynth. Thus, the terms and conditions of the GNU +// General Public License cover the whole combination. +// +// As a special exception, the copyright holders of Avisynth give you +// permission to link Avisynth with independent modules that communicate with +// Avisynth solely through the interfaces defined in avisynth.h, regardless of the license +// terms of these independent modules, and to copy and distribute the +// resulting combined work under terms of your choice, provided that +// every copy of the combined work is accompanied by a complete copy of +// the source code of Avisynth (the version of Avisynth used to produce the +// combined work), being distributed under the terms of the GNU General +// Public License plus this exception. An independent module is a module +// which is not derived from or based on Avisynth, such as 3rd-party filters, +// import and export plugins, or graphical user interfaces. + +#ifdef AVS_POSIX +#ifndef AVSCORE_POSIX_H +#define AVSCORE_POSIX_H + +#ifdef __cplusplus +#include +#endif +#include +#include + +// Define these MSVC-extension used in Avisynth +#define __single_inheritance + +// These things don't exist in Linux +#define __declspec(x) +#define lstrlen strlen +#define lstrcmp strcmp +#define lstrcmpi strcasecmp +#define _stricmp strcasecmp +#define _strnicmp strncasecmp +#define _strdup strdup +#define SetCurrentDirectory(x) chdir(x) +#define SetCurrentDirectoryW(x) chdir(x) +#define GetCurrentDirectoryW(x) getcwd(x) +#define _putenv putenv +#define _alloca alloca + +// Borrowing some compatibility macros from AvxSynth, slightly modified +#define UInt32x32To64(a, b) ((uint64_t)(((uint64_t)((uint32_t)(a))) * ((uint32_t)(b)))) +#define Int64ShrlMod32(a, b) ((uint64_t)((uint64_t)(a) >> (b))) +#define Int32x32To64(a, b) ((int64_t)(((int64_t)((long)(a))) * ((long)(b)))) + +#define InterlockedIncrement(x) __sync_add_and_fetch((x), 1) +#define InterlockedDecrement(x) __sync_sub_and_fetch((x), 1) +#define MulDiv(nNumber, nNumerator, nDenominator) (int32_t) (((int64_t) (nNumber) * (int64_t) (nNumerator) + (int64_t) ((nDenominator)/2)) / (int64_t) (nDenominator)) + +#ifndef TRUE +#define TRUE true +#endif + +#ifndef FALSE +#define FALSE false +#endif + +#define S_FALSE (0x00000001) +#define E_FAIL (0x80004005) +#define FAILED(hr) ((hr) & 0x80000000) +#define SUCCEEDED(hr) (!FAILED(hr)) + +// Statuses copied from comments in exception.cpp +#define STATUS_GUARD_PAGE_VIOLATION 0x80000001 +#define STATUS_DATATYPE_MISALIGNMENT 0x80000002 +#define STATUS_BREAKPOINT 0x80000003 +#define STATUS_SINGLE_STEP 0x80000004 +#define STATUS_ACCESS_VIOLATION 0xc0000005 +#define STATUS_IN_PAGE_ERROR 0xc0000006 +#define STATUS_INVALID_HANDLE 0xc0000008 +#define STATUS_NO_MEMORY 0xc0000017 +#define STATUS_ILLEGAL_INSTRUCTION 0xc000001d +#define STATUS_NONCONTINUABLE_EXCEPTION 0xc0000025 +#define STATUS_INVALID_DISPOSITION 0xc0000026 +#define STATUS_ARRAY_BOUNDS_EXCEEDED 0xc000008c +#define STATUS_FLOAT_DENORMAL_OPERAND 0xc000008d +#define STATUS_FLOAT_DIVIDE_BY_ZERO 0xc000008e +#define STATUS_FLOAT_INEXACT_RESULT 0xc000008f +#define STATUS_FLOAT_INVALID_OPERATION 0xc0000090 +#define STATUS_FLOAT_OVERFLOW 0xc0000091 +#define STATUS_FLOAT_STACK_CHECK 0xc0000092 +#define STATUS_FLOAT_UNDERFLOW 0xc0000093 +#define STATUS_INTEGER_DIVIDE_BY_ZERO 0xc0000094 +#define STATUS_INTEGER_OVERFLOW 0xc0000095 +#define STATUS_PRIVILEGED_INSTRUCTION 0xc0000096 +#define STATUS_STACK_OVERFLOW 0xc00000fd + +// Calling convension +#define __stdcall +#define __cdecl + +#endif // AVSCORE_POSIX_H +#endif // AVS_POSIX diff --git a/include/avisynth/avs/types.h b/include/avisynth/avs/types.h new file mode 100644 index 0000000..ba451c6 --- /dev/null +++ b/include/avisynth/avs/types.h @@ -0,0 +1,52 @@ +// Avisynth C Interface Version 0.20 +// Copyright 2003 Kevin Atkinson + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit +// http://www.gnu.org/copyleft/gpl.html . +// +// As a special exception, I give you permission to link to the +// Avisynth C interface with independent modules that communicate with +// the Avisynth C interface solely through the interfaces defined in +// avisynth_c.h, regardless of the license terms of these independent +// modules, and to copy and distribute the resulting combined work +// under terms of your choice, provided that every copy of the +// combined work is accompanied by a complete copy of the source code +// of the Avisynth C interface and Avisynth itself (with the version +// used to produce the combined work), being distributed under the +// terms of the GNU General Public License plus this exception. An +// independent module is a module which is not derived from or based +// on Avisynth C Interface, such as 3rd-party filters, import and +// export plugins, or graphical user interfaces. + +#ifndef AVS_TYPES_H +#define AVS_TYPES_H + +// Define all types necessary for interfacing with avisynth.dll +#include +#include +#ifdef __cplusplus + #include +#else + #include +#endif + +// Raster types used by VirtualDub & Avisynth +typedef uint32_t Pixel32; +typedef uint8_t BYTE; + +// Audio Sample information +typedef float SFLOAT; + +#endif //AVS_TYPES_H diff --git a/include/avisynth/avs/win.h b/include/avisynth/avs/win.h new file mode 100644 index 0000000..6692021 --- /dev/null +++ b/include/avisynth/avs/win.h @@ -0,0 +1,54 @@ +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit +// http://www.gnu.org/copyleft/gpl.html . +// +// Linking Avisynth statically or dynamically with other modules is making a +// combined work based on Avisynth. Thus, the terms and conditions of the GNU +// General Public License cover the whole combination. +// +// As a special exception, the copyright holders of Avisynth give you +// permission to link Avisynth with independent modules that communicate with +// Avisynth solely through the interfaces defined in avisynth.h, regardless of the license +// terms of these independent modules, and to copy and distribute the +// resulting combined work under terms of your choice, provided that +// every copy of the combined work is accompanied by a complete copy of +// the source code of Avisynth (the version of Avisynth used to produce the +// combined work), being distributed under the terms of the GNU General +// Public License plus this exception. An independent module is a module +// which is not derived from or based on Avisynth, such as 3rd-party filters, +// import and export plugins, or graphical user interfaces. + +#ifndef AVSCORE_WIN_H +#define AVSCORE_WIN_H + +// Whenever you need windows headers, start by including this file, then the rest. + +// WWUUT? We require XP now? +#if !defined(NTDDI_VERSION) && !defined(_WIN32_WINNT) + #define NTDDI_VERSION 0x05020000 + #define _WIN32_WINNT 0x0502 +#endif + +#define WIN32_LEAN_AND_MEAN +#define STRICT +#if !defined(NOMINMAX) + #define NOMINMAX +#endif + +#include + +// Provision for UTF-8 max 4 bytes per code point +#define AVS_MAX_PATH MAX_PATH*4 + +#endif // AVSCORE_WIN_H diff --git a/include/dualsynth/avs_wrapper.hpp b/include/dualsynth/avs_wrapper.hpp new file mode 100644 index 0000000..08aa0ea --- /dev/null +++ b/include/dualsynth/avs_wrapper.hpp @@ -0,0 +1,206 @@ +/* + * Copyright 2020 Xinyue Lu + * + * DualSynth wrapper - AviSynth+. + * + */ + +#pragma once + +namespace Plugin { + extern const char* Description; +} + +namespace AVSInterface +{ + struct AVSInDelegator final : InDelegator { + const AVSValue _args; + std::unordered_map _params_index_map; + int NameToIndex(const char* name) { + std::string name_string(name); + if (_params_index_map.find(name_string) == _params_index_map.end()) + throw "Unknown parameter during NameToIndex"; + return _params_index_map[name_string]; + } + void Read(const char* name, int& output) override { + output = _args[NameToIndex(name)].AsInt(output); + } + void Read(const char* name, int64_t& output) override { + output = _args[NameToIndex(name)].AsInt(static_cast(output)); + } + void Read(const char* name, float& output) override { + output = static_cast(_args[NameToIndex(name)].AsFloat(output)); + } + void Read(const char* name, double& output) override { + auto _default = output; + output = _args[NameToIndex(name)].AsFloat(NAN); + if (std::isnan(output)) + output = _default; + } + void Read(const char* name, bool& output) override { + output = _args[NameToIndex(name)].AsBool(output); + } + void Read(const char* name, void*& output) override { + PClip* clip = new PClip(_args[NameToIndex(name)].AsClip()); + output = (void *)(clip); + } + void Read(const char* name, std::vector& output) override { + auto arg = _args[NameToIndex(name)]; + if (!arg.IsArray()) + throw "Argument is not array"; + auto size = arg.ArraySize(); + output.clear(); + for (int i = 0; i < size; i++) + output.push_back(arg[i].AsInt()); + } + void Read(const char* name, std::vector& output) override { + auto arg = _args[NameToIndex(name)]; + if (!arg.IsArray()) + throw "Argument is not array"; + auto size = arg.ArraySize(); + output.clear(); + for (int i = 0; i < size; i++) + output.push_back(arg[i].AsInt()); + } + void Read(const char* name, std::vector& output) override { + auto arg = _args[NameToIndex(name)]; + if (!arg.IsArray()) + throw "Argument is not array"; + auto size = arg.ArraySize(); + output.clear(); + for (int i = 0; i < size; i++) + output.push_back(static_cast(arg[i].AsFloat())); + } + void Read(const char* name, std::vector& output) override { + auto arg = _args[NameToIndex(name)]; + if (!arg.IsArray()) + throw "Argument is not array"; + auto size = arg.ArraySize(); + output.clear(); + for (int i = 0; i < size; i++) + output.push_back(arg[i].AsFloat()); + } + void Read(const char* name, std::vector& output) override { + auto arg = _args[NameToIndex(name)]; + if (!arg.IsArray()) + throw "Argument is not array"; + auto size = arg.ArraySize(); + output.clear(); + for (int i = 0; i < size; i++) + output.push_back(arg[i].AsBool()); + } + void Free(void*& clip) override { + PClip* c = (PClip *)(clip); + delete c; + clip = nullptr; + } + + AVSInDelegator(const AVSValue args, std::vector params) : _args(args) + { + int idx = 0; + for (auto &¶m : params) + { + if (!param.AVSEnabled) continue; + _params_index_map[param.Name] = idx++; + } + } + }; + + struct AVSFetchFrameFunctor final : FetchFrameFunctor { + PClip _clip; + VideoInfo _vi; + IScriptEnvironment* _env; + AVSFetchFrameFunctor(PClip clip, VideoInfo vi, IScriptEnvironment * env) + : _clip(clip), _vi(vi), _env(env) {} + DSFrame operator()(int n) override { + auto frame = _clip->GetFrame(n, _env); + return DSFrame(frame, _vi, _env); + } + ~AVSFetchFrameFunctor() override {} + }; + + template + struct AVSWrapper : IClip + { + AVSValue _args; + IScriptEnvironment* _env; + FilterType data; + PClip clip; + VideoInfo vi; + AVSFetchFrameFunctor* functor {nullptr}; + + AVSWrapper(AVSValue args, IScriptEnvironment* env) + : _args(args), _env(env) {} + + void Initialize() + { + auto input_vi = DSVideoInfo(); + if (_args[0].IsClip()) { + clip = _args[0].AsClip(); + input_vi = DSVideoInfo(clip->GetVideoInfo()); + functor = new AVSFetchFrameFunctor(clip, clip->GetVideoInfo(), _env); + } + auto argument = AVSInDelegator(_args, data.Params()); + data.Initialize(&argument, input_vi, functor); + } + + PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment * env) override { + std::unordered_map in_frames; + if (functor) { + std::vector requests = data.RequestReferenceFrames(n); + for (auto &&i : requests) { + auto frame = clip->GetFrame(i, env); + in_frames[i] = DSFrame(frame, vi, env); + } + } + else + in_frames[n] = DSFrame(env); + + return data.GetFrame(n, in_frames).ToAVSFrame(); + } + + const VideoInfo& __stdcall GetVideoInfo() override { + auto output_vi = data.GetOutputVI(); + vi = output_vi.ToAVSVI(); + return vi; + } + + void __stdcall GetAudio(void* buf, int64_t start, int64_t count, IScriptEnvironment* env) override { if (clip) clip->GetAudio(buf, start, count, env); } + bool __stdcall GetParity(int n) override { return clip ? clip->GetParity(n) : false; } + int __stdcall SetCacheHints(int cachehints, int frame_range) override { return data.SetCacheHints(cachehints, frame_range); } + ~AVSWrapper() { + delete functor; + } + }; + + template + AVSValue __cdecl Create(AVSValue args, void* user_data, IScriptEnvironment* env) + { + auto filter = new AVSWrapper(args, env); + try { + filter->Initialize(); + } + catch (const char *err) { + env->ThrowError("%s: %s", filter->data.AVSName(), err); + } + return filter; + } + + template + void RegisterFilter(IScriptEnvironment* env) { + FilterType filter; + env->AddFunction(filter.AVSName(), filter.AVSParams().c_str(), Create, nullptr); + } +} + +const AVS_Linkage *AVS_linkage = NULL; + +extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit3(IScriptEnvironment* env, AVS_Linkage* linkage) +{ + AVS_linkage = linkage; + auto filters = RegisterAVSFilters(); + for (auto &&RegisterFilter : filters) { + RegisterFilter(env); + } + return Plugin::Description; +} diff --git a/include/dualsynth/ds_common.hpp b/include/dualsynth/ds_common.hpp new file mode 100644 index 0000000..7b02c51 --- /dev/null +++ b/include/dualsynth/ds_common.hpp @@ -0,0 +1,62 @@ +/* + * Copyright 2020 Xinyue Lu + * + * DualSynth wrapper - Common header+. + * + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ds_format.hpp" +#include "ds_videoinfo.hpp" +#include "ds_frame.hpp" + +typedef void (*register_vsfilter_proc)(VSRegisterFunction, VSPlugin*); +typedef void (*register_avsfilter_proc)(IScriptEnvironment* env); +std::vector RegisterVSFilters(); +std::vector RegisterAVSFilters(); + +enum ParamType +{ + Clip, Integer, Float, Boolean +}; + +struct Param +{ + const char* Name; + const ParamType Type; + const bool IsArray {false}; + bool AVSEnabled {true}; + bool VSEnabled {true}; + const bool IsOptional {true}; +}; + +struct InDelegator +{ + virtual void Read(const char* name, int& output) = 0; + virtual void Read(const char* name, int64_t& output) = 0; + virtual void Read(const char* name, float& output) = 0; + virtual void Read(const char* name, double& output) = 0; + virtual void Read(const char* name, bool& output) = 0; + virtual void Read(const char* name, std::vector& output) = 0; + virtual void Read(const char* name, std::vector& output) = 0; + virtual void Read(const char* name, std::vector& output) = 0; + virtual void Read(const char* name, std::vector& output) = 0; + virtual void Read(const char* name, std::vector& output) = 0; + virtual void Read(const char* name, void*& output) = 0; + virtual void Free(void*& clip) = 0; +}; + +struct FetchFrameFunctor +{ + virtual DSFrame operator()(int n) = 0; + virtual ~FetchFrameFunctor() {} +}; diff --git a/include/dualsynth/ds_filter.hpp b/include/dualsynth/ds_filter.hpp new file mode 100644 index 0000000..77f30c7 --- /dev/null +++ b/include/dualsynth/ds_filter.hpp @@ -0,0 +1,83 @@ +/* + * Copyright 2020 Xinyue Lu + * + * DualSynth wrapper - Filter parent class. + * + */ + +#pragma once + +struct Filter +{ + DSVideoInfo in_vi; + FetchFrameFunctor* fetch_frame; + virtual const char* VSName() const { return "FilterFoo"; } + virtual const char* AVSName() const { return "FilterFoo"; } + virtual const MtMode AVSMode() const { return MT_SERIALIZED; } + virtual const VSFilterMode VSMode() const { return fmSerial; } + virtual const std::vector Params() const = 0; + virtual const std::string VSParams() const + { + std::stringstream ss; + auto params = this->Params(); + for (auto &&p : params) + { + if (!p.VSEnabled) continue; + std::string type_name; + switch(p.Type) { + case Clip: type_name = "clip"; break; + case Integer: type_name = "int"; break; + case Float: type_name = "float"; break; + case Boolean: type_name = "int"; break; + } + ss << p.Name << ':' << type_name; + if (p.IsArray) + ss << "[]"; + if (p.IsOptional) + ss << ":opt"; + ss << ';'; + } + return ss.str(); + }; + virtual const std::string AVSParams() const + { + std::stringstream ss; + auto params = this->Params(); + for (auto &&p : params) + { + if (!p.AVSEnabled) continue; + char type_name; + switch(p.Type) { + case Clip: type_name = 'c'; break; + case Integer: type_name = 'i'; break; + case Float: type_name = 'f'; break; + case Boolean: type_name = 'b'; break; + } + if (p.IsOptional) + ss << '[' << p.Name << ']'; + ss << type_name; + } + return ss.str(); + }; + virtual void Initialize(InDelegator* in, DSVideoInfo in_vi, FetchFrameFunctor* fetch_frame) + { + this->in_vi = in_vi; + this->fetch_frame = fetch_frame; + }; + virtual std::vector RequestReferenceFrames(int n) const + { + return std::vector{n}; + } + virtual DSFrame GetFrame(int n, std::unordered_map in_frames) + { + return in_frames.size() > 0 ? in_frames.begin()->second : DSFrame(); + } + virtual DSVideoInfo GetOutputVI() + { + return in_vi; + } + virtual int SetCacheHints(int cachehints, int frame_range) + { + return cachehints == CACHE_GET_MTMODE ? AVSMode() : 0; + } +}; diff --git a/include/dualsynth/ds_format.hpp b/include/dualsynth/ds_format.hpp new file mode 100644 index 0000000..fb4c4a5 --- /dev/null +++ b/include/dualsynth/ds_format.hpp @@ -0,0 +1,100 @@ +/* + * Copyright 2020 Xinyue Lu + * + * DualSynth wrapper - DSFormat. + * + */ + +#pragma once + +struct DSFormat +{ + bool IsFamilyYUV {true}, IsFamilyRGB {false}, IsFamilyYCC {false}; + bool IsInteger {true}, IsFloat {false}; + int SSW {1}, SSH {1}; + int BitsPerSample {8}, BytesPerSample {1}; + int Planes {3}; + const VSCore* _vscore {nullptr}; + const VSAPI* _vsapi {nullptr}; + DSFormat() {} + DSFormat(const VSFormat* format, const VSCore* vscore, const VSAPI* vsapi) + : _vscore(vscore), _vsapi(vsapi) + { + Planes = format->numPlanes; + IsFamilyYUV = format->colorFamily == cmYUV || format->colorFamily == cmGray; + IsFamilyRGB = format->colorFamily == cmRGB; + IsFamilyYCC = format->colorFamily == cmYCoCg; + SSW = format->subSamplingW; + SSH = format->subSamplingH; + BitsPerSample = format->bitsPerSample; + BytesPerSample = format->bytesPerSample; + IsInteger = format->sampleType == stInteger; + IsFloat = format->sampleType == stFloat; + } + + const VSFormat* ToVSFormat() const + { + VSColorFamily family = cmYUV; + if (IsFamilyYUV) + family = Planes == 1 ? cmGray : cmYUV; + else if (IsFamilyRGB) + family = cmRGB; + else if (IsFamilyYCC) + family = cmYCoCg; + return _vsapi->registerFormat(family, IsInteger ? stInteger : stFloat, BitsPerSample, SSW, SSH, const_cast(_vscore)); + } + + DSFormat(int format) + { + const int componentBitSizes[8] = {8,16,32,0,0,10,12,14}; + if (format == VideoInfo::CS_I420) + format = VideoInfo::CS_YV12; + + IsFamilyYUV = format & VideoInfo::CS_YUV; + IsFamilyRGB = format & VideoInfo::CS_BGR; + IsFamilyYCC = false; + SSW = ((format >> VideoInfo::CS_Shift_Sub_Width) + 1) & 3; + SSH = ((format >> VideoInfo::CS_Shift_Sub_Height) + 1) & 3; + BitsPerSample = componentBitSizes[(format >> VideoInfo::CS_Shift_Sample_Bits) & 7]; + BytesPerSample = BitsPerSample == 8 ? 1 : BitsPerSample == 32 ? 4 : 2; + IsInteger = BitsPerSample < 32; + IsFloat = BitsPerSample == 32; + } + + int ToAVSFormat() const + { + int pixel_format = VideoInfo::CS_PLANAR | (Planes == 3 ? VideoInfo::CS_YUV : VideoInfo::CS_YUVA) | VideoInfo::CS_VPlaneFirst; + if (IsFamilyYUV) + pixel_format = VideoInfo::CS_PLANAR | (Planes == 3 ? VideoInfo::CS_YUV : VideoInfo::CS_YUVA) | VideoInfo::CS_VPlaneFirst; + else if (IsFamilyRGB) + pixel_format = VideoInfo::CS_PLANAR | VideoInfo::CS_BGR | (Planes == 3 ? VideoInfo::CS_BGR : VideoInfo::CS_RGB_TYPE) | VideoInfo::CS_VPlaneFirst; + else if (IsFamilyYCC) + pixel_format = VideoInfo::CS_PLANAR | VideoInfo::CS_BGR | (Planes == 3 ? VideoInfo::CS_BGR : VideoInfo::CS_RGBA_TYPE) | VideoInfo::CS_VPlaneFirst; + + switch(SSW) { + case 0: pixel_format |= VideoInfo::CS_Sub_Width_1; break; + case 1: pixel_format |= VideoInfo::CS_Sub_Width_2; break; + case 2: pixel_format |= VideoInfo::CS_Sub_Width_4; break; + } + + switch(SSH) { + case 0: pixel_format |= VideoInfo::CS_Sub_Height_1; break; + case 1: pixel_format |= VideoInfo::CS_Sub_Height_2; break; + case 2: pixel_format |= VideoInfo::CS_Sub_Height_4; break; + } + + if (Planes == 1 && IsFamilyYUV) + pixel_format = VideoInfo::CS_GENERIC_Y; + + switch(BitsPerSample) { + case 8: pixel_format |= VideoInfo::CS_Sample_Bits_8; break; + case 10: pixel_format |= VideoInfo::CS_Sample_Bits_10; break; + case 12: pixel_format |= VideoInfo::CS_Sample_Bits_12; break; + case 14: pixel_format |= VideoInfo::CS_Sample_Bits_14; break; + case 16: pixel_format |= VideoInfo::CS_Sample_Bits_16; break; + case 32: pixel_format |= VideoInfo::CS_Sample_Bits_32; break; + } + + return pixel_format; + } +}; diff --git a/include/dualsynth/ds_frame.hpp b/include/dualsynth/ds_frame.hpp new file mode 100644 index 0000000..84a760a --- /dev/null +++ b/include/dualsynth/ds_frame.hpp @@ -0,0 +1,238 @@ +/* + * Copyright 2020 Xinyue Lu + * + * DualSynth wrapper - DSFrame. + * + */ + +#pragma once + +struct DSFrame +{ + int FrameWidth {0}, FrameHeight {0}; + + const unsigned char ** SrcPointers {nullptr}; + int * StrideBytes {nullptr}; + unsigned char ** DstPointers {nullptr}; + DSFormat Format; + + // VapourSynth Interface + const VSFrameRef* _vssrc {nullptr}; + VSFrameRef* _vsdst {nullptr}; + const VSCore* _vscore {nullptr}; + const VSAPI* _vsapi {nullptr}; + const VSFormat* _vsformat {nullptr}; + + // AviSynth+ Interface + PVideoFrame _avssrc; + VideoInfo _vi; + IScriptEnvironment * _env {nullptr}; + int planes_y[4] = { PLANAR_Y, PLANAR_U, PLANAR_V, PLANAR_A }; + int planes_r[4] = { PLANAR_R, PLANAR_G, PLANAR_B, PLANAR_A }; + int *planes {0}; + + DSFrame() {} + DSFrame(const VSCore* vscore, const VSAPI* vsapi) + : _vscore(vscore), _vsapi(vsapi) {} + DSFrame(const VSFrameRef* src, const VSCore* vscore, const VSAPI* vsapi) + : _vssrc(src), _vscore(vscore), _vsapi(vsapi) + , _vsformat(src ? _vsapi->getFrameFormat(src) : nullptr) + { + if (_vssrc) { + Format = DSFormat(_vsformat, _vscore, _vsapi); + FrameWidth = _vsapi->getFrameWidth(src, 0); + FrameHeight = _vsapi->getFrameHeight(src, 0); + + SrcPointers = new const unsigned char*[Format.Planes]; + StrideBytes = new int[Format.Planes]; + for (int i = 0; i < Format.Planes; i++) { + SrcPointers[i] = _vsapi->getReadPtr(src, i); + StrideBytes[i] = _vsapi->getStride(src, i); + } + } + } + + DSFrame(IScriptEnvironment * env) + : _env(env) {} + DSFrame(PVideoFrame &src, VideoInfo vi, IScriptEnvironment * env) + : _avssrc(src), _vi(vi), _env(env) + { + if (_avssrc) { + Format = DSFormat(_vi.pixel_type); + planes = Format.IsFamilyYUV ? planes_y : planes_r; + FrameWidth = _vi.width; + FrameHeight = _vi.height; + + SrcPointers = new const unsigned char*[Format.Planes]; + StrideBytes = new int[Format.Planes]; + for (int i = 0; i < Format.Planes; i++) { + SrcPointers[i] = src->GetReadPtr(planes[i]); + StrideBytes[i] = src->GetPitch(planes[i]); + } + } + } + + DSFrame Create() { return Create(false, false); } + DSFrame Create(bool copy) { return Create(copy, false); } + DSFrame Create(bool copy, bool inplace) + { + if (_vssrc) { + // Create a new VS frame + const VSFrameRef* copy_frames[1] {ToVSFrame()}; + int copy_planes[4] = {0}; + auto vsframe = copy ? + _vsapi->newVideoFrame2(_vsformat, FrameWidth, FrameHeight, copy_frames, copy_planes, NULL, const_cast(_vscore)) : + _vsapi->newVideoFrame(_vsformat, FrameWidth, FrameHeight, NULL, const_cast(_vscore)); + _vsapi->freeFrame(copy_frames[0]); + + DSFrame new_frame(vsframe, _vscore, _vsapi); + new_frame._vsdst = vsframe; + new_frame.DstPointers = new unsigned char*[Format.Planes]; + for (int i = 0; i < Format.Planes; i++) + new_frame.DstPointers[i] = _vsapi->getWritePtr(vsframe, i); + return new_frame; + } + else if(_avssrc) { + // Create a new AVS frame + return Create(_vi); + } + throw "Unable to create from nothing."; + } + DSFrame Create(DSVideoInfo vi) { + if (_vsapi) { + auto vsframe = _vsapi->newVideoFrame(vi.Format.ToVSFormat(), vi.Width, vi.Height, NULL, const_cast(_vscore)); + DSFrame new_frame(vsframe, _vscore, _vsapi); + new_frame._vsdst = vsframe; + new_frame.DstPointers = new unsigned char*[Format.Planes]; + for (int i = 0; i < Format.Planes; i++) + new_frame.DstPointers[i] = _vsapi->getWritePtr(vsframe, i); + return new_frame; + } + else if (_env) { + auto avsvi = vi.ToAVSVI(); + auto new_avsframe = _env->NewVideoFrame(avsvi); + auto dstp = new unsigned char*[Format.Planes]; + for (int i = 0; i < Format.Planes; i++) + dstp[i] = new_avsframe->GetWritePtr(planes[i]); + DSFrame new_frame(new_avsframe, avsvi, _env); + new_frame.DstPointers = dstp; + return new_frame; + } + throw "Unable to create from nothing."; + } + + const VSFrameRef* ToVSFrame() + { + return _vsdst ? _vsapi->cloneFrameRef(_vsdst) : + _vssrc ? _vsapi->cloneFrameRef(_vssrc) : + nullptr; + } + PVideoFrame ToAVSFrame() {return _avssrc ? _avssrc : nullptr;} + + ~DSFrame() + { + if (SrcPointers) + delete[] SrcPointers; + if (DstPointers) + delete[] DstPointers; + if (StrideBytes) + delete[] StrideBytes; + if (_vsdst && _vsdst != _vssrc) + _vsapi->freeFrame(_vsdst); + if (_vssrc) + _vsapi->freeFrame(_vssrc); + } + + DSFrame(const DSFrame & old) + { + _avssrc = old._avssrc; + std::memcpy(this, &old, sizeof(DSFrame)); + if (old.SrcPointers) { + SrcPointers = new const unsigned char*[Format.Planes]; + std::copy_n(old.SrcPointers, Format.Planes, SrcPointers); + } + if (old.DstPointers) { + DstPointers = new unsigned char*[Format.Planes]; + std::copy_n(old.DstPointers, Format.Planes, DstPointers); + } + if (old.StrideBytes) { + StrideBytes = new int[Format.Planes]; + std::copy_n(old.StrideBytes, Format.Planes, StrideBytes); + } + if (_vsdst && _vsdst != _vssrc) + _vsdst = const_cast(_vsapi->cloneFrameRef(old._vsdst)); + if (_vssrc) + _vssrc = _vsapi->cloneFrameRef(old._vssrc); + } + DSFrame& operator =(const DSFrame & old) + { + if (&old == this) + return *this; + + if (SrcPointers) + delete[] SrcPointers; + if (DstPointers) + delete[] DstPointers; + if (StrideBytes) + delete[] StrideBytes; + if (_vsdst && _vsdst != _vssrc) + _vsapi->freeFrame(_vsdst); + if (_vssrc) + _vsapi->freeFrame(_vssrc); + + _avssrc = old._avssrc; + std::memcpy(this, &old, sizeof(DSFrame)); + if (old.SrcPointers) { + SrcPointers = new const unsigned char*[Format.Planes]; + std::copy_n(old.SrcPointers, Format.Planes, SrcPointers); + } + if (old.DstPointers) { + DstPointers = new unsigned char*[Format.Planes]; + std::copy_n(old.DstPointers, Format.Planes, DstPointers); + } + if (old.StrideBytes) { + StrideBytes = new int[Format.Planes]; + std::copy_n(old.StrideBytes, Format.Planes, StrideBytes); + } + if (_vsdst && _vsdst != _vssrc) + _vsdst = const_cast(_vsapi->cloneFrameRef(old._vsdst)); + if (_vssrc) + _vssrc = _vsapi->cloneFrameRef(old._vssrc); + return *this; + } + DSFrame(DSFrame && old) noexcept + { + _avssrc = old._avssrc; + std::memcpy(this, &old, sizeof(DSFrame)); + old.SrcPointers = nullptr; + old.DstPointers = nullptr; + old.StrideBytes = nullptr; + old._vssrc = nullptr; + old._vsdst = nullptr; + } + DSFrame& operator =(DSFrame && old) noexcept + { + if (&old == this) + return *this; + + if (SrcPointers) + delete[] SrcPointers; + if (DstPointers) + delete[] DstPointers; + if (StrideBytes) + delete[] StrideBytes; + if (_vsdst && _vsdst != _vssrc) + _vsapi->freeFrame(_vsdst); + if (_vssrc) + _vsapi->freeFrame(_vssrc); + + _avssrc = old._avssrc; + std::memcpy(this, &old, sizeof(DSFrame)); + old.SrcPointers = nullptr; + old.DstPointers = nullptr; + old.StrideBytes = nullptr; + old._vssrc = nullptr; + old._vsdst = nullptr; + return *this; + } +}; diff --git a/include/dualsynth/ds_videoinfo.hpp b/include/dualsynth/ds_videoinfo.hpp new file mode 100644 index 0000000..dccf52c --- /dev/null +++ b/include/dualsynth/ds_videoinfo.hpp @@ -0,0 +1,36 @@ +/* + * Copyright 2020 Xinyue Lu + * + * DualSynth wrapper - DSVideoInfo. + * + */ + +#pragma once + +struct DSVideoInfo +{ + DSFormat Format; + int64_t FPSNum {1}, FPSDenom {1}; + int Width {0}, Height {0}; + int Frames {0}; + + DSVideoInfo() {} + DSVideoInfo(const VSVideoInfo* vsvi, const VSCore* vscore, const VSAPI* vsapi) + : Format(vsvi->format, vscore, vsapi) + , FPSNum(vsvi->fpsNum), FPSDenom(vsvi->fpsDen) + , Width(vsvi->width), Height(vsvi->height) + , Frames(vsvi->numFrames) + { } + DSVideoInfo(const VideoInfo avsvi) + : Format(avsvi.pixel_type) + , FPSNum(avsvi.fps_numerator), FPSDenom(avsvi.fps_denominator) + , Width(avsvi.width), Height(avsvi.height) + , Frames(avsvi.num_frames) + {} + const VSVideoInfo* ToVSVI() { + return new VSVideoInfo {Format.ToVSFormat(), FPSNum, FPSDenom, Width, Height, Frames, 0}; + } + const VideoInfo ToAVSVI() { + return VideoInfo{Width, Height, static_cast(FPSNum), static_cast(FPSDenom), Frames, Format.ToAVSFormat()}; + } +}; diff --git a/include/dualsynth/vs_wrapper.hpp b/include/dualsynth/vs_wrapper.hpp new file mode 100644 index 0000000..e22033d --- /dev/null +++ b/include/dualsynth/vs_wrapper.hpp @@ -0,0 +1,195 @@ +/* + * Copyright 2020 Xinyue Lu + * + * DualSynth wrapper - VapourSynth. + * + */ + +#pragma once + +namespace Plugin { + extern const char* Identifier; + extern const char* Namespace; + extern const char* Description; +} + +namespace VSInterface { + const VSAPI * API; + + struct VSInDelegator final : InDelegator { + const VSMap *_in; + const VSAPI *_vsapi; + int _err; + void Read(const char* name, int& output) override { + auto _default = output; + output = static_cast(_vsapi->propGetInt(_in, name, 0, &_err)); + if (_err) output = _default; + } + void Read(const char* name, int64_t& output) override { + auto _default = output; + output = _vsapi->propGetInt(_in, name, 0, &_err); + if (_err) output = _default; + } + void Read(const char* name, float& output) override { + auto _default = output; + output = static_cast(_vsapi->propGetFloat(_in, name, 0, &_err)); + if (_err) output = _default; + } + void Read(const char* name, double& output) override { + auto _default = output; + output = _vsapi->propGetFloat(_in, name, 0, &_err); + if (_err) output = _default; + } + void Read(const char* name, bool& output) override { + auto output_int = _vsapi->propGetInt(_in, name, 0, &_err); + if (!_err) output = output_int != 0; + } + void Read(const char* name, std::vector& output) override { + auto size = _vsapi->propNumElements(_in, name); + if (size < 0) return; + output.clear(); + for (int i = 0; i < size; i++) + output.push_back(static_cast(_vsapi->propGetInt(_in, name, i, &_err))); + } + void Read(const char* name, std::vector& output) override { + auto size = _vsapi->propNumElements(_in, name); + if (size < 0) return; + output.clear(); + for (int i = 0; i < size; i++) + output.push_back(_vsapi->propGetInt(_in, name, i, &_err)); + } + void Read(const char* name, std::vector& output) override { + auto size = _vsapi->propNumElements(_in, name); + if (size < 0) return; + output.clear(); + for (int i = 0; i < size; i++) + output.push_back(static_cast(_vsapi->propGetFloat(_in, name, i, &_err))); + } + void Read(const char* name, std::vector& output) override { + auto size = _vsapi->propNumElements(_in, name); + if (size < 0) return; + output.clear(); + for (int i = 0; i < size; i++) + output.push_back(_vsapi->propGetFloat(_in, name, i, &_err)); + } + void Read(const char* name, std::vector& output) override { + auto size = _vsapi->propNumElements(_in, name); + if (size < 0) return; + output.clear(); + for (int i = 0; i < size; i++) + output.push_back(_vsapi->propGetInt(_in, name, i, &_err)); + } + void Read(const char* name, void*& output) override { + output = reinterpret_cast(_vsapi->propGetNode(_in, name, 0, &_err)); + } + void Free(void*& clip) override { + _vsapi->freeNode(reinterpret_cast(clip)); + clip = nullptr; + } + VSInDelegator(const VSMap *in, const VSAPI *vsapi) : _in(in), _vsapi(vsapi) {} + }; + + struct VSFetchFrameFunctor final : FetchFrameFunctor { + VSNodeRef *_vs_clip; + VSCore *_core; + const VSAPI *_vsapi; + VSFrameContext *_frameCtx; + VSFetchFrameFunctor(VSNodeRef *clip, VSCore *core, const VSAPI *vsapi) + : _vs_clip(clip), _core(core), _vsapi(vsapi) {} + DSFrame operator()(int n) override { + return DSFrame(_vsapi->getFrameFilter(n, _vs_clip, _frameCtx), _core, _vsapi); + } + ~VSFetchFrameFunctor() override { + _vsapi->freeNode(_vs_clip); + } + }; + + template + void VS_CC Initialize(VSMap *in, VSMap *out, void **instanceData, VSNode *node, VSCore *core, const VSAPI *vsapi) { + auto Data = reinterpret_cast(*instanceData); + auto output_vi = Data->GetOutputVI(); + vsapi->setVideoInfo(output_vi.ToVSVI(), 1, node); + } + + template + void VS_CC Delete(void *instanceData, VSCore *core, const VSAPI *vsapi) { + auto filter = reinterpret_cast(instanceData); + auto functor = reinterpret_cast(filter->fetch_frame); + delete functor; + delete filter; + } + + template + const VSFrameRef* VS_CC GetFrame(int n, int activationReason, void **instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi) { + auto filter = reinterpret_cast(*instanceData); + auto functor = reinterpret_cast(filter->fetch_frame); + if (functor) + functor->_frameCtx = frameCtx; + + std::vector ref_frames; + if (activationReason == VSActivationReason::arInitial) { + if (functor) { + ref_frames = filter->RequestReferenceFrames(n); + for (auto &&i : ref_frames) + vsapi->requestFrameFilter(i, functor->_vs_clip, frameCtx); + } + } + else if (activationReason == VSActivationReason::arAllFramesReady) { + std::unordered_map in_frames; + if (functor) { + ref_frames = filter->RequestReferenceFrames(n); + for (auto &&i : ref_frames) + in_frames[i] = DSFrame(vsapi->getFrameFilter(i, functor->_vs_clip, frameCtx), core, vsapi); + } + else + in_frames[n] = DSFrame(core, vsapi); + + auto vs_frame = (filter->GetFrame(n, in_frames).ToVSFrame()); + return vs_frame; + } + return nullptr; + } + + template + void VS_CC Create(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi) { + auto filter = new FilterType{}; + auto argument = VSInDelegator(in, vsapi); + try { + void* clip = nullptr; + argument.Read("clip", clip); + DSVideoInfo input_vi; + VSFetchFrameFunctor* functor = nullptr; + if (clip) { + auto vs_clip = reinterpret_cast(clip); + functor = new VSFetchFrameFunctor(vs_clip, core, vsapi); + input_vi = DSVideoInfo(vsapi->getVideoInfo(vs_clip), core, vsapi); + } + filter->Initialize(&argument, input_vi, functor); + vsapi->createFilter(in, out, filter->VSName(), Initialize, GetFrame, Delete, filter->VSMode(), 0, filter, core); + } + catch(const char *err){ + char msg_buff[256]; + snprintf(msg_buff, 256, "%s: %s", filter->VSName(), err); + vsapi->setError(out, msg_buff); + delete filter; + } + } + + template + void RegisterFilter(VSRegisterFunction registerFunc, VSPlugin* vsplugin) { + FilterType filter; + registerFunc(filter.VSName(), filter.VSParams().c_str(), Create, nullptr, vsplugin); + } + + void RegisterPlugin(VSConfigPlugin configFunc, VSPlugin* vsplugin) { + configFunc(Plugin::Identifier, Plugin::Namespace, Plugin::Description, VAPOURSYNTH_API_VERSION, 1, vsplugin); + } +} + +VS_EXTERNAL_API(void) VapourSynthPluginInit(VSConfigPlugin configFunc, VSRegisterFunction registerFunc, VSPlugin* vsplugin) { + VSInterface::RegisterPlugin(configFunc, vsplugin); + auto filters = RegisterVSFilters(); + for (auto &&RegisterFilter : filters) { + RegisterFilter(registerFunc, vsplugin); + } +} diff --git a/include/vapoursynth/VSHelper.h b/include/vapoursynth/VSHelper.h new file mode 100644 index 0000000..3d60441 --- /dev/null +++ b/include/vapoursynth/VSHelper.h @@ -0,0 +1,174 @@ +/***************************************************************************** +* Copyright (c) 2012-2015 Fredrik Mellbin +* --- Legal stuff --- +* This program is free software. It comes without any warranty, to +* the extent permitted by applicable law. You can redistribute it +* and/or modify it under the terms of the Do What The Fuck You Want +* To Public License, Version 2, as published by Sam Hocevar. See +* http://sam.zoy.org/wtfpl/COPYING for more details. +*****************************************************************************/ + +#ifndef VSHELPER_H +#define VSHELPER_H + +#include +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#endif +#include "VapourSynth.h" + +/* Visual Studio doesn't recognize inline in c mode */ +#if defined(_MSC_VER) && !defined(__cplusplus) +#define inline _inline +#endif + +/* A kinda portable definition of the C99 restrict keyword (or its inofficial C++ equivalent) */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* Available in C99 */ +#define VS_RESTRICT restrict +#elif defined(__cplusplus) || defined(_MSC_VER) /* Almost all relevant C++ compilers support it so just assume it works */ +#define VS_RESTRICT __restrict +#else /* Not supported */ +#define VS_RESTRICT +#endif + +#ifdef _WIN32 +#define VS_ALIGNED_MALLOC(pptr, size, alignment) do { *(pptr) = _aligned_malloc((size), (alignment)); } while (0) +#define VS_ALIGNED_FREE(ptr) do { _aligned_free((ptr)); } while (0) +#else +#define VS_ALIGNED_MALLOC(pptr, size, alignment) do { if(posix_memalign((void**)(pptr), (alignment), (size))) *((void**)pptr) = NULL; } while (0) +#define VS_ALIGNED_FREE(ptr) do { free((ptr)); } while (0) +#endif + +#define VSMAX(a,b) ((a) > (b) ? (a) : (b)) +#define VSMIN(a,b) ((a) > (b) ? (b) : (a)) + +#ifdef __cplusplus +/* A nicer templated malloc for all the C++ users out there */ +#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900) +template +#else +template +#endif +static inline T* vs_aligned_malloc(size_t size, size_t alignment) { +#ifdef _WIN32 + return (T*)_aligned_malloc(size, alignment); +#else + void *tmp = NULL; + if (posix_memalign(&tmp, alignment, size)) + tmp = 0; + return (T*)tmp; +#endif +} + +static inline void vs_aligned_free(void *ptr) { + VS_ALIGNED_FREE(ptr); +} +#endif /* __cplusplus */ + +/* convenience function for checking if the format never changes between frames */ +static inline int isConstantFormat(const VSVideoInfo *vi) { + return vi->height > 0 && vi->width > 0 && vi->format; +} + +/* convenience function to check for if two clips have the same format (unknown/changeable will be considered the same too) */ +static inline int isSameFormat(const VSVideoInfo *v1, const VSVideoInfo *v2) { + return v1->height == v2->height && v1->width == v2->width && v1->format == v2->format; +} + +/* multiplies and divides a rational number, such as a frame duration, in place and reduces the result */ +static inline void muldivRational(int64_t *num, int64_t *den, int64_t mul, int64_t div) { + /* do nothing if the rational number is invalid */ + if (!*den) + return; + + /* nobody wants to accidentally divide by zero */ + assert(div); + + int64_t a, b; + *num *= mul; + *den *= div; + a = *num; + b = *den; + while (b != 0) { + int64_t t = a; + a = b; + b = t % b; + } + if (a < 0) + a = -a; + *num /= a; + *den /= a; +} + +/* reduces a rational number */ +static inline void vs_normalizeRational(int64_t *num, int64_t *den) { + muldivRational(num, den, 1, 1); +} + +/* add two rational numbers and reduces the result */ +static inline void vs_addRational(int64_t *num, int64_t *den, int64_t addnum, int64_t addden) { + /* do nothing if the rational number is invalid */ + if (!*den) + return; + + /* nobody wants to accidentally add an invalid rational number */ + assert(addden); + + if (*den == addden) { + *num += addnum; + } else { + int64_t temp = addden; + addnum *= *den; + addden *= *den; + *num *= temp; + *den *= temp; + + *num += addnum; + + vs_normalizeRational(num, den); + } +} + +/* converts an int64 to int with saturation, useful to silence warnings when reading int properties among other things */ +static inline int int64ToIntS(int64_t i) { + if (i > INT_MAX) + return INT_MAX; + else if (i < INT_MIN) + return INT_MIN; + else return (int)i; +} + +static inline void vs_bitblt(void *dstp, int dst_stride, const void *srcp, int src_stride, size_t row_size, size_t height) { + if (height) { + if (src_stride == dst_stride && src_stride == (int)row_size) { + memcpy(dstp, srcp, row_size * height); + } else { + const uint8_t *srcp8 = (const uint8_t *)srcp; + uint8_t *dstp8 = (uint8_t *)dstp; + size_t i; + for (i = 0; i < height; i++) { + memcpy(dstp8, srcp8, row_size); + srcp8 += src_stride; + dstp8 += dst_stride; + } + } + } +} + +/* check if the frame dimensions are valid for a given format */ +/* returns non-zero for valid width and height */ +static inline int areValidDimensions(const VSFormat *fi, int width, int height) { + return !(width % (1 << fi->subSamplingW) || height % (1 << fi->subSamplingH)); +} + +/* Visual Studio doesn't recognize inline in c mode */ +#if defined(_MSC_VER) && !defined(__cplusplus) +#undef inline +#endif + +#endif diff --git a/include/vapoursynth/VSScript.h b/include/vapoursynth/VSScript.h new file mode 100644 index 0000000..b63b343 --- /dev/null +++ b/include/vapoursynth/VSScript.h @@ -0,0 +1,84 @@ +/* +* Copyright (c) 2013-2018 Fredrik Mellbin +* +* This file is part of VapourSynth. +* +* VapourSynth is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* VapourSynth is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with VapourSynth; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef VSSCRIPT_H +#define VSSCRIPT_H + +#include "VapourSynth.h" + +#define VSSCRIPT_API_MAJOR 3 +#define VSSCRIPT_API_MINOR 2 +#define VSSCRIPT_API_VERSION ((VSSCRIPT_API_MAJOR << 16) | (VSSCRIPT_API_MINOR)) + +/* As of api 3.2 all functions are threadsafe */ + +typedef struct VSScript VSScript; + +typedef enum VSEvalFlags { + efSetWorkingDir = 1, +} VSEvalFlags; + +/* Get the api version */ +VS_API(int) vsscript_getApiVersion(void); /* api 3.1 */ + +/* Initialize the available scripting runtimes, returns zero on failure */ +VS_API(int) vsscript_init(void); + +/* Free all scripting runtimes */ +VS_API(int) vsscript_finalize(void); + +/* +* Pass a pointer to a null handle to create a new one +* The values returned by the query functions are only valid during the lifetime of the VSScript +* scriptFilename is if the error message should reference a certain file, NULL allowed in vsscript_evaluateScript() +* core is to pass in an already created instance so that mixed environments can be used, +* NULL creates a new core that can be fetched with vsscript_getCore() later OR implicitly uses the one associated with an already existing handle when passed +* If efSetWorkingDir is passed to flags the current working directory will be changed to the path of the script +* note that if scriptFilename is NULL in vsscript_evaluateScript() then __file__ won't be set and the working directory won't be changed +* Set efSetWorkingDir to get the default and recommended behavior +*/ +VS_API(int) vsscript_evaluateScript(VSScript **handle, const char *script, const char *scriptFilename, int flags); +/* Convenience version of the above function that loads the script from a file */ +VS_API(int) vsscript_evaluateFile(VSScript **handle, const char *scriptFilename, int flags); +/* Create an empty environment for use in later invocations, mostly useful to set script variables before execution */ +VS_API(int) vsscript_createScript(VSScript **handle); + +VS_API(void) vsscript_freeScript(VSScript *handle); +VS_API(const char *) vsscript_getError(VSScript *handle); +/* The node returned must be freed using freeNode() before calling vsscript_freeScript() */ +VS_API(VSNodeRef *) vsscript_getOutput(VSScript *handle, int index); +/* Both nodes returned must be freed using freeNode() before calling vsscript_freeScript(), the alpha node pointer will only be set if an alpha clip has been set in the script */ +VS_API(VSNodeRef *) vsscript_getOutput2(VSScript *handle, int index, VSNodeRef **alpha); /* api 3.1 */ +/* Unset an output index */ +VS_API(int) vsscript_clearOutput(VSScript *handle, int index); +/* The core is valid as long as the environment exists */ +VS_API(VSCore *) vsscript_getCore(VSScript *handle); +/* Convenience function for retrieving a vsapi pointer */ +VS_API(const VSAPI *) vsscript_getVSApi(void); /* deprecated as of api 3.2 since it's impossible to tell the api version supported */ +VS_API(const VSAPI *) vsscript_getVSApi2(int version); /* api 3.2, generally you should pass VAPOURSYNTH_API_VERSION */ + +/* Variables names that are not set or not of a convertible type will return an error */ +VS_API(int) vsscript_getVariable(VSScript *handle, const char *name, VSMap *dst); +VS_API(int) vsscript_setVariable(VSScript *handle, const VSMap *vars); +VS_API(int) vsscript_clearVariable(VSScript *handle, const char *name); +/* Tries to clear everything set in an environment, normally it is better to simply free an environment completely and create a new one */ +VS_API(void) vsscript_clearEnvironment(VSScript *handle); + +#endif /* VSSCRIPT_H */ diff --git a/include/vapoursynth/VapourSynth.h b/include/vapoursynth/VapourSynth.h new file mode 100644 index 0000000..e03dc61 --- /dev/null +++ b/include/vapoursynth/VapourSynth.h @@ -0,0 +1,348 @@ +/* +* Copyright (c) 2012-2017 Fredrik Mellbin +* +* This file is part of VapourSynth. +* +* VapourSynth is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* VapourSynth is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with VapourSynth; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef VAPOURSYNTH_H +#define VAPOURSYNTH_H + +#include + +#define VAPOURSYNTH_API_MAJOR 3 +#define VAPOURSYNTH_API_MINOR 6 +#define VAPOURSYNTH_API_VERSION ((VAPOURSYNTH_API_MAJOR << 16) | (VAPOURSYNTH_API_MINOR)) + +/* Convenience for C++ users. */ +#ifdef __cplusplus +# define VS_EXTERN_C extern "C" +# if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900) +# define VS_NOEXCEPT noexcept +# else +# define VS_NOEXCEPT +# endif +#else +# define VS_EXTERN_C +# define VS_NOEXCEPT +#endif + +#if defined(_WIN32) && !defined(_WIN64) +# define VS_CC __stdcall +#else +# define VS_CC +#endif + +/* And now for some symbol hide-and-seek... */ +#if defined(_WIN32) /* Windows being special */ +# define VS_EXTERNAL_API(ret) VS_EXTERN_C __declspec(dllexport) ret VS_CC +#elif defined(__GNUC__) && __GNUC__ >= 4 +# define VS_EXTERNAL_API(ret) VS_EXTERN_C __attribute__((visibility("default"))) ret VS_CC +#else +# define VS_EXTERNAL_API(ret) VS_EXTERN_C ret VS_CC +#endif + +#if !defined(VS_CORE_EXPORTS) && defined(_WIN32) +# define VS_API(ret) VS_EXTERN_C __declspec(dllimport) ret VS_CC +#else +# define VS_API(ret) VS_EXTERNAL_API(ret) +#endif + +typedef struct VSFrameRef VSFrameRef; +typedef struct VSNodeRef VSNodeRef; +typedef struct VSCore VSCore; +typedef struct VSPlugin VSPlugin; +typedef struct VSNode VSNode; +typedef struct VSFuncRef VSFuncRef; +typedef struct VSMap VSMap; +typedef struct VSAPI VSAPI; +typedef struct VSFrameContext VSFrameContext; + +typedef enum VSColorFamily { + /* all planar formats */ + cmGray = 1000000, + cmRGB = 2000000, + cmYUV = 3000000, + cmYCoCg = 4000000, + /* special for compatibility */ + cmCompat = 9000000 +} VSColorFamily; + +typedef enum VSSampleType { + stInteger = 0, + stFloat = 1 +} VSSampleType; + +/* The +10 is so people won't be using the constants interchangably "by accident" */ +typedef enum VSPresetFormat { + pfNone = 0, + + pfGray8 = cmGray + 10, + pfGray16, + + pfGrayH, + pfGrayS, + + pfYUV420P8 = cmYUV + 10, + pfYUV422P8, + pfYUV444P8, + pfYUV410P8, + pfYUV411P8, + pfYUV440P8, + + pfYUV420P9, + pfYUV422P9, + pfYUV444P9, + + pfYUV420P10, + pfYUV422P10, + pfYUV444P10, + + pfYUV420P16, + pfYUV422P16, + pfYUV444P16, + + pfYUV444PH, + pfYUV444PS, + + pfYUV420P12, + pfYUV422P12, + pfYUV444P12, + + pfYUV420P14, + pfYUV422P14, + pfYUV444P14, + + pfRGB24 = cmRGB + 10, + pfRGB27, + pfRGB30, + pfRGB48, + + pfRGBH, + pfRGBS, + + /* special for compatibility, if you implement these in any filter I'll personally kill you */ + /* I'll also change their ids around to break your stuff regularly */ + pfCompatBGR32 = cmCompat + 10, + pfCompatYUY2 +} VSPresetFormat; + +typedef enum VSFilterMode { + fmParallel = 100, /* completely parallel execution */ + fmParallelRequests = 200, /* for filters that are serial in nature but can request one or more frames they need in advance */ + fmUnordered = 300, /* for filters that modify their internal state every request */ + fmSerial = 400 /* for source filters and compatibility with other filtering architectures */ +} VSFilterMode; + +typedef struct VSFormat { + char name[32]; + int id; + int colorFamily; /* see VSColorFamily */ + int sampleType; /* see VSSampleType */ + int bitsPerSample; /* number of significant bits */ + int bytesPerSample; /* actual storage is always in a power of 2 and the smallest possible that can fit the number of bits used per sample */ + + int subSamplingW; /* log2 subsampling factor, applied to second and third plane */ + int subSamplingH; + + int numPlanes; /* implicit from colorFamily */ +} VSFormat; + +typedef enum VSNodeFlags { + nfNoCache = 1, + nfIsCache = 2, + nfMakeLinear = 4 /* api 3.3 */ +} VSNodeFlags; + +typedef enum VSPropTypes { + ptUnset = 'u', + ptInt = 'i', + ptFloat = 'f', + ptData = 's', + ptNode = 'c', + ptFrame = 'v', + ptFunction = 'm' +} VSPropTypes; + +typedef enum VSGetPropErrors { + peUnset = 1, + peType = 2, + peIndex = 4 +} VSGetPropErrors; + +typedef enum VSPropAppendMode { + paReplace = 0, + paAppend = 1, + paTouch = 2 +} VSPropAppendMode; + +typedef struct VSCoreInfo { + const char *versionString; + int core; + int api; + int numThreads; + int64_t maxFramebufferSize; + int64_t usedFramebufferSize; +} VSCoreInfo; + +typedef struct VSVideoInfo { + const VSFormat *format; + int64_t fpsNum; + int64_t fpsDen; + int width; + int height; + int numFrames; /* api 3.2 - no longer allowed to be 0 */ + int flags; +} VSVideoInfo; + +typedef enum VSActivationReason { + arInitial = 0, + arFrameReady = 1, + arAllFramesReady = 2, + arError = -1 +} VSActivationReason; + +typedef enum VSMessageType { + mtDebug = 0, + mtWarning = 1, + mtCritical = 2, + mtFatal = 3 +} VSMessageType; + +/* core entry point */ +typedef const VSAPI *(VS_CC *VSGetVapourSynthAPI)(int version); + +/* plugin function and filter typedefs */ +typedef void (VS_CC *VSPublicFunction)(const VSMap *in, VSMap *out, void *userData, VSCore *core, const VSAPI *vsapi); +typedef void (VS_CC *VSRegisterFunction)(const char *name, const char *args, VSPublicFunction argsFunc, void *functionData, VSPlugin *plugin); +typedef void (VS_CC *VSConfigPlugin)(const char *identifier, const char *defaultNamespace, const char *name, int apiVersion, int readonly, VSPlugin *plugin); +typedef void (VS_CC *VSInitPlugin)(VSConfigPlugin configFunc, VSRegisterFunction registerFunc, VSPlugin *plugin); +typedef void (VS_CC *VSFreeFuncData)(void *userData); +typedef void (VS_CC *VSFilterInit)(VSMap *in, VSMap *out, void **instanceData, VSNode *node, VSCore *core, const VSAPI *vsapi); +typedef const VSFrameRef *(VS_CC *VSFilterGetFrame)(int n, int activationReason, void **instanceData, void **frameData, VSFrameContext *frameCtx, VSCore *core, const VSAPI *vsapi); +typedef void (VS_CC *VSFilterFree)(void *instanceData, VSCore *core, const VSAPI *vsapi); + +/* other */ +typedef void (VS_CC *VSFrameDoneCallback)(void *userData, const VSFrameRef *f, int n, VSNodeRef *, const char *errorMsg); +typedef void (VS_CC *VSMessageHandler)(int msgType, const char *msg, void *userData); +typedef void (VS_CC *VSMessageHandlerFree)(void *userData); + +struct VSAPI { + VSCore *(VS_CC *createCore)(int threads) VS_NOEXCEPT; + void (VS_CC *freeCore)(VSCore *core) VS_NOEXCEPT; + const VSCoreInfo *(VS_CC *getCoreInfo)(VSCore *core) VS_NOEXCEPT; /* deprecated as of api 3.6, use getCoreInfo2 instead */ + + const VSFrameRef *(VS_CC *cloneFrameRef)(const VSFrameRef *f) VS_NOEXCEPT; + VSNodeRef *(VS_CC *cloneNodeRef)(VSNodeRef *node) VS_NOEXCEPT; + VSFuncRef *(VS_CC *cloneFuncRef)(VSFuncRef *f) VS_NOEXCEPT; + + void (VS_CC *freeFrame)(const VSFrameRef *f) VS_NOEXCEPT; + void (VS_CC *freeNode)(VSNodeRef *node) VS_NOEXCEPT; + void (VS_CC *freeFunc)(VSFuncRef *f) VS_NOEXCEPT; + + VSFrameRef *(VS_CC *newVideoFrame)(const VSFormat *format, int width, int height, const VSFrameRef *propSrc, VSCore *core) VS_NOEXCEPT; + VSFrameRef *(VS_CC *copyFrame)(const VSFrameRef *f, VSCore *core) VS_NOEXCEPT; + void (VS_CC *copyFrameProps)(const VSFrameRef *src, VSFrameRef *dst, VSCore *core) VS_NOEXCEPT; + + void (VS_CC *registerFunction)(const char *name, const char *args, VSPublicFunction argsFunc, void *functionData, VSPlugin *plugin) VS_NOEXCEPT; + VSPlugin *(VS_CC *getPluginById)(const char *identifier, VSCore *core) VS_NOEXCEPT; + VSPlugin *(VS_CC *getPluginByNs)(const char *ns, VSCore *core) VS_NOEXCEPT; + VSMap *(VS_CC *getPlugins)(VSCore *core) VS_NOEXCEPT; + VSMap *(VS_CC *getFunctions)(VSPlugin *plugin) VS_NOEXCEPT; + void (VS_CC *createFilter)(const VSMap *in, VSMap *out, const char *name, VSFilterInit init, VSFilterGetFrame getFrame, VSFilterFree free, int filterMode, int flags, void *instanceData, VSCore *core) VS_NOEXCEPT; + void (VS_CC *setError)(VSMap *map, const char *errorMessage) VS_NOEXCEPT; /* use to signal errors outside filter getframe functions */ + const char *(VS_CC *getError)(const VSMap *map) VS_NOEXCEPT; /* use to query errors, returns 0 if no error */ + void (VS_CC *setFilterError)(const char *errorMessage, VSFrameContext *frameCtx) VS_NOEXCEPT; /* use to signal errors in the filter getframe function */ + VSMap *(VS_CC *invoke)(VSPlugin *plugin, const char *name, const VSMap *args) VS_NOEXCEPT; + + const VSFormat *(VS_CC *getFormatPreset)(int id, VSCore *core) VS_NOEXCEPT; + const VSFormat *(VS_CC *registerFormat)(int colorFamily, int sampleType, int bitsPerSample, int subSamplingW, int subSamplingH, VSCore *core) VS_NOEXCEPT; + + const VSFrameRef *(VS_CC *getFrame)(int n, VSNodeRef *node, char *errorMsg, int bufSize) VS_NOEXCEPT; /* do never use inside a filter's getframe function, for external applications using the core as a library or for requesting frames in a filter constructor */ + void (VS_CC *getFrameAsync)(int n, VSNodeRef *node, VSFrameDoneCallback callback, void *userData) VS_NOEXCEPT; /* do never use inside a filter's getframe function, for external applications using the core as a library or for requesting frames in a filter constructor */ + const VSFrameRef *(VS_CC *getFrameFilter)(int n, VSNodeRef *node, VSFrameContext *frameCtx) VS_NOEXCEPT; /* only use inside a filter's getframe function */ + void (VS_CC *requestFrameFilter)(int n, VSNodeRef *node, VSFrameContext *frameCtx) VS_NOEXCEPT; /* only use inside a filter's getframe function */ + void (VS_CC *queryCompletedFrame)(VSNodeRef **node, int *n, VSFrameContext *frameCtx) VS_NOEXCEPT; /* only use inside a filter's getframe function */ + void (VS_CC *releaseFrameEarly)(VSNodeRef *node, int n, VSFrameContext *frameCtx) VS_NOEXCEPT; /* only use inside a filter's getframe function */ + + int (VS_CC *getStride)(const VSFrameRef *f, int plane) VS_NOEXCEPT; + const uint8_t *(VS_CC *getReadPtr)(const VSFrameRef *f, int plane) VS_NOEXCEPT; + uint8_t *(VS_CC *getWritePtr)(VSFrameRef *f, int plane) VS_NOEXCEPT; + + VSFuncRef *(VS_CC *createFunc)(VSPublicFunction func, void *userData, VSFreeFuncData free, VSCore *core, const VSAPI *vsapi) VS_NOEXCEPT; + void (VS_CC *callFunc)(VSFuncRef *func, const VSMap *in, VSMap *out, VSCore *core, const VSAPI *vsapi) VS_NOEXCEPT; /* core and vsapi arguments are completely ignored, they only remain to preserve ABI */ + + /* property access functions */ + VSMap *(VS_CC *createMap)(void) VS_NOEXCEPT; + void (VS_CC *freeMap)(VSMap *map) VS_NOEXCEPT; + void (VS_CC *clearMap)(VSMap *map) VS_NOEXCEPT; + + const VSVideoInfo *(VS_CC *getVideoInfo)(VSNodeRef *node) VS_NOEXCEPT; + void (VS_CC *setVideoInfo)(const VSVideoInfo *vi, int numOutputs, VSNode *node) VS_NOEXCEPT; + const VSFormat *(VS_CC *getFrameFormat)(const VSFrameRef *f) VS_NOEXCEPT; + int (VS_CC *getFrameWidth)(const VSFrameRef *f, int plane) VS_NOEXCEPT; + int (VS_CC *getFrameHeight)(const VSFrameRef *f, int plane) VS_NOEXCEPT; + const VSMap *(VS_CC *getFramePropsRO)(const VSFrameRef *f) VS_NOEXCEPT; + VSMap *(VS_CC *getFramePropsRW)(VSFrameRef *f) VS_NOEXCEPT; + + int (VS_CC *propNumKeys)(const VSMap *map) VS_NOEXCEPT; + const char *(VS_CC *propGetKey)(const VSMap *map, int index) VS_NOEXCEPT; + int (VS_CC *propNumElements)(const VSMap *map, const char *key) VS_NOEXCEPT; + char (VS_CC *propGetType)(const VSMap *map, const char *key) VS_NOEXCEPT; + + int64_t(VS_CC *propGetInt)(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT; + double(VS_CC *propGetFloat)(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT; + const char *(VS_CC *propGetData)(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT; + int (VS_CC *propGetDataSize)(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT; + VSNodeRef *(VS_CC *propGetNode)(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT; + const VSFrameRef *(VS_CC *propGetFrame)(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT; + VSFuncRef *(VS_CC *propGetFunc)(const VSMap *map, const char *key, int index, int *error) VS_NOEXCEPT; + + int (VS_CC *propDeleteKey)(VSMap *map, const char *key) VS_NOEXCEPT; + int (VS_CC *propSetInt)(VSMap *map, const char *key, int64_t i, int append) VS_NOEXCEPT; + int (VS_CC *propSetFloat)(VSMap *map, const char *key, double d, int append) VS_NOEXCEPT; + int (VS_CC *propSetData)(VSMap *map, const char *key, const char *data, int size, int append) VS_NOEXCEPT; + int (VS_CC *propSetNode)(VSMap *map, const char *key, VSNodeRef *node, int append) VS_NOEXCEPT; + int (VS_CC *propSetFrame)(VSMap *map, const char *key, const VSFrameRef *f, int append) VS_NOEXCEPT; + int (VS_CC *propSetFunc)(VSMap *map, const char *key, VSFuncRef *func, int append) VS_NOEXCEPT; + + int64_t (VS_CC *setMaxCacheSize)(int64_t bytes, VSCore *core) VS_NOEXCEPT; + int (VS_CC *getOutputIndex)(VSFrameContext *frameCtx) VS_NOEXCEPT; + VSFrameRef *(VS_CC *newVideoFrame2)(const VSFormat *format, int width, int height, const VSFrameRef **planeSrc, const int *planes, const VSFrameRef *propSrc, VSCore *core) VS_NOEXCEPT; + void (VS_CC *setMessageHandler)(VSMessageHandler handler, void *userData) VS_NOEXCEPT; /* deprecated as of api 3.6, use addMessageHandler and removeMessageHandler instead */ + int (VS_CC *setThreadCount)(int threads, VSCore *core) VS_NOEXCEPT; + + const char *(VS_CC *getPluginPath)(const VSPlugin *plugin) VS_NOEXCEPT; + + /* api 3.1 */ + const int64_t *(VS_CC *propGetIntArray)(const VSMap *map, const char *key, int *error) VS_NOEXCEPT; + const double *(VS_CC *propGetFloatArray)(const VSMap *map, const char *key, int *error) VS_NOEXCEPT; + + int (VS_CC *propSetIntArray)(VSMap *map, const char *key, const int64_t *i, int size) VS_NOEXCEPT; + int (VS_CC *propSetFloatArray)(VSMap *map, const char *key, const double *d, int size) VS_NOEXCEPT; + + /* api 3.4 */ + void (VS_CC *logMessage)(int msgType, const char *msg) VS_NOEXCEPT; + + /* api 3.6 */ + int (VS_CC *addMessageHandler)(VSMessageHandler handler, VSMessageHandlerFree free, void *userData) VS_NOEXCEPT; + int (VS_CC *removeMessageHandler)(int id) VS_NOEXCEPT; + void (VS_CC *getCoreInfo2)(VSCore *core, VSCoreInfo *info) VS_NOEXCEPT; +}; + +VS_API(const VSAPI *) getVapourSynthAPI(int version) VS_NOEXCEPT; + +#endif /* VAPOURSYNTH_H */ diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..3037b83 --- /dev/null +++ b/main.cpp @@ -0,0 +1,5 @@ +#include +#include +#include +#include +#include "src/neo_tmedian.hpp" diff --git a/src/cpuid.cpp b/src/cpuid.cpp new file mode 100644 index 0000000..7a070ac --- /dev/null +++ b/src/cpuid.cpp @@ -0,0 +1,154 @@ +// Avisynth v1.0 beta. Copyright 2000 Ben Rudiak-Gould. +// http://www.math.berkeley.edu/~benrg/avisynth.html + +// VirtualDub - Video processing and capture application +// Copyright (C) 1998-2000 Avery Lee +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#include +#include +#include +#ifdef AVS_WINDOWS +#include +#else +#include +#undef __cpuid + +static inline void __cpuid(int cpuinfo[4], int leaf) { + unsigned int eax, ebx, ecx, edx; + // for deeper leaves __get_cpuid is not enough + __get_cpuid_count(leaf, 0, &eax, &ebx, &ecx, &edx); + cpuinfo[0] = eax; + cpuinfo[1] = ebx; + cpuinfo[2] = ecx; + cpuinfo[3] = edx; +} + +#endif + +#define IS_BIT_SET(bitfield, bit) ((bitfield) & (1<<(bit)) ? true : false) + +static uint32_t get_xcr0() +{ + uint32_t xcr0; + // _XCR_XFEATURE_ENABLED_MASK: 0 +#if defined(GCC) || defined(CLANG) + __asm__("xgetbv" : "=a" (xcr0) : "c" (0) : "%edx"); +#else + xcr0 = (uint32_t)_xgetbv(0); +#endif + return xcr0; +} + +static int CPUCheckForExtensions() +{ + int result = 0; + int cpuinfo[4]; + + __cpuid(cpuinfo, 1); + if (IS_BIT_SET(cpuinfo[3], 0)) + result |= CPUF_FPU; + if (IS_BIT_SET(cpuinfo[3], 23)) + result |= CPUF_MMX; + if (IS_BIT_SET(cpuinfo[3], 25)) + result |= CPUF_SSE | CPUF_INTEGER_SSE; + if (IS_BIT_SET(cpuinfo[3], 26)) + result |= CPUF_SSE2; + if (IS_BIT_SET(cpuinfo[2], 0)) + result |= CPUF_SSE3; + if (IS_BIT_SET(cpuinfo[2], 9)) + result |= CPUF_SSSE3; + if (IS_BIT_SET(cpuinfo[2], 19)) + result |= CPUF_SSE4_1; + if (IS_BIT_SET(cpuinfo[2], 20)) + result |= CPUF_SSE4_2; + if (IS_BIT_SET(cpuinfo[2], 22)) + result |= CPUF_MOVBE; + if (IS_BIT_SET(cpuinfo[2], 23)) + result |= CPUF_POPCNT; + if (IS_BIT_SET(cpuinfo[2], 25)) + result |= CPUF_AES; + if (IS_BIT_SET(cpuinfo[2], 29)) + result |= CPUF_F16C; + // AVX + bool xgetbv_supported = IS_BIT_SET(cpuinfo[2], 27); + bool avx_supported = IS_BIT_SET(cpuinfo[2], 28); + if (xgetbv_supported && avx_supported) + { + uint32_t xgetbv0_32 = get_xcr0(); + if ((xgetbv0_32 & 0x6u) == 0x6u) { + result |= CPUF_AVX; + if (IS_BIT_SET(cpuinfo[2], 12)) + result |= CPUF_FMA3; + __cpuid(cpuinfo, 7); + if (IS_BIT_SET(cpuinfo[1], 5)) + result |= CPUF_AVX2; + } + if((xgetbv0_32 & (0x7u << 5)) && // OPMASK: upper-256 enabled by OS + (xgetbv0_32 & (0x3u << 1))) { // XMM/YMM enabled by OS + // Verify that XCR0[7:5] = "111b" (OPMASK state, upper 256-bit of ZMM0-ZMM15 and + // ZMM16-ZMM31 state are enabled by OS) + /// and that XCR0[2:1] = "11b" (XMM state and YMM state are enabled by OS). + __cpuid(cpuinfo, 7); + if (IS_BIT_SET(cpuinfo[1], 16)) + result |= CPUF_AVX512F; + if (IS_BIT_SET(cpuinfo[1], 17)) + result |= CPUF_AVX512DQ; + if (IS_BIT_SET(cpuinfo[1], 21)) + result |= CPUF_AVX512IFMA; + if (IS_BIT_SET(cpuinfo[1], 26)) + result |= CPUF_AVX512PF; + if (IS_BIT_SET(cpuinfo[1], 27)) + result |= CPUF_AVX512ER; + if (IS_BIT_SET(cpuinfo[1], 28)) + result |= CPUF_AVX512CD; + if (IS_BIT_SET(cpuinfo[1], 30)) + result |= CPUF_AVX512BW; + if (IS_BIT_SET(cpuinfo[1], 31)) + result |= CPUF_AVX512VL; + if (IS_BIT_SET(cpuinfo[2], 1)) // [2]! + result |= CPUF_AVX512VBMI; + } + } + + // 3DNow!, 3DNow!, ISSE, FMA4 + __cpuid(cpuinfo, 0x80000000); + if (cpuinfo[0] >= 0x80000001) + { + __cpuid(cpuinfo, 0x80000001); + + if (IS_BIT_SET(cpuinfo[3], 31)) + result |= CPUF_3DNOW; + + if (IS_BIT_SET(cpuinfo[3], 30)) + result |= CPUF_3DNOW_EXT; + + if (IS_BIT_SET(cpuinfo[3], 22)) + result |= CPUF_INTEGER_SSE; + + if (result & CPUF_AVX) { + if (IS_BIT_SET(cpuinfo[2], 16)) + result |= CPUF_FMA4; + } + } + + return result; +} + +int GetCPUFlags() { + static int lCPUExtensionsAvailable = CPUCheckForExtensions(); + return lCPUExtensionsAvailable; +} diff --git a/src/neo_tmedian.hpp b/src/neo_tmedian.hpp new file mode 100644 index 0000000..25d6ade --- /dev/null +++ b/src/neo_tmedian.hpp @@ -0,0 +1,27 @@ +/* + * Copyright 2020 Xinyue Lu + * + * DualSynth bridge - plugin. + * + */ + +#pragma once + +#include "version.hpp" +#include "tmedian.hpp" + +namespace Plugin { + const char* Identifier = "in.7086.neo_tmedian"; + const char* Namespace = "neo_tmedian"; + const char* Description = "Neo Temporal Median Filter " PLUGIN_VERSION; +} + +std::vector RegisterVSFilters() +{ + return std::vector { VSInterface::RegisterFilter }; +} + +std::vector RegisterAVSFilters() +{ + return std::vector { AVSInterface::RegisterFilter }; +} diff --git a/src/tmedian.hpp b/src/tmedian.hpp new file mode 100644 index 0000000..8be74e3 --- /dev/null +++ b/src/tmedian.hpp @@ -0,0 +1,155 @@ +/* + * Copyright 2020 Xinyue Lu + * + * Temporal Median - filter. + * + */ + +#pragma once + +#include "tmedian_common.h" + +int GetCPUFlags(); + +struct TMedian final : Filter { + int process[4] {2, 2, 2, 2}; + int radius {1}; + InDelegator* _in; + bool crop; + + void (*tmedian_SIMD)(int radius, const unsigned char** in_ptr, unsigned char* out_ptr, int width, int height, int stride_bytes); + + const char* VSName() const override { return "TemporalMedian"; } + const char* AVSName() const override { return "neo_tmedian"; } + const MtMode AVSMode() const override { return MT_NICE_FILTER; } + const VSFilterMode VSMode() const override { return fmParallel; } + const std::vector Params() const override { + return std::vector { + Param {"clip", Clip, false, true, true, false}, + Param {"radius", Integer}, + Param {"planes", Integer, true}, + Param {"y", Integer, false, true, false}, + Param {"u", Integer, false, true, false}, + Param {"v", Integer, false, true, false} + }; + } + void Initialize(InDelegator* in, DSVideoInfo in_vi, FetchFrameFunctor* fetch_frame) override + { + Filter::Initialize(in, in_vi, fetch_frame); + in->Read("radius", radius); + + try { + process[0] = + process[1] = + process[2] = + process[3] = 2; + std::vector user_planes {0, 1, 2}; + in->Read("planes", user_planes); + for (auto &&p : user_planes) + { + if (p < in_vi.Format.Planes) + process[p] = 3; + } + } + catch (const char *) { + process[0] = + process[1] = + process[2] = 3; + in->Read("y", process[0]); + in->Read("u", process[1]); + in->Read("v", process[2]); + } + + int CPUFlags = GetCPUFlags(); + if (CPUFlags & CPUF_SSE4_1) { + switch (in_vi.Format.BytesPerSample) { + case 1: tmedian_SIMD = tmedian_SIMD_128; break; + case 2: tmedian_SIMD = tmedian_SIMD_128; break; + case 4: tmedian_SIMD = tmedian_SIMD_128; break; + } + } + if (CPUFlags & CPUF_AVX2) { + switch (in_vi.Format.BytesPerSample) { + case 1: tmedian_SIMD = tmedian_SIMD_256; break; + case 2: tmedian_SIMD = tmedian_SIMD_256; break; + case 4: tmedian_SIMD = tmedian_SIMD_256; break; + } + } + if (CPUFlags & CPUF_AVX512F) { + switch (in_vi.Format.BytesPerSample) { + case 1: tmedian_SIMD = tmedian_SIMD_512; break; + case 2: tmedian_SIMD = tmedian_SIMD_512; break; + case 4: tmedian_SIMD = tmedian_SIMD_512; break; + } + } + } + + std::vector RequestReferenceFrames(int n) const override + { + std::vector req; + int rad = radius; + if (rad > n) rad = n; + if (rad > in_vi.Frames - 1 - n) rad = in_vi.Frames - 1 - n; + + for (int i = n-rad; i <= n+rad; i++) + req.push_back(i); + + return req; + } + + DSFrame GetFrame(int n, std::unordered_map in_frames) override + { + auto src = in_frames[n]; + unsigned int real_radius = static_cast(in_frames.size()) / 2; // Real radius + if (real_radius == 0) + return src; + auto dst = src.Create(false); + std::vector samples(in_frames.size()); + for (int p = 0; p < in_vi.Format.Planes; p++) + { + bool chroma = in_vi.Format.IsFamilyYUV && p > 0 && p < 3; + auto height = in_vi.Height; + auto width = in_vi.Width; + auto stride = src.StrideBytes[p]; + auto src_ptr = src.SrcPointers[p]; + auto dst_ptr = dst.DstPointers[p]; + + if (chroma) { + height >>= in_vi.Format.SSH; + width >>= in_vi.Format.SSW; + } + + if (process[p] == 2) { + memcpy(dst_ptr, src_ptr, stride * height); + continue; + } + if (process[p] != 3) + continue; + + // Main code + const unsigned char* src_ptrp[MAX_FRAMES]; + int idx = 0; + if (real_radius <= MAX_FRAMES / 2) { + // SIMD + for (auto &&fv : in_frames) + src_ptrp[idx++] = fv.second.SrcPointers[p]; + tmedian_SIMD(real_radius, src_ptrp, dst_ptr, width, height, stride); + } + else + // C + for (int h = 0; h < height; h++) + for (int w = 0; w < width; w++) + { + int idx = 0; + for (auto &&fv : in_frames) + samples[idx++] = fv.second.SrcPointers[p][h * stride + w]; + std::nth_element(samples.begin(), samples.begin() + real_radius, samples.end()); + dst_ptr[h * stride + w] = samples[real_radius]; + } + } + + return dst; + } + + ~TMedian() = default; +}; diff --git a/src/tmedian_AVX2.cpp b/src/tmedian_AVX2.cpp new file mode 100644 index 0000000..21a8e70 --- /dev/null +++ b/src/tmedian_AVX2.cpp @@ -0,0 +1,23 @@ +/* + * Copyright 2020 Xinyue Lu + * + * Temporal Median implementation, AVX2 SIMD code. + * + */ + +#include "tmedian_SIMD.hpp" + +template <> __m256i min(__m256i a, __m256i b) { return _mm256_min_epu8 (a, b); } +template <> __m256i max(__m256i a, __m256i b) { return _mm256_max_epu8 (a, b); } +template <> __m256i min(__m256i a, __m256i b) { return _mm256_min_epu16(a, b); } +template <> __m256i max(__m256i a, __m256i b) { return _mm256_max_epu16(a, b); } +template <> __m256 min (__m256 a, __m256 b) { return _mm256_min_ps (a, b); } +template <> __m256 max (__m256 a, __m256 b) { return _mm256_max_ps (a, b); } +template <> __m256i load<__m256i>(const __m256i* mem) { return _mm256_load_si256(mem); } +template <> void store<__m256i>(__m256i* mem, __m256i a) { return _mm256_store_si256(mem, a); } +template <> __m256 load<__m256>(const __m256* mem) { return _mm256_castsi256_ps(_mm256_load_si256((const __m256i*)mem)); } +template <> void store<__m256>(__m256* mem, __m256 a) { return _mm256_store_si256((__m256i*)mem, _mm256_castps_si256(a)); } + +template void tmedian_SIMD_256(int, const unsigned char**, unsigned char*, int, int, int); +template void tmedian_SIMD_256(int, const unsigned char**, unsigned char*, int, int, int); +template void tmedian_SIMD_256(int, const unsigned char**, unsigned char*, int, int, int); diff --git a/src/tmedian_AVX512.cpp b/src/tmedian_AVX512.cpp new file mode 100644 index 0000000..07582be --- /dev/null +++ b/src/tmedian_AVX512.cpp @@ -0,0 +1,23 @@ +/* + * Copyright 2020 Xinyue Lu + * + * Temporal Median implementation, AVX512 SIMD code. + * + */ + +#include "tmedian_SIMD.hpp" + +template <> __m512i min(__m512i a, __m512i b) { return _mm512_min_epu8 (a, b); } +template <> __m512i max(__m512i a, __m512i b) { return _mm512_max_epu8 (a, b); } +template <> __m512i min(__m512i a, __m512i b) { return _mm512_min_epu16(a, b); } +template <> __m512i max(__m512i a, __m512i b) { return _mm512_max_epu16(a, b); } +template <> __m512 min (__m512 a, __m512 b) { return _mm512_min_ps (a, b); } +template <> __m512 max (__m512 a, __m512 b) { return _mm512_max_ps (a, b); } +template <> __m512i load<__m512i>(const __m512i* mem) { return _mm512_load_si512(mem); } +template <> void store<__m512i>(__m512i* mem, __m512i a) { return _mm512_store_si512(mem, a); } +template <> __m512 load<__m512>(const __m512* mem) { return _mm512_castsi512_ps(_mm512_load_si512((const __m512i*)mem)); } +template <> void store<__m512>(__m512* mem, __m512 a) { return _mm512_store_si512((__m512i*)mem, _mm512_castps_si512(a)); } + +template void tmedian_SIMD_512(int, const unsigned char**, unsigned char*, int, int, int); +template void tmedian_SIMD_512(int, const unsigned char**, unsigned char*, int, int, int); +template void tmedian_SIMD_512(int, const unsigned char**, unsigned char*, int, int, int); diff --git a/src/tmedian_SIMD.hpp b/src/tmedian_SIMD.hpp new file mode 100644 index 0000000..d9bdd8f --- /dev/null +++ b/src/tmedian_SIMD.hpp @@ -0,0 +1,225 @@ +/* + * Copyright 2020 Xinyue Lu + * + * Temporal Median implementation, shared SIMD code. + * + */ + +#pragma once +#include "tmedian_common.h" + +#if defined(__clang__) + #ifndef __AVX512F__ + class __m512 {}; + class __m512i {}; + #endif + #ifndef __AVX2__ + class __m256 {}; + class __m256i {}; + #endif +#endif + +template +static inline void swap2(ST &a, ST &b) { + auto minab = min(a, b); + auto maxab = max(a, b); + a = minab; + b = maxab; +} + +template +static inline ST median3(ST a, ST b, ST c) { + swap2(a, b); + return max(a, min(c, b)); +} + +template +static inline void tmedian_SIMD_r1(const T** in_ptr, T* out_ptr, int width, int height, int offset) +{ + ST _s[3]; + for (int i = 0; i < 3; i++) + _s[i] = load(reinterpret_cast(in_ptr[i] + offset)); + + auto med = median3(_s[0], _s[1], _s[2]); + store(reinterpret_cast(out_ptr + offset), med); +} + +template +static inline void tmedian_SIMD_r2(const T** in_ptr, T* out_ptr, int width, int height, int offset) +{ + ST _s[5]; + for (int i = 0; i < 5; i++) + _s[i] = load(reinterpret_cast(in_ptr[i] + offset)); + + swap2(_s[0], _s[1]); swap2(_s[2], _s[3]); + swap2(_s[0], _s[2]); swap2(_s[1], _s[3]); // Throw s0 s3 + // Note: thrown variables are optimized away by compiler. + auto med = median3(_s[1], _s[2], _s[4]); + store(reinterpret_cast(out_ptr + offset), med); +} + +template +static inline void tmedian_SIMD_r3(const T** in_ptr, T* out_ptr, int width, int height, int offset) +{ + ST _s[7]; + for (int i = 0; i < 7; i++) + _s[i] = load(reinterpret_cast(in_ptr[i] + offset)); + + swap2(_s[1], _s[2]); swap2(_s[3], _s[4]); + swap2(_s[0], _s[2]); swap2(_s[3], _s[5]); + swap2(_s[0], _s[1]); swap2(_s[4], _s[5]); + swap2(_s[0], _s[4]); swap2(_s[1], _s[5]); // Throw s0 s5 + swap2(_s[1], _s[3]); swap2(_s[2], _s[4]); // Throw s1 s4 + auto med = median3(_s[2], _s[3], _s[6]); + store(reinterpret_cast(out_ptr + offset), med); +} + +template +static inline void tmedian_SIMD_r4(const T** in_ptr, T* out_ptr, int width, int height, int offset) +{ + ST _s[9]; + for (int i = 0; i < 9; i++) + _s[i] = load(reinterpret_cast(in_ptr[i] + offset)); + + swap2(_s[0], _s[1]); swap2(_s[2], _s[3]); + swap2(_s[4], _s[5]); swap2(_s[6], _s[7]); + + swap2(_s[0], _s[2]); swap2(_s[1], _s[3]); + swap2(_s[4], _s[6]); swap2(_s[5], _s[7]); + + swap2(_s[0], _s[4]); swap2(_s[1], _s[2]); // Throw s0 + swap2(_s[5], _s[6]); swap2(_s[3], _s[7]); // Throw s7 + + swap2(_s[1], _s[5]); swap2(_s[2], _s[6]); // Throw s1 s6 + swap2(_s[2], _s[4]); swap2(_s[3], _s[5]); // Throw s2 s5 + + auto med = median3(_s[3], _s[4], _s[8]); + store(reinterpret_cast(out_ptr + offset), med); +} + +template +static inline void tmedian_SIMD_r5(const T** in_ptr, T* out_ptr, int width, int height, int offset) +{ + ST _s[11]; + for (int i = 0; i < 11; i++) + _s[i] = load(reinterpret_cast(in_ptr[i] + offset)); + + swap2(_s[0], _s[9]); + swap2(_s[1], _s[2]); swap2(_s[3], _s[4]); + swap2(_s[5], _s[6]); swap2(_s[7], _s[8]); + + swap2(_s[1], _s[8]); + swap2(_s[0], _s[2]); swap2(_s[3], _s[5]); + swap2(_s[0], _s[3]); // Throw s0 + swap2(_s[4], _s[6]); swap2(_s[7], _s[9]); + swap2(_s[6], _s[9]); // Throw s9 + swap2(_s[2], _s[7]); + + swap2(_s[4], _s[5]); + swap2(_s[4], _s[8]); + swap2(_s[1], _s[5]); + + swap2(_s[1], _s[2]); swap2(_s[3], _s[4]); // Throw s1 s3 + swap2(_s[5], _s[6]); swap2(_s[7], _s[8]); // Throw s6 s8 + swap2(_s[2], _s[4]); swap2(_s[5], _s[7]); // Throw s2 s7 + + auto med = median3(_s[4], _s[5], _s[10]); + store(reinterpret_cast(out_ptr + offset), med); +} + +template +static inline void tmedian_SIMD_r6(const T** in_ptr, T* out_ptr, int width, int height, int offset) +{ + ST _s[13]; + for (int i = 0; i < 13; i++) + _s[i] = load(reinterpret_cast(in_ptr[i] + offset)); + + swap2(_s[0], _s[1]); swap2(_s[2], _s[3]); + swap2(_s[4], _s[5]); swap2(_s[6], _s[7]); + swap2(_s[8], _s[9]); swap2(_s[10], _s[11]); + + swap2(_s[0], _s[2]); swap2(_s[1], _s[3]); + swap2(_s[4], _s[6]); swap2(_s[5], _s[7]); + swap2(_s[8], _s[10]); swap2(_s[9], _s[11]); + swap2(_s[7], _s[11]); // Throw s11 + + swap2(_s[2], _s[6]); + swap2(_s[1], _s[5]); + swap2(_s[1], _s[2]); + swap2(_s[9], _s[10]); + swap2(_s[6], _s[10]); + swap2(_s[5], _s[9]); + swap2(_s[0], _s[4]); // Throw s0 + swap2(_s[9], _s[10]); // Throw s10 + swap2(_s[4], _s[8]); // Throw s4 + swap2(_s[3], _s[7]); // Throw s7 + swap2(_s[2], _s[6]); + swap2(_s[1], _s[5]); // Throw s1 + swap2(_s[0], _s[4]); + + swap2(_s[3], _s[8]); + + swap2(_s[2], _s[3]); // Throw s2 + swap2(_s[5], _s[6]); + swap2(_s[8], _s[9]); // Throw s9 + + swap2(_s[3], _s[5]); // Throw s3 + swap2(_s[6], _s[8]); // Throw s8 + + auto med = median3(_s[4], _s[5], _s[12]); + store(reinterpret_cast(out_ptr + offset), med); +} + +template +void tmedian_SIMD_impl(int radius, const unsigned char** in_ptr, unsigned char* out_ptr, int width, int height, int stride_bytes) +{ + int stride = stride_bytes / sizeof(T); + const T* src_ptr[MAX_FRAMES]; + for (int i = 0; i <= radius * 2; i++) + src_ptr[i] = reinterpret_cast(in_ptr[i]); + T* dst_ptr = reinterpret_cast(out_ptr); + + int length = 16; + if constexpr(std::is_same::value || std::is_same::value) + length = 16; + else if constexpr(std::is_same::value || std::is_same::value) + length = 32; + else if constexpr(std::is_same::value || std::is_same::value) + length = 64; + int step = length / sizeof(T); + + for (int h = 0; h < height; h++) + for (int w = 0; w < width; w += step) { + int offset = h * stride + w; + switch(radius) { + case 1: tmedian_SIMD_r1(src_ptr, dst_ptr, width, height, offset); break; + case 2: tmedian_SIMD_r2(src_ptr, dst_ptr, width, height, offset); break; + case 3: tmedian_SIMD_r3(src_ptr, dst_ptr, width, height, offset); break; + case 4: tmedian_SIMD_r4(src_ptr, dst_ptr, width, height, offset); break; + case 5: tmedian_SIMD_r5(src_ptr, dst_ptr, width, height, offset); break; + case 6: tmedian_SIMD_r6(src_ptr, dst_ptr, width, height, offset); break; + } + } +} + +template +void tmedian_SIMD_128(int radius, const unsigned char** in_ptr, unsigned char* out_ptr, int width, int height, int stride_bytes) { + if constexpr(std::is_same::value) + tmedian_SIMD_impl(radius, in_ptr, out_ptr, width, height, stride_bytes); + else + tmedian_SIMD_impl(radius, in_ptr, out_ptr, width, height, stride_bytes); +} +template +void tmedian_SIMD_256(int radius, const unsigned char** in_ptr, unsigned char* out_ptr, int width, int height, int stride_bytes) { + if constexpr(std::is_same::value) + tmedian_SIMD_impl(radius, in_ptr, out_ptr, width, height, stride_bytes); + else + tmedian_SIMD_impl(radius, in_ptr, out_ptr, width, height, stride_bytes); +} +template +void tmedian_SIMD_512(int radius, const unsigned char** in_ptr, unsigned char* out_ptr, int width, int height, int stride_bytes) { + if constexpr(std::is_same::value) + tmedian_SIMD_impl(radius, in_ptr, out_ptr, width, height, stride_bytes); + else + tmedian_SIMD_impl(radius, in_ptr, out_ptr, width, height, stride_bytes); +} diff --git a/src/tmedian_SSE41.cpp b/src/tmedian_SSE41.cpp new file mode 100644 index 0000000..d355b5a --- /dev/null +++ b/src/tmedian_SSE41.cpp @@ -0,0 +1,23 @@ +/* + * Copyright 2020 Xinyue Lu + * + * Temporal Median implementation, SSE4.1 SIMD code. + * + */ + +#include "tmedian_SIMD.hpp" + +template <> __m128i min(__m128i a, __m128i b) { return _mm_min_epu8 (a, b); } +template <> __m128i max(__m128i a, __m128i b) { return _mm_max_epu8 (a, b); } +template <> __m128i min(__m128i a, __m128i b) { return _mm_min_epu16(a, b); } +template <> __m128i max(__m128i a, __m128i b) { return _mm_max_epu16(a, b); } +template <> __m128 min (__m128 a, __m128 b) { return _mm_min_ps (a, b); } +template <> __m128 max (__m128 a, __m128 b) { return _mm_max_ps (a, b); } +template <> __m128i load<__m128i>(const __m128i* mem) { return _mm_load_si128(mem); } +template <> void store<__m128i>(__m128i* mem, __m128i a) { return _mm_store_si128(mem, a); } +template <> __m128 load<__m128>(const __m128* mem) { return _mm_castsi128_ps(_mm_load_si128((const __m128i*)mem)); } +template <> void store<__m128>(__m128* mem, __m128 a) { return _mm_store_si128((__m128i*)mem, _mm_castps_si128(a)); } + +template void tmedian_SIMD_128(int, const unsigned char**, unsigned char*, int, int, int); +template void tmedian_SIMD_128(int, const unsigned char**, unsigned char*, int, int, int); +template void tmedian_SIMD_128(int, const unsigned char**, unsigned char*, int, int, int); diff --git a/src/tmedian_common.h b/src/tmedian_common.h new file mode 100644 index 0000000..378d483 --- /dev/null +++ b/src/tmedian_common.h @@ -0,0 +1,25 @@ +/* + * Copyright 2020 Xinyue Lu + * + * Temporal Median implementation, public header. + * + */ + +#pragma once +#include +#include +#include +#include + +#define MAX_FRAMES 13 + +template +void tmedian_SIMD_128(int radius, const unsigned char** in_ptr, unsigned char* out_ptr, int width, int height, int stride_bytes); +template +void tmedian_SIMD_256(int radius, const unsigned char** in_ptr, unsigned char* out_ptr, int width, int height, int stride_bytes); +template +void tmedian_SIMD_512(int radius, const unsigned char** in_ptr, unsigned char* out_ptr, int width, int height, int stride_bytes); +template ST min(ST a, ST b); +template ST max(ST a, ST b); +template ST load(const ST* mem); +template void store(ST* mem, ST a); diff --git a/src/version.hpp.in b/src/version.hpp.in new file mode 100644 index 0000000..0bb98ba --- /dev/null +++ b/src/version.hpp.in @@ -0,0 +1,3 @@ +#pragma once + +#define PLUGIN_VERSION "@VERSION@" \ No newline at end of file diff --git a/src/version.rc.in b/src/version.rc.in new file mode 100644 index 0000000..b4db383 --- /dev/null +++ b/src/version.rc.in @@ -0,0 +1,30 @@ +#include + +VS_VERSION_INFO VERSIONINFO +FILEVERSION 1,0,0,0 +PRODUCTVERSION 1,0,0,0 +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +FILEFLAGS 0 +FILEOS VOS__WINDOWS32 +FILETYPE VFT_DLL +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "Comments", "Neo-FFT3D" + VALUE "CompanyName", "" + VALUE "FileDescription", "Neo-FFT3D" + VALUE "FileVersion", "@VERSION@" + VALUE "InternalName", "Neo-FFT3D" + VALUE "OriginalFilename", "Neo-FFT3D.dll" + VALUE "ProductName", "Neo-FFT3D" + VALUE "ProductVersion", "@VERSION@" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409,1200 + END +END