けん悟庵 KenGo:Lab
2011.01.24

AE0-003. AEプラグイン開発のサンプルコードと説明

1. サンプルプロジェクト「KW_Skeleton」の入手

AEプラグイン開発の説明をするにあたって、サンプルプロジェクトを作成しました。
まず基本的な構造を知るために、このコードを利用し、理解を深めていきます。

After Effects プラグイン サンプルコードKW_Skeleton.zip

KW_Skeletonに関する扱いは同梱のreadme.txtをお読みください。
基本的に改変自由・配布自由ということにしています。いちおう使ったら連絡ちょうだいね、
という条件などがありますので、もし利用される方はご確認の上でご利用くださいませ。

※注意点
ここからは環境を「Windows版 + Visual Studio2008 + AE CS3(32bit)」であることを前提に話をすすめます。
説明に使用するSDKはAE CS4 SDKです(CS3~CS5でもほぼ通用します)
よってKW_SkeletonにはMac版プロジェクトは同梱しておりません。
ただMac版を開発したい方でもKW_Skeletonのサンプルコードは参考になると思いますので、
ぜひご覧いただき、お役立てください(と、いうかビルドすれば多分動きます)

2. KW_Skeletonを動かす

2-1. KW_Skeletonの展開と配置

KW_Skeletonをダウンロードしzipを展開したら 、
そのフォルダを他のサンプルと同じ場所(同じ階層であれば別フォルダでもOK)に置きます。
階層が異なるとビルドができません。注意してください。
良くわからないという人は「Effects」フォルダ内にコピーするのがよいとおもいます。

KW_Skeletonをサンプルと同じ場所に置く

2-2. KW_Skeletonのビルド

KW_Skeletonを移動したら、中身のWinフォルダ内にKW_Skeleton.slnがありますので、
これをクリックしVisual Studioを立ち上げ、ビルドします。
ビルドは、メニューの「ビルド」→「ソリューションのリビルド」から行えます。

ソリューションのリビルド

ビルドを開始すると、画面下のコンソール画面にビルドの進捗が表示されます。
プログラムにミスやエラーがあると、ここで指摘されますので確認しましょう。
エラーなくビルドが終わると、最後に1 成功、0 失敗、0 更新不要というメッセージが出ます。
これは1つビルドに成功したよ、という意味です。失敗と更新不要は0個だったということです。

2-3. プラグインの確認

無事ビルドが成功すると、KW_Skeleton.slnと同階層に「build」と「plugin」というフォルダができます。

ビルド成果物の確認

これはKW_Skeletonの独自設定ですが、プラグインの出力先を分かりやすくするために整理しました。
出来上がったプラグインは「plugin」フォルダの中にありますので確認してください。
KW_Skeleton.aexというファイルが作成したプラグインです。

出来上がったファイルをAfter Effectsのプラグインフォルダにコピーした後、After Effectsを再起動すると、
エフェクト一覧に「KW_Sample」カテゴリが追加され、その中にKW_Skeletonがあります。
AE CS3上で処理を施した結果は以下のようになります。

KW_Skeletonの処理結果

単純なグレースケールフィルタですが、スライダを動かすとグレーと元画像の色が混ざります。
以下で、このフィルタをつくる手順を説明していきます。

3. KW_Skeletonのコードの説明

KW_SkeletonプロジェクトをVisual Studioで読みだすと、
ソリューションエクスプローラというウィンドウに関係するファイルが列挙されますが、
実際に使用したり、書き換えたりするのは下記画像で示す4つのファイルです。

書き換えの対象となるコード

それぞれのファイルには次のような役割があります。
・KW_Skeleton.h :SDKの必要なヘッダと、パラメータ情報を書くヘッダ
・KW_Skeleton_define.h :プラグインの各種情報を入力するヘッダ
・KW_Skeleton.cpp:処理のメインを書くソースファイル
・PiPL.r:AE SDK専用のリソースファイル

プログラムに不慣れな方は、何がなんだかわからん!と思いますが、
以降で1つずつ順に説明していきますので、ゆっくり理解していってください。

3-1. パラメータ情報を書き込む「KW_Skeleton.h」

KW_Skeleton.hは道具の準備です。
まずソースを示します。

