Skip to main content
Inspiring
February 25, 2024
Answered

Writing a Plugin that scales the layer applied to

  • February 25, 2024
  • 2 replies
  • 2248 views

Hi, im new to writing ae plugins and wanted to try figure out how to write a simple plugin that can scale the footage,layer etc... of what its applied to. 

 

i understand in logic of how to do it:

 

find out layer w&h in pixels

CurrentscaleX = 100;

CurrentScaleY = 100;

 

scaleP.ScaleMultipler = params[EXAMPLE_SCALE]->u.fs_d.value / 100;     ie 101/100= 1.01 scale increase  

 

if (ScaleP ) {

 NewscaleX = CurrentScaleX * Scale Multiplier; 

 NewscaleY = CurrentScaleY * Scale Multiplier; 

 

CurrentScaleX = NewScaleX;

CurrentScaleY = NewScaleY;

}

 

but as to writing that logic into a format the sdk can understand i have no idea. would be great if i could get a bit of help.

Correct answer Mallowed

oh gawd no! construct one matrix and do the transormation only once. both for performance sake and for not re-sampling the image 3 times which will blur your image in a nasty way.

 

you're putting the correct stuff in the correct places in the matrix, but these operations need to be multiplied as whole matrices for each of the transformation operations, and not multiplying the separate components in the matrix.

lookup matrix multiplication. you'll find a function in no time.

 

but here's some shortcuts before going on this adventure:

the default matirx which does nothing is rederred to as "identity mtrix". it's filled as follows:

1, 0, 0,

0, 1, 0,

0, 0, 1,

use that as the base for each of your separate transform, scale and rotate matrices, and then multiply them.

 

2. matrix operations happen relative to the top left corner of the image. if you want it relative to the center you need to first create an transpose matrix for half the width and height.

so what you'd actually want to do to construct a proper transformation matrix is:

1. create a transpose matrix for negative the anchor point value. that moves the image's pivot point to the matrix's 0,0 coordinate.

2. multiply that matrix by the scale matrix, which will now happen around the anchor point.

3. multiply the previous result by the rotation marix (again, will happen aroung the expected point)

4. multiply the previous result by another transform matrix with the x/y position values.

that's it. your matrix is ready to use.

when going more advanced, you'll want to factor in pixel aspext ration as well. it would just mean doing a scaling matrix on x and the begining and end steps.


I cannot thank you enough for all the help through this. i have the image scaling through the slider now without issue, i need to make it scale from the center of the layer but im sure i can figure out the rest myself, i cannot thank you enough through all of this. you are a legend,

 

Mal

2 replies

Community Expert
February 25, 2024

generally speaking, a plug-in receives an input buffer with pixels to mnipulate, and an output buffer to put the results into. *how* the output buffer is filled is entirely up to you. AE doesn't care.

so to scale your image you could use your won algorithm to shuffle pixels around, use 3rd party libraries, OR use some of the tools supplied by AE's API.

for example, you could use transform_world() from PF_WorldTransformSuite1 to transpose, scale to rotate an image.

here's a thread talking aobut it:
https://community.adobe.com/t5/after-effects-discussions/help-with-transform-world/m-p/1840909

search the forum for more info, this function has been talked about here on numerous occasions.

MallowedAuthor
Inspiring
February 25, 2024

thank you for this, i saw in the sdk documentation about transform_world() but didnt know how to execute it. do just code it directly into the render section of the cpp file or do i use a err(suites.pf_worldtransfomsuite1 )-> and pass it through to a func8?

 

sorry if this seems like a silly question but still very new to this 

Community Expert
February 26, 2024

i feel like i should give context as to what i am trying to do,

i created this expression within after effects:

 

