Skip to main content
Inspiring
July 2, 2025
Answered

Struggling to understand extent_hint and image buffer

  • July 2, 2025
  • 1 reply
  • 384 views

I am currently creating an Premiere Pro plugin using the After Effects C++ SDK. I created a simple Tinter plugin that tints all pixels in my clip. To iterate on all of the pixels, I am using the iterate function.

 

My problem

The plugin worked well, but when I applied it to a clip that had a smaller resolution (1920 x 1080) than my

sequence global resolution (3840 x 2160), I realized that the effect was applied to all the sequence's pixels, and not just my clip's pixels. I don't understand why, since I didn't neither manipulate the output size nor used the PF_OutFlag_I_EXPAND_BUFFER flag.

 

What I have checked so far

Using breakpoints and the debugger, I verified that no variable (in_dataout_dataoutput, all of the extent_hint variables) was containing informations about the sequence resolution. The only time where the sequence resolution was visible was within the iterate function, when looking at the x and y coordinates, but I have no idea as up to where it comes from...

 

I tried to manipulate the area parameter of the iterate function, but none of the followings solved my issue :

  • Using NULL
  • Using &params[PLUGIN_INPUT]->u.ld.extent_hint
  • Using &output->extent_hint

 

I have found a workaround by looking at Nate's pixel by pixel control video, but this is just ignoring the transparent pixels. It does not prevent the effect from iterating over the entire sequence, which seems like a massive performance issue, aspecially for 4K resolution scenes...

 

I tried to force a value to the output buffer size in the FrameSetup sequence, like you would normally do if you wanted to expand the buffer, but that didn't work as well...

 

Any help would be greatly appreciated !

My code

#include "plugin.h"

static PF_Err 
About (	
	PF_InData		*in_data,
	PF_OutData		*out_data,
	PF_ParamDef		*params[],
	PF_LayerDef		*output )
{
	AEGP_SuiteHandler suites(in_data->pica_basicP);

	suites.ANSICallbacksSuite1()->sprintf(	out_data->return_msg,
											"%s v%d.%d\r%s",
											STR(StrID_Name), 
											MAJOR_VERSION, 
											MINOR_VERSION, 
											STR(StrID_Description));
	return PF_Err_NONE;
}

static PF_Err 
GlobalSetup (	
	PF_InData		*in_data,
	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);

	out_data->out_flags =	PF_OutFlag_DEEP_COLOR_AWARE ; // just 16bpc, not 32bpc
	
	return PF_Err_NONE;
}

static PF_Err 
FrameSetup (	
	PF_InData		*in_data,
	PF_OutData		*out_data,
	PF_ParamDef		*params[],
	PF_LayerDef		*output )
{
	// out_data->width = in_data->width;
	// out_data-> height = in_data->height;
	// output->extent_hint = params[PLUGIN_INPUT]->u.ld.extent_hint;
	return PF_Err_NONE;
}

static PF_Err 
ParamsSetup (	
	PF_InData		*in_data,
	PF_OutData		*out_data,
	PF_ParamDef		*params[],
	PF_LayerDef		*output )
{
	PF_Err err = PF_Err_NONE;
	PF_ParamDef	def;

	AEFX_CLR_STRUCT(def);

	PF_ADD_FLOAT_SLIDERX(
		STR(StrID_Ratio_Param_Name),
		TINTER_RATIO_MIN,
		TINTER_RATIO_MAX,
		TINTER_RATIO_MIN,
		TINTER_RATIO_MAX,
		TINTER_RATIO_DFLT,
		PF_Precision_TENTHS,
		0,
		0,
		RATIO_DISK_ID);

	AEFX_CLR_STRUCT(def);

	PF_ADD_COLOR(
		STR(StrID_Color_Param_Name),
		128,
		255,
		255,
		COLOR_DISK_ID
	);
	AEFX_CLR_STRUCT(def);
	
	out_data->num_params = PLUGIN_NUM_PARAMS;

	return err;
}

PF_Err TintImage8(void *refcon, A_long x, A_long y, PF_Pixel *inPixel, PF_Pixel *outPixel) {
	if (inPixel->alpha == 0) {
		return PF_Err_NONE;
	}

	SettingsInfo* effectSettings = (SettingsInfo*)refcon;

	*outPixel = *inPixel;

	plugin::tint::tint_pixel(
		*(reinterpret_cast<plugin::tint::rgba_pixel*>(&effectSettings->color)),
		reinterpret_cast<plugin::tint::rgba_pixel*>(outPixel),
		effectSettings->ratio
	);

	return PF_Err_NONE;
}

static PF_Err 
Render (
	PF_InData		*in_data,
	PF_OutData		*out_data,
	PF_ParamDef		*params[],
	PF_LayerDef		*output )
{
	PF_Err				err		= PF_Err_NONE;
	SettingsInfo effectSettings;
	AEFX_CLR_STRUCT(effectSettings);
	effectSettings.color = params[TINTER_COLOR]->u.cd.value;
	effectSettings.ratio = static_cast<float>(params[TINTER_RATIO]->u.fs_d.value / 100);

	const int lines = output->extent_hint.bottom - output->extent_hint.top;

	AEGP_SuiteHandler suites(in_data->pica_basicP);
	ERR(suites.Iterate8Suite2()->iterate(
		in_data,
		0,
		lines,
		&params[PLUGIN_INPUT]->u.ld,
		&output->extent_hint,
		(void *)&effectSettings,
		TintImage8,
		output
	));

	return err;
}


extern "C" DllExport
PF_Err PluginDataEntryFunction2(
	PF_PluginDataPtr inPtr,
	PF_PluginDataCB2 inPluginDataCallBackPtr,
	SPBasicSuite* inSPBasicSuitePtr,
	const char* inHostName,
	const char* inHostVersion)
{
	PF_Err result = PF_Err_INVALID_CALLBACK;

	result = PF_REGISTER_EFFECT_EXT2(
		inPtr,
		inPluginDataCallBackPtr,
		"Tinter", // Name
		"ADBE Tinter", // Match Name
		"Anatole", // Category
		AE_RESERVED_INFO, // Reserved Info
		"EffectMain",	// Entry point
		"https://www.adobe.com");	// support URL

	return result;
}


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_FRAME_SETUP:

				err = FrameSetup(	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;
}

 

Correct answer anatole_0732

Final update

I solved the background issue by iterating on the output instead of the input. I copy the input frame to the output before the iteration. Here is the final code :

static PF_Err 
Render (
    PF_InData        *in_data,
    PF_OutData        *out_data,
    PF_ParamDef        *params[],
    PF_LayerDef        *output )
{
    PF_Err err = PF_Err_NONE;

    SettingsInfo effectSettings;
    AEFX_CLR_STRUCT(effectSettings);
    effectSettings.color = params[TINTER_COLOR]->u.cd.value;
    effectSettings.ratio = static_cast<float>(params[TINTER_RATIO]->u.fs_d.value / 100);
    
    const PF_Rect effectArea = {
        in_data->pre_effect_source_origin_x,
        in_data->pre_effect_source_origin_y,
        params[PLUGIN_INPUT]->u.ld.width - in_data->pre_effect_source_origin_x,
        params[PLUGIN_INPUT]->u.ld.height - in_data->pre_effect_source_origin_y
    };
    
    AEGP_SuiteHandler suites(in_data->pica_basicP);
    const int lines = effectArea.bottom - effectArea.top;

    PF_COPY(
        &params[PLUGIN_INPUT]->u.ld,
        output,
        NULL,
        NULL
    );

    ERR(suites.Iterate8Suite2()->iterate(
        in_data,
        0,
        lines,
        output,
        &effectArea,
        (void *)&effectSettings,
        TintImage,
        output
    ));

    return err;
}

If someone knows why this work and the original version (iteration on the input) don't, please enlight me because I am a bit confused... 

1 reply

Inspiring
July 8, 2025

Update on this post

I have managed to partially solve the issue by using an effect area (PF_Rect), with an offset using the effect pre-origin, and the image input's width and height. However, I have a jittering effect in the background of most frames. It's visible both in Debug and Release mode, as well as when I am exporting the clip.

 