KW_Skeleton.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// 名前:KW_Skeleton.h
// 説明:必要なSDKヘッダファイルやプラグインのパラメータ情報を決めます
// インクルードガード
#ifndef
__KW_SKELETON_H__
#define
__KW_SKELETON_H__
// 使用するSDK(※利用しないほうをコメントアウト)
#define
SDK_CS4
//#define SDK_CS5
// SDKに必要なヘッダファイル ////
#include
"AEConfig.h"
#include
"entry.h"
#if
defined(SDK_CS4)
#include
"PF_Suite_Helper.h"
#elif
defined(SDK_CS5)
#include
"PFFX_SuiteHelper.h"
#endif
#include
"PrSDKAESupport.h"
#include
"AE_Effect.h"
#include
"AE_EffectCB.h"
#include
"AE_EffectCBSuites.h"
#include
"AE_Macros.h"
#include
"AEGP_SuiteHandler.h"
#include
"String_Utils.h"
#include
"Param_Utils.h"
#include
"Smart_Utils.h"
#include
"KW_Skeleton_define.h"
#ifdef
AE_OS_WIN
#include
<Windows.h>
#endif
//////
// (構造体)受け渡すパラメータの情報
typedef struct
ParamInfo { PF_FpLong valF; } ParamInfo;
// 変更不可 ////
extern
"C"
{
// エントリポイント関数
DllExport PF_Err EntryPointFunc( PF_Cmd cmd, PF_InData *in_data, PF_OutData *out_data, PF_ParamDef *params[], PF_LayerDef *output,
void
*extra); }
////
#endif
// end of インクルードガード

KW_Skeleton.hの役割は大きく二つです。
1つはプラグインを作るのに必要な道具(SDKのヘッダ)を読み込むこと、
1つはスライダなどの"パラメータ"の値を受け渡すための入れ物(構造体)を作ることです。
前者はほとんど決まりごとで編集の必要はありません。
ですから後者のパラメータを受け渡す入れ物を編集するのがここでのお仕事になります。

編集する入れ物はソースコードの下部、ParamInfoという構造体です。
構造体とは複数の値(変数)を1つのパッケージにして保管できる変数がたくさん入る箱です。
ここに作るプラグインに必要な分だけ変数を入れます。
今回はスライダを1個つけます。そのスライダの値(小数)を保管したいので、
PF_FpLong valFという小数を格納できる変数を追加しました。

3-2. プラグインの情報を書くKW_Skeleton_define.h

KW_Skeleton_define.hは、プラグインの基本情報をまとめて記述する場所です。
通常のサンプルではKW_Skeleton.hにまとめられているのですが、分けました。
理由はこの後に説明するPiPL.rの記述が楽になるため、このような形式にしました。

KW_Skeleton_define.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 名前:KW_Skeleton_define.h
// 説明:プラグインの基本情報を入力します
// インクルードガード
#ifndef
__KW_SKELETON_DEFINE_H__
#define
__KW_SKELETON_DEFINE_H__
//// プラグインの基本情報 ////
#define
DESCRIPTION
"\n Copyright(c) KengoLab, some rights reserved."
/* プラグインの説明 */
#define
APP_CATEGORY
"KW_Sample"
/* プラグインのカテゴリ名 */
#define
NAME
"KW_Skeleton"
/* プラグインの名前 */
//// プラグインのバージョン情報 ////
#define
MAJOR_VERSION 3
/* プラグインのメジャーバージョン */
#define
MINOR_VERSION 0
/* プラグインのマイナーバージョン */
#define
BUG_VERSION 0
/* プラグインのバグバージョン */
#define
STAGE_VERSION PF_Stage_DEVELOP
/* 現在の開発進行状況 */
#define
BUILD_VERSION 1
/* ビルドバージョン */
#endif
// end of インクルードガード

ここで編集するのは、ひとまずプラグインのカテゴリ名、プラグインの名前、プラグインの説明だけで充分です。
他の値を編集すると同時にPiPL.rの編集が必要になるためです。

3-3. 実際の処理を書くKW_Skeleton.cpp

プラグイン制作の"本丸"、ほとんどの処理はKW_Skeleton.cppに書きます。
長いですが、まずはソースを示し、以下で処理の流れを説明していきます。