var rotRight = effect("Right Amplitude (A)")("Slider");
var rotLeft = effect("Left Amplitude (B)")("Slider");
var shakeDirection;
var scaleClamp = effect("Detail-preserving Upscale")("Scale");
var minScale = effect("Scale Min Clamp ")("Slider");
var maxScale = effect("Scale Max Clamp")("Slider");
var nonClamp = effect("Amplitude")("Slider");
if (effect("Shake Logic")("Menu").value ==1) {
if (effect("Shake Direction Bias")("Menu").value ==1) {
var seedGen = effect("Seed")("Slider");
seedRandom(seedGen, true);
var shakeBias = random();
if (shakeBias < 0.5) {
shakeDirection = rotLeft;
} else {
shakeDirection = rotRight;
}
} else if (effect("Shake Direction Bias")("Menu").value ==2) {
shakeDirection = rotLeft;
} else if (effect("Shake Direction Bias")("Menu").value ==3) {
shakeDirection = rotRight;
}

// shake logic calc

if (effect("Clamped to Scale?")("Checkbox") == true) {
var LinearRotation = linear(scaleClamp, minScale, maxScale, 0, shakeDirection);
var noiseRotation = noise(time + scaleClamp * (effect("Frequency")("Slider")));
var easedRotation = ease(scaleClamp, minScale, maxScale, 0, 1);
var finalRotation = LinearRotation * noiseRotation * easedRotation;
rotation = finalRotation*effect("Dissolve Multiplier")("Slider");
} else {
var LinearRotation = linear(nonClamp, minScale, maxScale, 0, shakeDirection);
var noiseRotation = noise(time + scaleClamp * (effect("Frequency")("Slider")));
var easedRotation = ease(nonClamp, minScale, maxScale, 0, 1);
var finalRotation = LinearRotation * noiseRotation * easedRotation;
rotation = finalRotation*effect("Dissolve Multiplier")("Slider");
}

} else {
// redundant code because expressions have limitations
var seedGen = effect("Seed")("Slider");
seedRandom(seedGen, true);
var shakeBias;
if (shakeBias < 0.5) {
shakeDirection = rotLeft;
} else {
shakeDirection = rotRight;
}
var sinwaveFrequency;
var sinwaveAmp;
if (effect("Use A/B values instead")("Checkbox") == true) {
sinwaveFrequency = effect("Frequency")("Slider");
sinwaveAmp = shakeDirection;
} else {
sinwaveFrequency = effect("Sin Wave Frequency")("Slider");
sinwaveAmp = effect("Sin Wave Tilt Amplitude")("Slider");
}
shakeDirection = Math.sin((time-effect("seed")("slider"))*effect("Sin Wave Frequency")("slider"))*sinwaveAmp*effect("Amplitude")("Slider")

if (effect("Clamped to Scale?")("Checkbox") == true) {
var LinearRotation = linear(scaleClamp, minScale, maxScale, 0, sinwaveAmp);
var noiseRotation = noise(time + scaleClamp * sinwaveFrequency);
var easedRotation = ease(scaleClamp, minScale, maxScale, 0, 1);
var finalRotation = LinearRotation * noiseRotation * easedRotation;
rotation = finalRotation*effect("Dissolve Multiplier")("Slider");
} else {
var LinearRotation = linear(nonClamp, minScale, maxScale, 0, sinwaveAmp);
var noiseRotation = noise(time + scaleClamp * sinwaveFrequency);
var easedRotation = ease(nonClamp, minScale, maxScale, 0, 1);
var finalRotation = LinearRotation * noiseRotation * easedRotation;
rotation = finalRotation*effect("Dissolve Multiplier")("Slider");
}
}

 

but i wanted to see if i could write it as plugin as i wanted to add more to it and wanted to clean it up as it looks horridly messy with not being able to have drop down sub menus to tidy elements


i see.

let's tell a few things apart:

1. the means of determining what the transformation should be like at a given time.

2. putting all the transformation info into a matrix.

3. transforming the input image with said matrix.

 

so:

1. that's pretty much a translation of your expression to c++, ending up with x/y position, rotation and x/y scaling values for a given time. i trust you don't have an issue there.

 

2. why do you need a matrix? because rendering a transformation is no trivial manner which requires a TON different algorithms for sampling the image when upsclaed and downscaled, and a TON of optimizations to run it fast. luckily, the AE api offers you transform_world which accepts a matrix and does the rest. that's why you need a matrix. you need to read up on transformation matrices and how to construct them. but for testing sake, here's how to fill the PF_FloatMatrix to downscale the image by half:

0.5, 0, 0,

0, 0.5, 0,

0, 0, 1

matrices are kind of magical. you can concatenate multiple transformations into one matrix, and when multiplying an input point's x/y coordinates (a vector) by the matrix, you magically get the x/y coordinates of where it should appear on the output buffer.

 

3. now that you have a matrix contaning all of your transformation decisions put together, you can call transform_world with an input buffer to take pixels from, output buffer to put pixels to, and the matirx with all your transformation. transform_world does not talk to any other functions. all it does is move the pixels from the input to the output according to the matrix.