static PF_Err 
Render (
    PF_InData        *in_data,
    PF_OutData        *out_data,
    PF_ParamDef        *params[],
    PF_LayerDef        *output )
{
    PF_Err                err        = PF_Err_NONE;

    SettingsInfo effectSettings;
    AEFX_CLR_STRUCT(effectSettings);

    AEGP_SuiteHandler suites(in_data->pica_basicP);

    effectSettings.color = params[TINTER_COLOR]->u.cd.value;
    effectSettings.ratio = static_cast<float>(params[TINTER_RATIO]->u.fs_d.value / 100);
        
    const PF_Rect effectArea = {
        in_data->pre_effect_source_origin_x,
        in_data->pre_effect_source_origin_y,
        params[PLUGIN_INPUT]->u.ld.width - in_data->pre_effect_source_origin_x,
        params[PLUGIN_INPUT]->u.ld.height - in_data->pre_effect_source_origin_y
    };
    const int lines = effectArea.bottom - effectArea.top;

    ERR(suites.Iterate8Suite2()->iterate(
        in_data,
        0,
        lines,
        &params[PLUGIN_INPUT]->u.ld,
        &effectArea,
        (void *)&effectSettings,
        TintImage,
        output
    ));

    return err;
}

 

What is somewhat strange is that when I pass frame one by one using the next frame button, the jitter is less frequent. And for every frame where it is visible, it disappears after a few seconds...

 

If someone has already seen this bug, I am very much interested..

Inspiring
July 10, 2025

Update

While trying many things on the plugin to see where the problem could come from, I discovered that the problem was not necessarily due to the plugin, tint or iterate functions.

When I tried to simply use the PF_FILL 
macro with the input color, I saw that the same problem was still happening with the background.

 

At this point, I have removed all flags from the effect, I just have this fill, and the effectArea rectangle, otherwise the fill is applied to all the pixel of the sequence frame (which was my initial problem). I don't even use the effectSettings structure and just pass the color parameter value's pointer directly to the macro... This is my render function :

 

static PF_Err 
Render (
    PF_InData        *in_data,
    PF_OutData        *out_data,
    PF_ParamDef        *params[],
    PF_LayerDef        *output )
{
    PF_Err err = PF_Err_NONE;

    const PF_Rect effectArea = {
        in_data->pre_effect_source_origin_x,
        in_data->pre_effect_source_origin_y,
        params[PLUGIN_INPUT]->u.ld.width - in_data->pre_effect_source_origin_x,
        params[PLUGIN_INPUT]->u.ld.height - in_data->pre_effect_source_origin_y
    };

    PF_FILL(
        &params[TINTER_COLOR]->u.cd.value,
        &effectArea,
        output
    );

    return err;
}

 I have also already cleaned the cache multiple time, but it didn't solve the issue...

anatole_0732AuthorCorrect answer
Inspiring
July 10, 2025

Final update

I solved the background issue by iterating on the output instead of the input. I copy the input frame to the output before the iteration. Here is the final code :

static PF_Err 
Render (
    PF_InData        *in_data,
    PF_OutData        *out_data,
    PF_ParamDef        *params[],
    PF_LayerDef        *output )
{
    PF_Err err = PF_Err_NONE;

    SettingsInfo effectSettings;
    AEFX_CLR_STRUCT(effectSettings);
    effectSettings.color = params[TINTER_COLOR]->u.cd.value;
    effectSettings.ratio = static_cast<float>(params[TINTER_RATIO]->u.fs_d.value / 100);
    
    const PF_Rect effectArea = {
        in_data->pre_effect_source_origin_x,
        in_data->pre_effect_source_origin_y,
        params[PLUGIN_INPUT]->u.ld.width - in_data->pre_effect_source_origin_x,
        params[PLUGIN_INPUT]->u.ld.height - in_data->pre_effect_source_origin_y
    };
    
    AEGP_SuiteHandler suites(in_data->pica_basicP);
    const int lines = effectArea.bottom - effectArea.top;

    PF_COPY(
        &params[PLUGIN_INPUT]->u.ld,
        output,
        NULL,
        NULL
    );

    ERR(suites.Iterate8Suite2()->iterate(
        in_data,
        0,
        lines,
        output,
        &effectArea,
        (void *)&effectSettings,
        TintImage,
        output
    ));

    return err;
}

If someone knows why this work and the original version (iteration on the input) don't, please enlight me because I am a bit confused...