KW_Skeleton.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
// 名前:KW_Skeleton.cpp
// 説明:処理の本体です
// ヘッダファイルの読み込み
#include
"KW_Skeleton.h"
// (関数)プラグインの情報を表示する
static
PF_Err About(PF_InData *in_data, PF_OutData *out_data, PF_ParamDef *params[], PF_LayerDef *output){
// プラグインの情報を表示(プラグインの情報はヘッダファイルで設定)
PF_SPRINTF( out_data->return_msg,
"%s, v%d.%d\r%s"
, NAME, MAJOR_VERSION, MINOR_VERSION, DESCRIPTION);
// エラー無し(=PF_Err_NONE)という結果を返す
return
PF_Err_NONE; }
// (関数)プラグインの初期(全体)設定を行う
static
PF_Err GlobalSetup(PF_InData *in_dataP, PF_OutData *out_data, PF_ParamDef *params[], PF_LayerDef *output){
// バージョン情報を設定する(バージョン情報はヘッダで設定)
out_data->my_version = PF_VERSION(MAJOR_VERSION, MINOR_VERSION, BUG_VERSION, STAGE_VERSION, BUILD_VERSION);
// プラグインの出力設定1(PiPL.rのAE_Effect_Global_OutFlagsと関連)
out_data->out_flags = PF_OutFlag_PIX_INDEPENDENT
// 1024
| PF_OutFlag_DEEP_COLOR_AWARE
// 33554432(16bit対応プラグインになる)
| PF_OutFlag_USE_OUTPUT_EXTENT
// 64
| PF_OutFlag_I_EXPAND_BUFFER;
// 512
//---------
//合計= 33556032 -> PiPL.rの数字を合わせる
// プラグインの出力設定2(PiPL.rのAE_Effect_Global_OutFlags2と関連)
out_data->out_flags2 = PF_OutFlag2_FLOAT_COLOR_AWARE
// 4096(32bit対応プラグインになる)
| PF_OutFlag2_PARAM_GROUP_START_COLLAPSED_FLAG
// 8
| PF_OutFlag2_SUPPORTS_SMART_RENDER
// 1024(SmartRenderを使う)
| PF_OutFlag2_SUPPORTS_QUERY_DYNAMIC_FLAGS
// 1
| PF_OutFlag2_DOESNT_NEED_EMPTY_PIXELS;
// 64
//-----
//合計= 5193 -> PiPL.rの数字を合わせる
// エラー無し(=PF_Err_NONE)という結果を返す
return
PF_Err_NONE; }
// (関数)プラグインのパラメータコントローラを設定する
static
PF_Err ParamsSetup(PF_InData *in_data, PF_OutData *out_data, PF_ParamDef *params[], PF_LayerDef *output) {
// パラメータ情報構造体
PF_ParamDef def;
// パラメータ構造体の初期化
AEFX_CLR_STRUCT(def);
int
id = 1;
// パラメータGUIのID (0にデータが入るのでidは1から始める)
// 小数スライダの作成(id=1)
PF_ADD_FLOAT_SLIDER(
"Slider"
, 0, 1.0, 0, 1.0, 1, 0.5, 2, 0, 0, id); id++;
// パラメータidを1すすめる
// パラメータが何個あるか入れる(0が画像データ,1からパラメータデータ)
// 今回は2つデータがあるので2を入れる(idを1増やしているのでid=2を代入している)
out_data->num_params = id;
return
PF_Err_NONE; }
// (関数)イテレータ8bit処理(コールバック関数)
static
PF_Err FilterImage8(A_long refcon, A_long xL, A_long yL, PF_Pixel8 *inP, PF_Pixel8 *outP){
// パラメータデータの取得
ParamInfo *niP = reinterpret_cast<ParamInfo*>(refcon);
if
(niP){ PF_FpLong grey = (inP->red + inP->green + inP->blue) / 3;
// グレーを計算
PF_FpLong alp = niP->valF;
// スライダによる混合率
outP->alpha = inP->alpha;
// アルファ値
outP->red = (A_u_char)(inP->red * (1.0-alp) + grey * alp);
// 元のR値とグレーを混合
outP->green = (A_u_char)(inP->green * (1.0-alp) + grey * alp);
// 元のG値とグレーを混合
outP->blue = (A_u_char)(inP->blue * (1.0-alp) + grey * alp);
// 元のB値とグレーを混合
}
return
PF_Err_NONE; }
// (関数))イテレータ16bit処理(コールバック関数)
static
PF_Err FilterImage16(A_long refcon, A_long xL, A_long yL, PF_Pixel16 *inP, PF_Pixel16 *outP) {
// パラメータデータの取得
ParamInfo *niP = reinterpret_cast<ParamInfo*>(refcon);
if
(niP){ PF_FpLong grey = (inP->red + inP->green + inP->blue) / 3;
// グレーを計算
PF_FpLong alp = niP->valF;
// スライダによる混合率
outP->alpha = inP->alpha;
// アルファ値
outP->red = (A_u_short)(inP->red * (1.0-alp) + grey * alp);
// 元のR値とグレーを混合
outP->green = (A_u_short)(inP->green * (1.0-alp) + grey * alp);
// 元のG値とグレーを混合
outP->blue = (A_u_short)(inP->blue * (1.0-alp) + grey * alp);
// 元のB値とグレーを混合
}
return
PF_Err_NONE; }
// (関数))イテレータ32bit処理(コールバック関数)
static
PF_Err FilterImage32(A_long refcon, A_long xL, A_long yL, PF_PixelFloat *inP, PF_PixelFloat *outP) {
// パラメータデータの取得
ParamInfo *niP = reinterpret_cast<ParamInfo*>(refcon);
if
(niP){ PF_FpLong grey = (inP->red + inP->green + inP->blue) / 3;
// グレーを計算
PF_FpLong alp = niP->valF;
// スライダによる混合率
outP->alpha = inP->alpha;
// アルファ値
outP->red = (A_FpShort)(inP->red * (1.0-alp) + grey * alp);
// 元のR値とグレーを混合
outP->green = (A_FpShort)(inP->green * (1.0-alp) + grey * alp);
// 元のG値とグレーを混合
outP->blue = (A_FpShort)(inP->blue * (1.0-alp) + grey * alp);
// 元のB値とグレーを混合
}
return
PF_Err_NONE; }
// (関数)レンダ処理(処理のメイン : AE6.5)
static
PF_Err Render(PF_InData *in_dataP, PF_OutData *out_data, PF_ParamDef *params[], PF_LayerDef *output){ PF_Err err = PF_Err_NONE;
// エラー情報
A_short linesS = output->extent_hint.bottom - output->extent_hint.top;
// 処理の縦ライン数
PF_Boolean bDeep = PF_WORLD_IS_DEEP(output);
// 16bitか否か
// パラメータ情報の取得
ParamInfo niP; AEFX_CLR_STRUCT(niP); niP.valF = params[1]->u.fs_d.value;
// スライダ(id=1)の値を取得
// スイート情報の取得
AEGP_SuiteHandler suites(in_dataP->pica_basicP);
if
(bDeep){
// イテレータを呼び出し(16bit処理)
ERR(suites.Iterate16Suite1()->iterate(in_dataP, 0, linesS, &params[0]->u.ld, NULL, (
long
)&niP, FilterImage16, output)); }
else
{
// イテレータを呼び出し(8bit処理)
ERR(suites.Iterate8Suite1()->iterate(in_dataP, 0, linesS, &params[0]->u.ld, NULL, (
long
)&niP, FilterImage8, output)); }
return
err; }
// (関数)スマートレンダ前の処理(主にパラメータの取得 : AE7.0~)
static
PF_Err PreRender(PF_InData *in_dataP, PF_OutData *out_dataP, PF_PreRenderExtra *extraP){ PF_Err err = PF_Err_NONE;
// エラー情報(ERRで使う)
PF_RenderRequest req = extraP->input->output_request; PF_CheckoutResult in_result;
// スイート情報の取得
AEGP_SuiteHandler suites(in_dataP->pica_basicP);
// パラメータデータの取得
PF_Handle infoH = suites.HandleSuite1()->host_new_handle(
sizeof
(ParamInfo));
if
(infoH){
// infoPに入れたパラメータ情報がSmartRenderに引き継がれる
ParamInfo* infoP = reinterpret_cast<ParamInfo*>(suites.HandleSuite1()->host_lock_handle(infoH));
if
(infoP){ PF_ParamDef param; extraP->output->pre_render_data = infoH;
// スライダ(id=1)から情報を読み取り(paramに入る)
AEFX_CLR_STRUCT(param); ERR(PF_CHECKOUT_PARAM(in_dataP, 1, in_dataP->current_time, in_dataP->time_step, in_dataP->time_scale, &param));
// エラーでなければ、infoP->valFにスライダの値(param.u.fs_d.value)を代入(SmartRenderに受け継ぎ)
if
(!err){ infoP->valF = param.u.fs_d.value; }
// 0は元データのid
ERR(extraP->cb->checkout_layer(in_dataP->effect_ref, 0, 0, &req, in_dataP->current_time, in_dataP->time_step, in_dataP->time_scale, &in_result)); UnionLRect(&in_result.result_rect, &extraP->output->result_rect); UnionLRect(&in_result.max_result_rect, &extraP->output->max_result_rect); suites.HandleSuite1()->host_unlock_handle(infoH); } }
else
{ err = PF_Err_OUT_OF_MEMORY; }
return
err; }
// (関数)スマートレンダの処理(メイン処理 : AE7.0~)
static
PF_Err SmartRender(PF_InData *in_data, PF_OutData *out_data, PF_SmartRenderExtra *extraP){ PF_Err err = PF_Err_NONE,
// エラー情報(ERRで使う)
err2 = PF_Err_NONE;
// エラー情報2(ERR2で使う)
PF_EffectWorld *input_worldP = NULL,
// 入力データのWorldデータ(※AEGP処理のとき必要)
*output_worldP = NULL;
// 出力データのWorldデータ(※AEGP処理のとき必要)
// スイート機能の取得
AEGP_SuiteHandler suites(in_data->pica_basicP);
// パラメータ情報の取得(SmartPreRenderで格納した値が取得できる)
ParamInfo *infoP = reinterpret_cast<ParamInfo*>(suites.HandleSuite1()->host_lock_handle(reinterpret_cast<PF_Handle>(extraP->input->pre_render_data)));
if
(infoP){
// 0は元データのid
ERR((extraP->cb->checkout_layer_pixels( in_data->effect_ref, 0, &input_worldP))); ERR(extraP->cb->checkout_output(in_data->effect_ref, &output_worldP)); PF_PixelFormat format = PF_PixelFormat_INVALID; PF_WorldSuite2 *wsP = NULL; ERR(AEFX_AcquireSuite(in_data, out_data, kPFWorldSuite, kPFWorldSuiteVersion2,
"Couldn't load suite."
, (void**)&wsP)); ERR(wsP->PF_GetPixelFormat(input_worldP, &format));
if
(!err){
switch
(format) {
case
PF_PixelFormat_ARGB128:
// 32bitのときの処理
// FilterImage32関数を、各ピクセルで呼び出すIterate処理を開始
ERR(suites.IterateFloatSuite1()->iterate(in_data, 0, output_worldP->height, input_worldP, NULL, (A_long)infoP, FilterImage32, output_worldP));
break
;
case
PF_PixelFormat_ARGB64:
// 16bitのときの処理
// FilterImage16関数を、各ピクセルで呼び出すIterate処理を開始
ERR(suites.Iterate16Suite1()->iterate(in_data, 0, output_worldP->height, input_worldP, NULL, (A_long)infoP, FilterImage16, output_worldP));
break
;
case
PF_PixelFormat_ARGB32:
// 8bitのときの処理
// FilterImage8関数を、各ピクセルで呼び出すIterate処理を開始
ERR(suites.Iterate8Suite1()->iterate(in_data, 0, output_worldP->height, input_worldP, NULL, (A_long)infoP, FilterImage8, output_worldP));
break
;
default
:
// その他の場合
// 処理の失敗を通知
err = PF_Err_BAD_CALLBACK_PARAM;
break
; } }
// スイート機能の解放
ERR2(AEFX_ReleaseSuite(in_data, out_data, kPFWorldSuite, kPFWorldSuiteVersion2,
"Couldn't release suite."
));
// 0は元データのID
ERR2(extraP->cb->checkin_layer_pixels(in_data->effect_ref, 0)); }
return
err; }
// (関数)エントリポイント(プログラムはここから始まる)
PF_Err EntryPointFunc(PF_Cmd cmd, PF_InData *in_dataP, PF_OutData *out_data, PF_ParamDef *params[], PF_LayerDef *output,
void
*extra){ PF_Err err = PF_Err_NONE;
// エラー情報
switch
(cmd) {
case
PF_Cmd_ABOUT:
// プラグイン情報を開いたとき
err = About(in_dataP,out_data,params,output);
break
;
case
PF_Cmd_GLOBAL_SETUP:
// プラグインを初期化するとき
err = GlobalSetup(in_dataP,out_data,params,output);
break
;
case
PF_Cmd_PARAMS_SETUP:
// パラメータ設定のとき
err = ParamsSetup(in_dataP,out_data,params,output);
break
;
case
PF_Cmd_RENDER:
// プラグインのメイン処理
err = Render(in_dataP,out_data,params,output);
break
;
case
PF_Cmd_SMART_PRE_RENDER:
// プラグインをSmartRenderで処理する場合の前処理
err = PreRender(in_dataP, out_data, (PF_PreRenderExtra*)extra);
break
;
case
PF_Cmd_SMART_RENDER:
// プラグインをSmartRenderで処理する場合のメイン処理
err = SmartRender(in_dataP, out_data, (PF_SmartRenderExtra*)extra);
break
; }
return
err; }

