Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
1

Question About Function

Community Beginner ,
Aug 01, 2016 Aug 01, 2016

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!

TOPICS
SDK
1.2K
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

Engaged , Aug 08, 2016 Aug 08, 2016

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

...
Translate
Engaged ,
Aug 01, 2016 Aug 01, 2016

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.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Aug 02, 2016 Aug 02, 2016

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!

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Aug 03, 2016 Aug 03, 2016

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!

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Aug 04, 2016 Aug 04, 2016

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!

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Aug 05, 2016 Aug 05, 2016

> 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.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Aug 05, 2016 Aug 05, 2016

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

  &params[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!

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Aug 08, 2016 Aug 08, 2016

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

                                     &params[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.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Aug 08, 2016 Aug 08, 2016

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!

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Engaged ,
Aug 09, 2016 Aug 09, 2016
LATEST

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.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines