First After Effects Effects-Plug-In Development
GOAL
Develop my first After Effects Plugin, ColorChange effect.
Environment
Adobe CC2019 v16.0.0
Windows10 with environment for C++ compile
Microsoft Visual C++ 2017 ver15.8.4
Method
Environment setup
Set up the environment for After Effects plugin with reference to “The Environment for After Effects Plugin Development.”
Copy Template
Copy Skeleton template from “AfterEffectsSDK\Examples\Template\Skeleton” , with directory hierarchies of “Skeleton”, “Headers”, “Resources” and “Util” preserved .
Rename file names and replace “Skeleton” with “Color Change” in it. DON’T rename “SkeletonPiPL.rc” because binary .rc file is automatically generated from .r file (AE Plugin SDK Guide “PiPL Resources”).
EffectMain Function
Main function is defined in SkeletonPiPL.r as follows.
#ifdef AE_OS_WIN #ifdef AE_PROC_INTELx64 CodeWin64X86 {"EffectMain"}, #endif #else #ifdef AE_OS_MAC CodeMacIntel64 {"EffectMain"}, #endif #endif
EffectMain is the Function accepts PF_Cmd cmd as a parameter and call functions using cmd as a selector such as “PF_Cmd_ABOUT”, “PF_Cmd_PARAMS_SETUP” and “PF_Cmd_RENDER”. Refer to “AE Plugin SDK Guide command selectors.”
PF_Err EffectMain( PF_Cmd cmd, PF_InData *in_data, PF_OutData *out_data, PF_ParamDef *params[], PF_LayerDef *output, void *extra) { PF_Err err = PF_Err_NONE; try { switch (cmd) { case PF_Cmd_ABOUT: err = About(in_data, out_data, params, output); break; case PF_Cmd_GLOBAL_SETUP: err = GlobalSetup(in_data, out_data, params, output); break; case PF_Cmd_PARAMS_SETUP: err = ParamsSetup(in_data, out_data, params, output); break; case PF_Cmd_RENDER: err = Render(in_data, out_data, params, output); break; } } catch(PF_Err &thrown_err){ err = thrown_err; } return err; }
About Function
About is the function to display a dialog describing the plug-in. Change TableString in ColorChange_Stgrings.cpp as follows.
TableString g_strs[StrID_NUMTYPES] = { StrID_NONE, "", StrID_Name, "ColorChange", StrID_Description, "Change comp color to specified color", StrID_Gain_Param_Name, "Gain", StrID_Color_Param_Name, "Color", };
GlobalSetup
Now GlobalSetup function doesn’t need any change.
ParamsSetup
ParamsSetup is the function to setup UI, describe your parameters and register them.
I renamed “SKELETON” to “COLORCHANGE”, delete “GAIN”parameter and added “LEVEL” parameter. Then reset values as follows in ColorChange.h.
/* Parameter defaults */ #define COLORCHANGE_LEVEL_MIN 0 #define COLORCHANGE_LEVEL_MAX 100 #define COLORCHANGE_LEVEL_DFLT 50 enum { COLORCHANGE_INPUT = 0, COLOECHANGE_LEVEL, COLORCHANGE_COLOR, COLORCHANGE_NUM_PARAMS }; enum { LEVEL_DISK_ID = 1, COLOR_DISK_ID, };
Accordingly, rename constant names in ColorChange.cpp.
Render
Render function is the function to render the effect into the output, based on the input and parameters.
GainInfo
GainInfo is the structure to handle parameter “GAIN”. Create new structure for passing parameter data “level” and “color” in ColorChange.h.
typedef struct ParamInfo { PF_FpLong level; PF_Pixel color; PF_Pixel16 color16; } PramInfo, *PramInfoP, **PramInfoH;
//GainInfo giP; //AEFX_CLR_STRUCT(giP); ParamInfo paramDataP; AEFX_CLR_STRUCT(paramDataP); A_long linesL = 0; linesL = output->extent_hint.bottom - output->extent_hint.top; paramDataP.level = params[COLOECHANGE_LEVEL]->u.fs_d.value; paramDataP.color = params[COLORCHANGE_COLOR]->u.cd.value;
iterate
The iterate function scan input flame and calculate output frame as pixel to pixel operation, pixel function. In this case, the pixel function is “MySimpleGainFunc16” or “MySimpleGainFunc8”. Rename and change them into “MyColorChangeFunc16” and “MyColorChangeFunc8”.
if (PF_WORLD_IS_DEEP(output)){ paramDataP.color16.red = CONVERT8TO16(paramDataP.color.red); paramDataP.color16.green = CONVERT8TO16(paramDataP.color.green); paramDataP.color16.blue = CONVERT8TO16(paramDataP.color.blue); paramDataP.color16.alpha = CONVERT8TO16(paramDataP.color.alpha); ERR(suites.Iterate16Suite1()->iterate( in_data, 0, // progress base linesL, // progress final ¶ms[COLORCHANGE_INPUT]->u.ld, // src NULL, // area - null for all pixels (void*)¶mDataP, // refcon - your custom data pointer MyColorChangeFunc16, // pixel function pointer output)); }else { ERR(suites.Iterate8Suite1()->iterate( in_data, 0, // progress base linesL, // progress final ¶ms[COLORCHANGE_INPUT]->u.ld, // src NULL, // area - null for all pixels (void*)¶mDataP, // refcon - your custom data pointer MyColorChangeFunc8, // pixel function pointer output)); }
MyColorChangeFunc
I changed the pixel function as follows.
static PF_Err MyColorChangeFunc8 ( void *refcon, A_long xL, A_long yL, PF_Pixel8 *inP, PF_Pixel8 *outP) { PF_Err err = PF_Err_NONE; ParamInfo *paramDataP = reinterpret_cast<ParamInfo*>(refcon); PF_FpLong levelF = 0; float red_diff, green_diff, blue_diff; if (paramDataP){ levelF = paramDataP->level/100.0; red_diff = (paramDataP->color.red-inP->red)*levelF; green_diff = (paramDataP->color.green-inP->green)*levelF; blue_diff = (paramDataP->color.blue-inP->blue)*levelF; outP->alpha = inP->alpha; outP->red = MIN(inP->red + red_diff, PF_MAX_CHAN8); outP->green = MIN(inP->green +green_diff, PF_MAX_CHAN8); outP->blue = MIN(inP->blue + blue_diff, PF_MAX_CHAN8); } return err; }
static PF_Err MyColorChangeFunc16 ( void *refcon, A_long xL, A_long yL, PF_Pixel16 *inP, PF_Pixel16 *outP) { PF_Err err = PF_Err_NONE; ParamInfo *paramDataP = reinterpret_cast<ParamInfo*>(refcon); PF_FpLong levelF = 0; float red_diff, green_diff, blue_diff; if (paramDataP) { levelF = paramDataP->level / 100.0; red_diff = (paramDataP->color16.red - inP->red)*levelF; green_diff = (paramDataP->color16.green - inP->green)*levelF; blue_diff = (paramDataP->color16.blue - inP->blue)*levelF; outP->alpha = inP->alpha; outP->red = MIN(inP->red + red_diff, PF_MAX_CHAN16); outP->green = MIN(inP->green + green_diff, PF_MAX_CHAN16); outP->blue = MIN(inP->blue + blue_diff, PF_MAX_CHAN16); } return err; }
Build and Install
Build solution and put generated .aex file into Adobe AE directory such as “C:\Program Files\Adobe\Adobe After Effects CC 2019\Support Files.”
Appenedix
2020/09/06 Changed source codes to support 16bpc color. Thank you for comments and pointing it out.
Please take a look at the article “How to adapt your AE plugin for 8, 16, 32bit color” as well.
Hello, author.
Thank you for publish great article!!
But there was one point that stuck with me, and I’m happy to report it.
When following your Instruction, This program is not work on 16bit color, Because of “typedef struct ParamInfo”.
Hello, thank you for your helpful comment.
I’ve changed source codes to support 16bit color.
Actually this was just a simplest plugin explanation so I omitted color depth(16/32bpc), PreRender and SmartEender, but it should confuse some readers.
Thank you for pointing it out!