突然250行ものプログラムコードが示されて、驚かれるかと思います。
しかし順に追いかけていけば整理された流れになっていますので、ひとつずつひも解いていきましょう。

■3-3-1. プラグインは「EntryPointFunc」からはじまる

プログラムが起動したとき、開始する場所をエントリポイントと呼びます。
AEプラグインのエントリポイントはEntryPointFuncです。
EntryPointFuncはコードの一番下に書いてあります(232行目)。

レイヤーにプラグインを付与する、プラグイン情報をみるなど、何か操作されるたび、
必ずEntryPointFuncが呼び出されます。このとき"ユーザがどんな操作をしたのか"という情報は、
引数のcmdに格納されているので、これを使って、
ユーザの操作に合わせた処理を呼び出すよう処理を分岐します(235~254行目)。

分岐を説明すると、以下の通りになります。
 ・PF_Cmd_ABOUT(プラグイン情報を見る)→ About関数へ
 ・PF_Cmd_GLOBAL_SETUP(プラグインを最初に使う)→ GlobalSetup関数へ
 ・PF_Cmd_PARAMS_SETUP(パラメータを設置する)→ParamsSetup関数へ
 ・PF_Cmd_RENDER(メイン処理(AE6.5))→ Render関数へ
 ・PF_Cmd_SMART_PRE_RENDER(処理前の準備(AE7.0~)) → PreRender関数へ
 ・PF_Cmd_SMART_RENDER (メイン処理(AE7.0~)) →SmartRender関数へ

