Copy link to clipboard
Copied
Dear After Effects community, I'm doing something wrong. I started with the skeleton example and changed it to do what I thought I wanted it to do, however it's not working. I changed the "MySimpleGainFunc8" and "MySimpleGainFunc16" to look like this:
MySimpleGainFunc16 (
void *refcon,
A_long xL,
A_long yL,
PF_Pixel16 *inP,
PF_Pixel16 *outP)
{
PF_Err err = PF_Err_NONE;
PF_FpLong tempR = 0;
PF_FpLong tempG = 0;
PF_FpLong tempB = 0;
outP->alpha = inP->alpha;
outP->red = inP->red + tempR * 0.5;
outP->green = inP->green + tempG * 0.5;
outP->blue = inP->blue + tempB * 0.5;
tempR = inP->red + tempR * 0.5;
tempG = inP->green + tempG * 0.5;
tempB = inP->blue + tempB * 0.5;
return err;
}
Now, what I want to happen, is for each frame, the color of each pixel in the previous frame would be averaged with the color of each respective pixel in the current frame. The function I have set up right now does not seem to be working properly. When I apply the effect to a composition, nothing happens at all. Can someone please help me to try to figure out where I'm going wrong. Thanks in advance!
I'm finding it difficult to follow what exactly is going on there, but it seems you're a bit confused as to how functions are defined, and where exactly things should go.
In your header file (formerly skeleton.h):
- Define your RenderData struct:
struct RenderData {
PF_EffectWorld *prev_frame_world;
};
(It's worth mentioning here that you can put whatever you like in this struct - whatever you might need to access in your iterate function.)
In your main file (formerly skeleton.cpp):
- Define your pi
...Copy link to clipboard
Copied
Your temp variables are declared and initialised within this function, which means that once the function has finished executing, the temp variables will be destroyed. You're initialising them with 0, and they don't get changed until after you've assigned your out pixel values, so assigning them at the end of the function does nothing (except for waste cycles 😉 )
Also this function will only iterate over a PF_EffectWorld for the current frame being rendered (it gives you the in and out pixels). To get the pixels from the previous frame I guess you'd have to include a pointer in your refcon to the PF_EffectWorld, checked out with the previous frame's pixels, then get the value using xL and yL.
Copy link to clipboard
Copied
Ok that makes total sense. What doesn't make total sense is how to get the previous frame's pixels.
Here's what my code looks like now:
static PF_Err
MySimpleGainFunc8 (
void *refcon,
A_long xL,
A_long yL,
PF_Pixel8 *inP,
PF_Pixel8 *outP,
PF_Pixel8 *outPP)
{
PF_Err err = PF_Err_NONE;
//16 BIT GAIN FUNCTION (MY FUNCTION HERE)
outP->alpha = inP->alpha;
outP->red = inP->red + outPP->red * 0.5;
outP->green = inP->green + outPP->green * 0.5;
outP->blue = inP->blue + outPP->blue * 0.5;
return err;
}
static PF_Err
PF_GET_PIXEL_DATA_8 (
PF_EffectWorld *wP,
PF_PixelPtr pP0,
PF_Pixel8 **outPP);
Of course I have separate functions for 16bpc as well. This seems to me like it should work, but when I try to build the plugin it gives me errors which look like this:
cannot initialize a parameter of type 'PF_Err (*)(void *, A_long, A_long, PF_Pixel16 *, PF_Pixel16 *)' with an lvalue of type 'PF_Err (void *, A_long, A_long, PF_Pixel16 *, PF_Pixel16 *, PF_Pixel16 *)': different number of parameters (5 vs 6)
I think I might be on the right track, but I'm not sure I'm quite understanding what you're telling me to do. If you could clear things up just a little bit and show me what you're thinking in code that would be great. But thank you for your input!
Copy link to clipboard
Copied
Well I'm not going to write your code for you but I'll try and explain 😉
Firstly your compile error is... let's start at the beginning...
Your function MySimpleGainFunc8 is called by AE's own Iterate function, part of the PF_Iterate8Suite1. You're probably doing something like this in your render function:
suites.Iterate8Suite1()->Iterate(in_data, 0, output_worldP->height, input_worldP, &rect, NULL, MySimpleGainFunc8, output_worldP);
Note how you're only passing the name of your GainFunc, not passing any parameters? This is because when you call Iterate(), AE loops through all the pixels in your input world (across multiple threads, but that's not important here) and calls MySimpleGainFunc8 itself.
The signature for the function passed into Iterate() cannot be changed, which is what you've done by adding the new parameter PF_Pixel8 *outPP. C++ compliers don't like it when you deviate from the defined function signature, and so compilation will fail.
So, first things first, ditch the new parameter and your program should compile.
However this won't solve your problem, as you still need to somehow get the previous frame's pixel into MySimpleGainFunc8.
It should be obvious by now that when you call Iterate(), which in turn calls MySimpleGainFunc8, this is only operating on the pixels of your input world, for the a single frame (which in your case will be the current frame). There's no way to access the pixels of any other frame (or layer) unless you check them out first and then somehow get them into MySimpleGainFunc8. How? Well, you should first check out the Checkout example project - this shows you how to check out a frame from another time (and layer). You'll need to use the same layer as your input, and choose a time of the current frame - 1.
As for getting it into your function, that's what the "refcon" pointer is for.
Any of the example projects that use an iterate suite function will demonstrate this, but the basics are:
- create a struct called, say, RenderData, and within add a pointer to a PF_EffectWorld, e.g.
struct RenderData {
PF_EffectWorld *prev_frame_world;
};
- Check out the previous frame of your input
- Create a new instance of RenderData and point your checked out frame to the pointer within:
RenderData render_data;
render_data.prev_frame_world = &world_prevP;
- Pass render_data into your Iterate() function (casting to void*)
Then within MySimpleGainFunc8, recast the refcon to RenderData* and access the pixel using the passed x and y coordinates (Look for Sampling Pixels At (x,y) in the guide for how to do this).
This sounds complicated but it's actually pretty straightforward!
Good luck!
Copy link to clipboard
Copied
Ok, I want to make sure I'm on the right track. Within PF_Err Render I've added this to access the pixels from the previous frame and store the info to render_data:
PF_ParamDef prevframe;
struct RenderData {
PF_ParamDef *prev_frame_world;
};
ERR(PF_CHECKOUT_PARAM( in_data,
SKELETON_GAIN,
(in_data->current_time - 1),
in_data->time_step,
in_data->time_scale,
&prevframe));
RenderData render_data;
render_data.prev_frame_world = &prevframe;
So I think that looks OK, right? Next up, I move to MySimpleGainFunction8. I replaced *refcon with *renderdata, like this:
static PF_Err
MySimpleGainFunc8 (
void *render_data,
A_long xL,
A_long yL,
PF_Pixel8 *inP,
PF_Pixel8 *outP)
Now, where I really start to get confused is when I start to have to sample the pixels. This is what they show in the SDK guide:
PF_Pixel *sampleIntegral32(PF_EffectWorld &def, int x, int y){
return (PF_Pixel*)((char*)def.data +
(y * def.rowbytes) +
(x * sizeof(PF_Pixel)));
}
When I add that directly to MySimpleFunction8, this is where things start to get hairy. I pasted it directly in like this:
MySimpleGainFunc8 (
void *render_data,
A_long xL,
A_long yL,
PF_Pixel8 *inP,
PF_Pixel8 *outP)
{
PF_Err err = PF_Err_NONE;
//16 BIT GAIN FUNCTION (MY FUNCTION HERE)
PF_Pixel *sampleIntegral32(PF_EffectWorld &def, int x, int y){
return (PF_Pixel*)((char*)def.data +
(y * def.rowbytes) +
(x * sizeof(PF_Pixel)));
}
outP->alpha = inP->alpha;
.............................continuing on....................................
This is where I'm going to need just a little more guidance (I'm sorry
)
It gives me an error saying that function definition isn't allowed here, and I also have a feeling that I'm doing something horribly wrong by just copying and pasting. So should I put this somewhere differently? Or is there something I need to add? Is this going to be sampling all the pixels, or just a small portion of them? Also, once I do work out the pixel sampling stuff, how do I apply it to my initial algorithm that looked like this:
outP->red = inP->red + [previous frame's red pixels] * 0.5
Sorry for being so needy, but this is my first time working with the After Effects SDK, or any SDK for that matter. Thanks again!
Copy link to clipboard
Copied
> So I think that looks OK, right?
Not quite. Your refcon struct RenderData needs to have a pointer to a PF_EffectWorld, not a PF_ParamDef. You need to check out the pixels from Param[0] (i.e. your input) at a given time. This code is checking out a gain slider (I assume) from the previous frame.
I pointed you in the direction of the Checkout sample project for how to do this, and I recommend you go back and look at it again.
> It gives me an error saying that function definition isn't allowed here
Yes, you can't define a function within a function in C/C++. It's not Javascript!
Define your sampleIntegral32 function outside of MySimpleGainFunc8, and declare it as static (like all the other functions).
sampleIntegral32 returns a pointer to a pixel so you'll need to do something like this in MySimpleGainFunc8:
// First recast the refcon
RenderData *render_data = reinterpret_cast<RenderData *>(refcon);
// Get the equivalent pixel from the render data world pointer, using the xL and yL parameters
PF_Pixel *p_prev_frame = sampleIntegral32(render_data->prev_frame_world, xL, yL);
// Do stuff including
// ...
outP->red = inP->red + p_prev_frame->red * 0.5;
One more thing, you should retain the naming convention *refcon for the function parameter for the above to work, otherwise you'll get a compilation error.
Copy link to clipboard
Copied
Thanks again for another response. I feel soooooo close! Also, thank you for bearing with me through all of this. I know this project might be a little over my head, but I think I can finish this.
The iterate suite function thing just wasn't working out for me. What I did looked something like this:
PF_ParamDef prevframe;
struct RenderData {
PF_EffectWorld *prev_frame_world;
};
ERR(PF_CHECKOUT_PARAM( in_data,
SKELETON_GAIN,
(in_data->current_time - 1),
in_data->time_step,
in_data->time_scale,
&prevframe));
RenderData render_data;
render_data.prev_frame_world = &prevframe;
You told me RenderData needs to have a pointer to a PF_EffectWorld instead of a PF_ParamDef. So I changed it to this:
PF_EffectWorld prevframe;
struct RenderData {
PF_EffectWorld *prev_frame_world;
};
ERR(PF_CHECKOUT_PARAM( in_data,
SKELETON_GAIN,
(in_data->current_time - 1),
in_data->time_step,
in_data->time_scale,
&prevframe));
RenderData render_data;
render_data.prev_frame_world = &prevframe;
Then this line: ERR(PF_CHECKOUT_PARAM( in_data, Gave me an error saying "Cannot initialize a parameter of type 'PF_ParamDef *' with an rvalue of type 'PF_EffectWorld *' (aka 'PF_LayerDef *')"
So I was in an infinite loop of errors, what I did, is just below where I checkout the previous frame's pixels, I found the iterate suite function, which looks like this:
ERR(suites.Iterate8Suite1()->iterate( in_data,
0, // progress base
linesL, // progress final
¶ms[SKELETON_INPUT]->u.ld, // src
NULL, // area - null for all pixels
(void*)&refcon, // refcon - your custom data pointer
MySimpleGainFunc8, // pixel function pointer
output));
And changed (void*)&refcon, to (void*)&prevframe. I also removed these two lines from just beneath where I get the pixels from the previous frame:
RenderData render_data;
render_data.prev_frame_world = &prevframe;
Going back up to MySimpleGainFunc8, I changed a few things up there, this is what that looks like now:
MySimpleGainFunc8 (
void *prevframe, //CHANGE HERE
A_long xL,
A_long yL,
PF_Pixel8 *inP,
PF_Pixel8 *outP)
{
PF_Err err = PF_Err_NONE;
//16 BIT GAIN FUNCTION (MY FUNCTION HERE)
RenderData *render_data = reinterpret_cast<RenderData *>(prevframe); //CHANGE HERE
PF_Pixel *p_prev_frame = sampleIntegral32(render_data->prev_frame_world, xL, yL); //CHANGE ALSO HERE
outP->alpha = inP->alpha;
.........Continuing on............
Now I start encountering errors. Above MySimpleGainFunc8 I added a couple of lines of code to declare sampleIntegral32 as a static:
static PF_Err
sampleIntegral32;
But then this line of code [PF_Pixel *p_prev_frame = sampleIntegral32(render_data->prev_frame_world, xL, yL);] gives me an error saying "Called object type 'PF_Err' (aka 'int') is not a function or function pointer"
So that's where I am. I hope I'm really close to finishing. Thanks again for putting up with me!
Copy link to clipboard
Copied
I'm finding it difficult to follow what exactly is going on there, but it seems you're a bit confused as to how functions are defined, and where exactly things should go.
In your header file (formerly skeleton.h):
- Define your RenderData struct:
struct RenderData {
PF_EffectWorld *prev_frame_world;
};
(It's worth mentioning here that you can put whatever you like in this struct - whatever you might need to access in your iterate function.)
In your main file (formerly skeleton.cpp):
- Define your pixel sampler OUTSIDE of any other function:
inline PF_Pixel *sampleIntegral32(PF_EffectWorld &def, int x, int y){
return (PF_Pixel*)((char*)def.data + (y * def.rowbytes) + (x * sizeof(PF_Pixel)));
}
(Note I've used "inline" rather than "static" - this will give a hint to the compiler to inline the contents of the function directly where you call SampleIntegral32, but you can use static if you want. For some reason you'd declared it as static PF_Err, which would return a PF_Err, rather than the PF_Pixel *)
- Your iterator function(s):
MySimpleGainFunc8 (
void *refcon,
A_long xL,
A_long yL,
PF_Pixel8 *inP,
PF_Pixel8 *outP)
{
PF_Err err = PF_Err_NONE;
RenderData *render_data = reinterpret_cast<RenderData *>(prevframe);
PF_Pixel *p_prev_frame = sampleIntegral32(render_data->prev_frame_world, xL, yL);
outP->alpha = (inP->alpha + p_prev_frame->alpha) * 0.5; // I think this is what you wanted to do, right?
// etc...
return err;
}
- Your render function:
PF_EffectWorld is exactly the same thing as PF_LayerDef. You're checking out your parameter OK (although the parameter ID "SKELETON_GAIN" is probably a slider rather than the input layer. You can use 0 (zero) here, as the first parameter is always the input PF_EffectWorld, but reading on, the first param has the enum SKELETON_INPUT, so we'll use that.
PF_ParamDef prevframe = {};
ERR(PF_CHECKOUT_PARAM(in_data,
SKELETON_INPUT,
in_data->current_time - (1 * in_data->time_step), // I think this is right...
in_data->time_step,
in_data->time_scale,
&prevframe));
Where you're going wrong here is you're attempting to point a PF_ParamDef to a PF_EffectWorld in your render data. You need to point the layer data instead:
RenderData render_data;
render_data.prev_frame_world = &prevframe.u.ld; // Point your prev_frame_world pointer to the layer def in the union
ERR(suites.Iterate8Suite1()->iterate(in_data,
0, // progress base
output->height, // progress final. Use linesL if you've defined it as per the Skeleton example
¶ms[SKELETON_INPUT]->u.ld, // src
NULL, // area - null for all pixels
(void*)&render_data, // refcon - your custom data pointer
MySimpleGainFunc8, // pixel function pointer
output));
// Check in that parameter!
ERR2(PF_CHECKIN_PARAM(in_data, &prevframe));
And that should work I think! It's untested but the basics are there.
Copy link to clipboard
Copied
That was very helpful! Just a couple things left:
When I check in &prevframe ( ERR2(PF_CHECKIN_PARAM(in_data, &prevframe)); ) it gives me an error that says "use of undeclared identifier 'ERR2'" I can change it to ERR(PF_CHECKIN_PARAM(in_data, &prevframe)); and it doesn't give me any errors, but I'm not sure that's an OK thing to do. If not, how and where to I declare the identifier ERR2?
2nd thing, this line of code: PF_Pixel *p_prev_frame = sampleIntegral32(render_data->prev_frame_world, xL, yL); gave me an error saying: "No matching function for call to 'sampleIntegral32'" xCode suggested that I change that line of code to: PF_Pixel *p_prev_frame = sampleIntegral32(*render_data->prev_frame_world, xL, yL); Now the compiler's happy, but I don't know if after effects will be.
3rd thing, I had to change void *refcon, to: void *prevframe. If I didn't, it gave me an error saying "use of undeclared identifier 'prevframe'"
Anyhow, I made all the changes to make the compiler happy, then I loaded the plugin into after effects, and when I applied the effect to a composition, after effects itself gave me errors saying "parameter count mismatch ( 25::34 )" and also an error saying "Invalid filter ( 35::3 )
Thanks again for all the help!
Copy link to clipboard
Copied
You're using XCode, which provides very helpful compiler messages when it encounters errors. It also allows you to CMD-click to see how functions and macros (like ERR and ERR2) are defined. Try changing it back to ERR2, then following the trail to see why it is giving you the error. This is relatively simple code, and you need to start investigating the headers, the sample projects (all of them) and really digging into the guide if you're going to progress. I'd also recommend learning some C/C++ basics as it seems apparent that your coding experience lies in another language.
As for your parameter mismatch, you need to make sure that you're adding the correct number of parameters in PF_Cmd_PARAM_SETUP, to those listed in your enum in skeleton.h, e.g.
enum {
SKELETON_INPUT = 0,
SKELETON_GAIN,
NUM_PARAMS
};
So here NUM_PARAMS will equal 2, so you need to ensure you're adding the SKELETON_GAIN parameter before telling out_data that the number of parameters is 2 (the first parameter is implicit, and is always the input layer).
I'll leave it up to you to figure the rest out 😉
Please mark my previous answer as helpful at least, if not correct.
Good luck.
Get ready! An upgraded Adobe Community experience is coming in January.
Learn more