全部大切な処理ですが、今回特に取り扱うものを赤字にしました。
…みていただいてわかると思いますが、全部何かの関数にジャンプしてますね。
その関数たちがEntryPointFuncの前に書いてあります。
処理を分岐したら後はお任せ、ということです。では以下でそれぞれの関数を説明します。

■3-3-2. プラグイン情報 (About)

About(8行目)の中身を変えると、プラグインの情報が表示されるダイアログの処理を変更できます。
KW_Skeletonでは表示される情報はKW_Skeleton_define.hから取ってきて自動的に変わるので、
無理に変更しなくていいです。ノンタッチで。

■3-3-3. プラグインのセットアップ(GlobalSetup)

GlobalSetup(17行目)はプラグインを初めに使用するとき、呼び出されます。
「このプラグインはこんな性格のやつだよ」という情報をAfter Effectsに伝えます。
この設定はとても重要ですが、後述するPiPL.rと連動していて扱いが難しい。
実際、よほど凝ったことをしない限りKW_Skeletonの設定で事足ります。
ひとまず華麗にスルーしましょう。

■3-3-4. パラメータのセットアップ(ParamsSetup) ※重要!

ParamsSetup(42行目)はプラグイン使用時に呼び出されます。
スライダやチェックボックスといった「パラメータ」を追加する場所です。
今回は参考例としてスライダを1つ付けています(51行目)。
スライダを追加するにはPF_ADD_FLOAT_SLIDER()を用います(51行目)。
スライダに幾つかのパラメータ設定がありますが、詳細は後日に回しますが、
ここに追記していけば、必要な分だけパラメータを追加していけるということです。

パラメータを扱う上で重要なのが「id」です。
後述するレンダ処理内で、パラメータの値を取得するとき、
どのパラメータから取るのか、指定するためにidが必要になります。

今回のサンプルコードではスライダのid = 1としています
どうやらAEではid = 0が画像データそのものを指すようなので、
パラメータのidは1から始めるのが良いんじゃないかと思います。

加えて最後にout_data->num_paramsにパラメータの(idの最大値+1)を入れます(56行目)。
これでAfter Effectsがプラグインのパラメータの数を認識してくれます。

■3-3-5. メイン処理(Render) ※重要!

Render(113行目)はプラグインのメイン処理です。処理を施すたびに何度も呼ばれます。
この関数が呼ばれるのはAfter Effects6.5です。つまりRenderは古いバージョンの処理になります。
この関数のお仕事は以下の流れになります。
 ・パラメータの値の取得
 ・処理のビット数(8bit or 16bit)を判別
 ・Suite(AEの機能を活かす道具)の準備
 ・実際に画像を処理する

まずはParamsSetupで設置したパラメータの値を取得します(119~121行目)。
このとき使うのがParamInfo、KW_Skeleton.hで定義した構造体です。
この中に用意した変数valFに、スライダ(id=1)から取得した小数を格納しています。
この値を利用して、画像処理に活かすわけです。

次に現在のコンポジションのチャンネル数が8bitなのか16bitなのかを判断します。
これにはPF_WORLD_IS_DEEP(output)を用います(116行目)。
戻り値がtrueなら16bit, falseなら8bitであると判別できます。

Suiteとは「AEの機能一式」のことです。これの準備が124行目です。
Suiteには多種多様の機能がありますので割愛しますが、画像処理の機能もあります。
それについては後述。

さて、準備は整いました。ここからが本番です。
ここにそのまま画像処理の内容を書いても良いのですが、Adobeのマニュアルによると、
Iterateを使った画像処理を推奨しているので、ここではIterateでの画像処理を紹介していきます。
Iterateを使うには、SuiteのIterate8suite, Iterate16suiteを用います。
16bitであればIterate16suite、8bitであればIterate8suiteを使うよう分岐します(126~133行目)。
そして、各iterateにFilterImage16,FilterImage8を指定します。
またパラメータの値ParamInfo niPも同時に渡すことができます。

Iterateは指定した関数(FilterImage16など)を1ピクセルずつ繰り返し呼び出し、画像全体を処理します。
よって、FilterImage16やFilterImage8に1ピクセルを編集する処理を書けばいいのです。
すると、Iterateが必要な回数だけ処理を繰り返し、画像全体を自動処理してくれます。

さて、FilterImage16などを紹介したいのですが、その前にAE7.0以降のレンダ処理を紹介します。
その後にFilterImageのイテレータ処理を一気に紹介します。

■3-3-6. スマートレンダの前処理(PreRender) ※重要!

AE7.0からSmartRenderという機能が追加されました。
GlobalSetupで「SmartRenderを使う」と設定すると、AE7.0以降ではRenderは呼ばれず、
PreRenderとSmartRenderが呼び出されるようになります。

何がスマートなのかは、実は僕にもよくわかりません(苦笑)詳しい人教えてください。
とりあえず大きな違いとして32bitチャンネル処理が扱えます
それとAE7.0以降の新しい機能が使えるのではないかと思います。

さてSmartRenderは、処理前のPreRenderと、本処理のSmartRenderに分かれています。
PreRenderでやるお仕事は以下の通りです。
・Suiteを準備
・パラメータの値を取得
・パラメータ値をParamInfo* infoPに格納し、SmartRenderに引き継ぐ

一言でいえば、パラメータをとるだけです。
注意はパラメータの取り方の記述がRenderの時と異なることです。
少しややこしくなっているので、サンプルを真似しつつ注意して編集してください。

■3-3-7. スマートレンダのメイン処理(SmartRender) ※重要!

SmartRenderを使用する場合のメインはこちらです。
準備はPreRenderで終わっていますので、やるお仕事は次の通り。
 ・Suiteを準備
 ・PreRenderで作ったParamInfoを取得
 ・なにやらSuiteでごちゃごちゃ…(※渡辺もわかってません)
 ・8bit, 16bit, 32bitでそれぞれIterate処理

Suiteを使った複雑な処理がありますが、そのあたりは真似でいいと思います。
重要なのは、202~214行目のチャンネル数ごとの分岐です。
ピクセルフォーマットが8bit, 16bit or 32bitを判別して分岐しています。
あとはRenderと同様にIterateに画像処理をお任せします。

■3-3-8. イテレータによるピクセル処理(FilterImage8, FilterImage16, FilterImage32) ※重要!

Render(AE6.5), SmartRender(AE7.0~)の双方からIterateを介して、
FilterImage8, FilterImage16, FilterImage32が呼び出されます。
それぞれが8bit, 16bit, 32bitチャンネル時のピクセル処理となります。
つまりプラグインの"天守閣"はここです
ここを書き変えれば、プラグインのフィルタ効果をさまざまなものに変えることができます。

再度確認ですが、
この関数はIterateによって"画像のピクセル分繰り返し呼ばれる"関数です。
ですから、関数の中では1ピクセル分の処理を書くだけで画面全体を処理してくれます。

ここではFilterImage8(62~76行目)を参考に処理を眺めます。
これらの関数でまず重要なのが「引数」です。材料がなんであるかを理解するのは重要です。
 ・A_long refcon :ParamInfoによるパラメータ値。キャストして使う
 ・A_long xL : 処理中のx座標 (座標値 x 65536の値)
 ・A_long yL : 処理中のy座標 (座標値 x 65536の値)
 ・PF_Pixel8 *inP : 処理座標の元のピクセルデータ(RGBA)
 ・PF_Pixel8 *outP : 処理座標の結果を入れてほしいピクセルデータ(RGBA)

つまり、パラメータや現在の(x,y)座標、そして元のピクセルデータ(inP)を用いて、
outPに計算結果を格納するのが目的となります。

inPとoutPには、(red, green, blue, alpha)の4つの値が格納されていますので、
値を1つずつ取り出しながら計算処理をしているのが66~72行目です。
そして計算結果をoutPの(red, green, blue, alpha)に格納すればピクセル処理完了です(69~72行目)

FilterImage16, FilterImage32も内容はほぼ同じなので、すぐご理解いただけるかと思います。
相違点は、inPとoutPの中にある{ red, green, blue, alpha }の値の大きさ(変数の型)が異なることです。
FilterImage8のinP,outPの値(PF_Pixel8) : 0~255
FilterImage16のinP, outPの値(PF_Pixel16) : 0~32768
FilterImage32のinP, outPの値(PF_PixelFloat) : 0.0~1.0

チャンネル数が異なると、色値の最大値最小値が変化します。
ですから渡されるinPの値もチャンネルによって変化しますし、
計算してoutPに入れなければならない値も変化しますので、注意してください。

ここまで理解できれば、簡単な画像処理フィルタはどんどん作れます。
KW_Skeletonはグレースケールフィルタでしたが、中の計算を少しアレンジしてみましょう。
意外に簡単に別のプラグインを追加できると思います。

3-4. リソース情報 PiPL.r

PiPL.rはAEプラグイン独自のリソースファイルです。
KW_Skeleton.cppのGlobalSetupの値と連動したりとなかなか面倒なシロモノです。
しかしこれが正しく書かれていないとAEはプラグインをうまく認識してくれません。

そこでKW_SkeletonではKW_Skeleton_define.hを書き換えるだけでPiPL.rには
あまり手を入れずに済むよう工夫を凝らしました。
バージョン番号などを変えてしまうと、PiPL.rを直接書き変える必要が出てくるのですが、
プラグインのカテゴリ、名前程度でしたら、PiPL.rを編集する必要はありません。

ここではPiPL.rはいじらずそのまま使うものとしましょう。
後日、より高度な内容に踏み込む機会があれば、触れていこうと思います。

4. 長くなりましたが…

KW_Skeletonを例にとった、プラグインの中身の紹介は以上です。
今後応用方法のtipsを展開していく予定ですが、基本構造は今回とほとんど同じです。

実は、今回でプラグインの基本的な仕組みがほとんど理解できたことになります。
あとは新しい機能を覚えつつ応用していけば、自由にプラグインが作れるようになるでしょう。