1 /*
2  * transition_affine.c -- affine transformations
3  * Copyright (C) 2003-2021 Meltytech, LLC
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19 
20 #include <framework/mlt_transition.h>
21 #include <framework/mlt.h>
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <ctype.h>
26 #include <string.h>
27 #include <math.h>
28 #include <float.h>
29 
30 #include "interp.h"
31 
32 #define MLT_AFFINE_MAX_DIMENSION (16000)
33 
alignment_parse(char * align)34 static double alignment_parse( char* align )
35 {
36 	int ret = 0.0;
37 
38 	if ( align == NULL );
39 	else if ( isdigit( align[ 0 ] ) )
40 		ret = atoi( align );
41 	else if ( align[ 0 ] == 'c' || align[ 0 ] == 'm' )
42 		ret = 1.0;
43 	else if ( align[ 0 ] == 'r' || align[ 0 ] == 'b' )
44 		ret = 2.0;
45 
46 	return ret;
47 }
48 
repeat_position(mlt_properties properties,const char * name,mlt_position position,int length)49 static mlt_position repeat_position(mlt_properties properties, const char* name, mlt_position position, int length)
50 {
51 	// Make mlt_properties parse and refresh animation.
52 	mlt_properties_anim_get_double(properties, name, position, length);
53 	mlt_animation animation = mlt_properties_get_animation(properties, name);
54 	if (animation) {
55 		// Apply repeat and mirror options.
56 		int anim_length = mlt_animation_get_length(animation);
57 		int repeat_off = mlt_properties_get_int(properties, "repeat_off");
58 		if (!repeat_off && position >= anim_length && anim_length != 0) {
59 			int section = position / anim_length;
60 			int mirror_off = mlt_properties_get_int(properties, "mirror_off");
61 			position -= section * anim_length;
62 			if (!mirror_off && section % 2 == 1)
63 				position = anim_length - position;
64 		}
65 	}
66 	return position;
67 }
68 
anim_get_angle(mlt_properties properties,const char * name,mlt_position position,mlt_position length)69 static double anim_get_angle(mlt_properties properties, const char* name, mlt_position position, mlt_position length)
70 {
71 	double result = 0.0;
72 	if (mlt_properties_get(properties, name)) {
73 		position = repeat_position(properties, name, position, length);
74 		result = mlt_properties_anim_get_double(properties, name, position, length);
75 		if (strchr(mlt_properties_get(properties, name), '%'))
76 			result *= 360;
77 	}
78 	return result;
79 }
80 
81 /** Calculate real geometry.
82 */
83 
geometry_calculate(mlt_transition transition,const char * store,struct mlt_geometry_item_s * output,double position)84 static void geometry_calculate( mlt_transition transition, const char *store, struct mlt_geometry_item_s *output, double position )
85 {
86 	mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition );
87 	mlt_geometry geometry = mlt_properties_get_data( properties, store, NULL );
88 	int mirror_off = mlt_properties_get_int( properties, "mirror_off" );
89 	int repeat_off = mlt_properties_get_int( properties, "repeat_off" );
90 	int length = mlt_geometry_get_length( geometry );
91 
92 	// Allow wrapping
93 	if ( !repeat_off && position >= length && length != 0 )
94 	{
95 		int section = position / length;
96 		position -= section * length;
97 		if ( !mirror_off && section % 2 == 1 )
98 			position = length - position;
99 	}
100 
101 	// Fetch the key for the position
102 	mlt_geometry_fetch( geometry, output, position );
103 }
104 
105 
transition_parse_keys(mlt_transition transition,const char * name,const char * store,int normalised_width,int normalised_height)106 static mlt_geometry transition_parse_keys( mlt_transition transition, const char *name, const char *store, int normalised_width, int normalised_height )
107 {
108 	// Get the properties of the transition
109 	mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition );
110 
111 	// Try to fetch it first
112 	mlt_geometry geometry = mlt_properties_get_data( properties, store, NULL );
113 
114 	// Determine length and obtain cycle
115 	mlt_position length = mlt_transition_get_length( transition );
116 	double cycle = mlt_properties_get_double( properties, "cycle" );
117 
118 	// Allow a geometry repeat cycle
119 	if ( cycle >= 1 )
120 		length = cycle;
121 	else if ( cycle > 0 )
122 		length *= cycle;
123 
124 	if ( geometry == NULL )
125 	{
126 		// Get the new style geometry string
127 		char *property = mlt_properties_get( properties, name );
128 
129 		// Create an empty geometries object
130 		geometry = mlt_geometry_init( );
131 
132 		// Parse the geometry if we have one
133 		mlt_geometry_parse( geometry, property, length, normalised_width, normalised_height );
134 
135 		// Store it
136 		mlt_properties_set_data( properties, store, geometry, 0, ( mlt_destructor )mlt_geometry_close, NULL );
137 	}
138 	else
139 	{
140 		// Check for updates and refresh if necessary
141 		mlt_geometry_refresh( geometry, mlt_properties_get( properties, name ), length, normalised_width, normalised_height );
142 	}
143 
144 	return geometry;
145 }
146 
composite_calculate(mlt_transition transition,struct mlt_geometry_item_s * result,int nw,int nh,double position)147 static mlt_geometry composite_calculate( mlt_transition transition, struct mlt_geometry_item_s *result, int nw, int nh, double position )
148 {
149 	// Structures for geometry
150 	mlt_geometry start = transition_parse_keys( transition, "geometry", "geometries", nw, nh );
151 
152 	// Do the calculation
153 	geometry_calculate( transition, "geometries", result, position );
154 
155 	return start;
156 }
157 
158 typedef struct
159 {
160 	double matrix[3][3];
161 }
162 affine_t;
163 
affine_init(double affine[3][3])164 static void affine_init( double affine[3][3] )
165 {
166 	affine[0][0] = 1;
167 	affine[0][1] = 0;
168 	affine[0][2] = 0;
169 	affine[1][0] = 0;
170 	affine[1][1] = 1;
171 	affine[1][2] = 0;
172 	affine[2][0] = 0;
173 	affine[2][1] = 0;
174 	affine[2][2] = 1;
175 }
176 
177 // Multiply two this affine transform with that
affine_multiply(double affine[3][3],double matrix[3][3])178 static void affine_multiply( double affine[3][3], double matrix[3][3] )
179 {
180 	double output[3][3];
181 	int i;
182 	int j;
183 
184 	for ( i = 0; i < 3; i ++ )
185 		for ( j = 0; j < 3; j ++ )
186 			output[i][j] = affine[i][0] * matrix[j][0] + affine[i][1] * matrix[j][1] + affine[i][2] * matrix[j][2];
187 
188 	affine[0][0] = output[0][0];
189 	affine[0][1] = output[0][1];
190 	affine[0][2] = output[0][2];
191 	affine[1][0] = output[1][0];
192 	affine[1][1] = output[1][1];
193 	affine[1][2] = output[1][2];
194 	affine[2][0] = output[2][0];
195 	affine[2][1] = output[2][1];
196 	affine[2][2] = output[2][2];
197 }
198 
199 // Rotate by a given angle
affine_rotate_x(double affine[3][3],double angle)200 static void affine_rotate_x( double affine[3][3], double angle )
201 {
202 	double matrix[3][3];
203 	matrix[0][0] = cos( angle * M_PI / 180 );
204 	matrix[0][1] = 0 - sin( angle * M_PI / 180 );
205 	matrix[0][2] = 0;
206 	matrix[1][0] = sin( angle * M_PI / 180 );
207 	matrix[1][1] = cos( angle * M_PI / 180 );
208 	matrix[1][2] = 0;
209 	matrix[2][0] = 0;
210 	matrix[2][1] = 0;
211 	matrix[2][2] = 1;
212 	affine_multiply( affine, matrix );
213 }
214 
affine_rotate_y(double affine[3][3],double angle)215 static void affine_rotate_y( double affine[3][3], double angle )
216 {
217 	double matrix[3][3];
218 	matrix[0][0] = cos( angle * M_PI / 180 );
219 	matrix[0][1] = 0;
220 	matrix[0][2] = 0 - sin( angle * M_PI / 180 );
221 	matrix[1][0] = 0;
222 	matrix[1][1] = 1;
223 	matrix[1][2] = 0;
224 	matrix[2][0] = sin( angle * M_PI / 180 );
225 	matrix[2][1] = 0;
226 	matrix[2][2] = cos( angle * M_PI / 180 );
227 	affine_multiply( affine, matrix );
228 }
229 
affine_rotate_z(double affine[3][3],double angle)230 static void affine_rotate_z( double affine[3][3], double angle )
231 {
232 	double matrix[3][3];
233 	matrix[0][0] = 1;
234 	matrix[0][1] = 0;
235 	matrix[0][2] = 0;
236 	matrix[1][0] = 0;
237 	matrix[1][1] = cos( angle * M_PI / 180 );
238 	matrix[1][2] = sin( angle * M_PI / 180 );
239 	matrix[2][0] = 0;
240 	matrix[2][1] = - sin( angle * M_PI / 180 );
241 	matrix[2][2] = cos( angle * M_PI / 180 );
242 	affine_multiply( affine, matrix );
243 }
244 
affine_scale(double affine[3][3],double sx,double sy)245 static void affine_scale( double affine[3][3], double sx, double sy )
246 {
247 	double matrix[3][3];
248 	matrix[0][0] = sx;
249 	matrix[0][1] = 0;
250 	matrix[0][2] = 0;
251 	matrix[1][0] = 0;
252 	matrix[1][1] = sy;
253 	matrix[1][2] = 0;
254 	matrix[2][0] = 0;
255 	matrix[2][1] = 0;
256 	matrix[2][2] = 1;
257 	affine_multiply( affine, matrix );
258 }
259 
260 // Shear by a given value
affine_shear(double affine[3][3],double shear_x,double shear_y,double shear_z)261 static void affine_shear( double affine[3][3], double shear_x, double shear_y, double shear_z )
262 {
263 	double matrix[3][3];
264 	matrix[0][0] = 1;
265 	matrix[0][1] = tan( shear_x * M_PI / 180 );
266 	matrix[0][2] = 0;
267 	matrix[1][0] = tan( shear_y * M_PI / 180 );
268 	matrix[1][1] = 1;
269 	matrix[1][2] = tan( shear_z * M_PI / 180 );
270 	matrix[2][0] = 0;
271 	matrix[2][1] = 0;
272 	matrix[2][2] = 1;
273 	affine_multiply( affine, matrix );
274 }
275 
affine_offset(double affine[3][3],double x,double y)276 static void affine_offset( double affine[3][3], double x, double y )
277 {
278 	affine[0][2] += x;
279 	affine[1][2] += y;
280 }
281 
282 // Obtain the mapped x coordinate of the input
MapX(double affine[3][3],double x,double y)283 static inline double MapX( double affine[3][3], double x, double y )
284 {
285 	return affine[0][0] * x + affine[0][1] * y + affine[0][2];
286 }
287 
288 // Obtain the mapped y coordinate of the input
MapY(double affine[3][3],double x,double y)289 static inline double MapY( double affine[3][3], double x, double y )
290 {
291 	return affine[1][0] * x + affine[1][1] * y + affine[1][2];
292 }
293 
MapZ(double affine[3][3],double x,double y)294 static inline double MapZ( double affine[3][3], double x, double y )
295 {
296 	return affine[2][0] * x + affine[2][1] * y + affine[2][2];
297 }
298 
affine_max_output(double affine[3][3],double * w,double * h,double dz,double max_width,double max_height)299 static void affine_max_output( double affine[3][3], double *w, double *h, double dz, double max_width, double max_height )
300 {
301 	int tlx = MapX( affine, -max_width,  max_height ) / dz;
302 	int tly = MapY( affine, -max_width,  max_height ) / dz;
303 	int trx = MapX( affine,  max_width,  max_height ) / dz;
304 	int try = MapY( affine,  max_width,  max_height ) / dz;
305 	int blx = MapX( affine, -max_width, -max_height ) / dz;
306 	int bly = MapY( affine, -max_width, -max_height ) / dz;
307 	int brx = MapX( affine,  max_width, -max_height ) / dz;
308 	int bry = MapY( affine,  max_width, -max_height ) / dz;
309 
310 	int max_x;
311 	int max_y;
312 	int min_x;
313 	int min_y;
314 
315 	max_x = MAX( tlx, trx );
316 	max_x = MAX( max_x, blx );
317 	max_x = MAX( max_x, brx );
318 
319 	min_x = MIN( tlx, trx );
320 	min_x = MIN( min_x, blx );
321 	min_x = MIN( min_x, brx );
322 
323 	max_y = MAX( tly, try );
324 	max_y = MAX( max_y, bly );
325 	max_y = MAX( max_y, bry );
326 
327 	min_y = MIN( tly, try );
328 	min_y = MIN( min_y, bly );
329 	min_y = MIN( min_y, bry );
330 
331 	*w = ( double )( max_x - min_x + 1 ) / max_width / 2.0;
332 	*h = ( double )( max_y - min_y + 1 ) / max_height / 2.0;
333 }
334 
335 #define IN_RANGE( v, r )	( v >= - r / 2 && v < r / 2 )
336 
get_affine(affine_t * affine,mlt_transition transition,double position,int length,double scale_width,double scale_height)337 static inline void get_affine( affine_t *affine, mlt_transition transition,
338 	double position, int length, double scale_width, double scale_height )
339 {
340 	mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition );
341 	int keyed = mlt_properties_get_int( properties, "keyed" );
342 
343 	if ( keyed == 0 )
344 	{
345 		double fix_rotate_x = anim_get_angle( properties, "fix_rotate_x", position, length );
346 		double fix_rotate_y = anim_get_angle( properties, "fix_rotate_y", position, length );
347 		double fix_rotate_z = anim_get_angle( properties, "fix_rotate_z", position, length );
348 		double rotate_x = mlt_properties_get_double( properties, "rotate_x" );
349 		double rotate_y = mlt_properties_get_double( properties, "rotate_y" );
350 		double rotate_z = mlt_properties_get_double( properties, "rotate_z" );
351 		double fix_shear_x = anim_get_angle( properties, "fix_shear_x", position, length );
352 		double fix_shear_y = anim_get_angle( properties, "fix_shear_y", position, length );
353 		double fix_shear_z = anim_get_angle( properties, "fix_shear_z", position, length );
354 		double shear_x = mlt_properties_get_double( properties, "shear_x" );
355 		double shear_y = mlt_properties_get_double( properties, "shear_y" );
356 		double shear_z = mlt_properties_get_double( properties, "shear_z" );
357 		double ox = mlt_properties_anim_get_double( properties, "ox", position, length );
358 		double oy = mlt_properties_anim_get_double( properties, "oy", position, length );
359 
360 		affine_rotate_x( affine->matrix, fix_rotate_x + rotate_x * position );
361 		affine_rotate_y( affine->matrix, fix_rotate_y + rotate_y * position );
362 		affine_rotate_z( affine->matrix, fix_rotate_z + rotate_z * position );
363 		affine_shear( affine->matrix,
364 					  fix_shear_x + shear_x * position,
365 					  fix_shear_y + shear_y * position,
366 					  fix_shear_z + shear_z * position );
367 		affine_offset( affine->matrix, ox * scale_width, oy * scale_height );
368 	}
369 	else
370 	{
371 		double rotate_x = anim_get_angle(properties, "rotate_x", position, length);
372 		double rotate_y = anim_get_angle(properties, "rotate_y", position, length);
373 		double rotate_z = anim_get_angle(properties, "rotate_z", position, length);
374 		double shear_x = anim_get_angle(properties, "shear_x", position, length);
375 		double shear_y = anim_get_angle(properties, "shear_y", position, length);
376 		double shear_z = anim_get_angle(properties, "shear_z", position, length);
377 		double o_x = mlt_properties_anim_get_double(properties, "ox",
378 			repeat_position(properties, "ox", position, length), length);
379 		double o_y = mlt_properties_anim_get_double(properties, "oy",
380 			repeat_position(properties, "oy", position, length), length);
381 
382 		affine_rotate_x( affine->matrix, rotate_x );
383 		affine_rotate_y( affine->matrix, rotate_y );
384 		affine_rotate_z( affine->matrix, rotate_z );
385 		affine_shear( affine->matrix, shear_x, shear_y, shear_z );
386 		affine_offset( affine->matrix, o_x * scale_width, o_y * scale_height );
387 	}
388 }
389 
390 struct sliced_desc
391 {
392 	uint8_t *a_image, *b_image;
393 	interpp interp;
394 	affine_t affine;
395 	int a_width, a_height, b_width, b_height;
396 	double lower_x, lower_y;
397 	double dz, mix;
398 	double x_offset, y_offset;
399 	int b_alpha;
400 	double minima, xmax, ymax;
401 };
402 
sliced_proc(int id,int index,int jobs,void * cookie)403 static int sliced_proc( int id, int index, int jobs, void* cookie )
404 {
405 	(void) id; // unused
406 	struct sliced_desc ctx = *((struct sliced_desc*) cookie);
407 	int height_slice = (ctx.a_height + jobs / 2) / jobs;
408 	int starty = height_slice * index;
409 	double x, y;
410 	double dx, dy;
411 	int i, j;
412 
413 	ctx.a_image += (index * height_slice) * (ctx.a_width * 4);
414 	for (i = 0, y = ctx.lower_y; i < ctx.a_height; i++, y++) {
415 		if (i >= starty && i < (starty + height_slice)) {
416 			for (j = 0, x = ctx.lower_x; j < ctx.a_width; j++, x++) {
417 				dx = MapX( ctx.affine.matrix, x, y ) / ctx.dz + ctx.x_offset;
418 				dy = MapY( ctx.affine.matrix, x, y ) / ctx.dz + ctx.y_offset;
419 				if (dx >= ctx.minima && dx <= ctx.xmax && dy >= ctx.minima && dy <= ctx.ymax)
420 					ctx.interp(ctx.b_image, ctx.b_width, ctx.b_height, dx, dy, ctx.mix, ctx.a_image, ctx.b_alpha);
421 				ctx.a_image += 4;
422 			}
423 		}
424 	}
425 	return 0;
426 }
427 
428 /** Get the image.
429 */
430 
transition_get_image(mlt_frame a_frame,uint8_t ** image,mlt_image_format * format,int * width,int * height,int writable)431 static int transition_get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
432 {
433 	// Get the b frame from the stack
434 	mlt_frame b_frame = mlt_frame_pop_frame( a_frame );
435 
436 	// Get the transition object
437 	mlt_transition transition = mlt_frame_pop_service( a_frame );
438 
439 	// Get the properties of the transition
440 	mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition );
441 
442 	// Get the properties of the a frame
443 	mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame );
444 
445 	// Get the properties of the b frame
446 	mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
447 
448 	// Image, format, width, height and image for the b frame
449 	uint8_t *b_image = NULL;
450 	mlt_image_format b_format = mlt_image_rgb24a;
451 	int b_width = mlt_properties_get_int( b_props, "meta.media.width" );
452 	int b_height = mlt_properties_get_int( b_props, "meta.media.height" );
453 	double b_ar = mlt_frame_get_aspect_ratio( b_frame );
454 	double b_dar = b_ar * b_width / b_height;
455 
456 	// Assign the current position
457 	mlt_position position =  mlt_transition_get_position( transition, a_frame );
458 
459 	int mirror = mlt_properties_get_position( properties, "mirror" );
460 	int length = mlt_transition_get_length( transition );
461 	if ( mlt_properties_get_int( properties, "always_active" ) )
462 	{
463 		mlt_properties props = mlt_properties_get_data( b_props, "_producer", NULL );
464 		mlt_position in = mlt_properties_get_int( props, "in" );
465 		mlt_position out = mlt_properties_get_int( props, "out" );
466 		length = out - in + 1;
467 	}
468 
469 	// Obtain the normalised width and height from the a_frame
470 	mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( transition ) );
471 	int normalised_width = profile->width;
472 	int normalised_height = profile->height;
473 	double consumer_ar = mlt_profile_sar( profile );
474 
475 	if ( mirror && position > length / 2 )
476 		position = abs( position - length );
477 
478 	// Preview scaling is not working correctly when offsets are active. I have
479 	// not figured out the math; so, disable preview scaling for now.
480 	double ox = mlt_properties_anim_get_double( properties, "ox", position, length );
481 	double oy = mlt_properties_anim_get_double( properties, "oy", position, length );
482 	if (ox != 0.0 || oy != 0.0) {
483 		*width = normalised_width;
484 		*height = normalised_height;
485 	}
486 
487 	// Fetch the a frame image
488 	*format = mlt_image_rgb24a;
489 	int error = mlt_frame_get_image( a_frame, image, format, width, height, 1 );
490 	if (error || !image)
491 		return error;
492 
493 	// Calculate the region now
494 	double scale_width = mlt_profile_scale_width(profile, *width);
495 	double scale_height = mlt_profile_scale_height(profile, *height);
496 	mlt_rect result = {0, 0, normalised_width, normalised_height, 1.0};
497 
498 	mlt_service_lock( MLT_TRANSITION_SERVICE( transition ) );
499 
500 	if (mlt_properties_get(properties, "geometry"))
501 	{
502 		// Structures for geometry
503 		struct mlt_geometry_item_s geometry;
504 		composite_calculate( transition, &geometry, normalised_width, normalised_height, ( double )position );
505 		result.x = geometry.x;
506 		result.y = geometry.y;
507 		result.w = geometry.w;
508 		result.h = geometry.h;
509 		result.o = geometry.mix / 100.0f;
510 	}
511 	else if (mlt_properties_get(properties, "rect"))
512 	{
513 		// Determine length and obtain cycle
514 		double cycle = mlt_properties_get_double( properties, "cycle" );
515 
516 		// Allow a repeat cycle
517 		if ( cycle >= 1 )
518 			length = cycle;
519 		else if ( cycle > 0 )
520 			length *= cycle;
521 
522 		mlt_position anim_pos = repeat_position(properties, "rect", position, length);
523 		result = mlt_properties_anim_get_rect(properties, "rect", anim_pos, length);
524 		if (mlt_properties_get(properties, "rect") && strchr(mlt_properties_get(properties, "rect"), '%')) {
525 			result.x *= normalised_width;
526 			result.y *= normalised_height;
527 			result.w *= normalised_width;
528 			result.h *= normalised_height;
529 		}
530 		result.o = (result.o == DBL_MIN)? 1.0 : MIN(result.o, 1.0);
531 	}
532 
533 	int threads = mlt_properties_get_int(properties, "threads");
534 	threads = CLAMP(threads, 0, mlt_slices_count_normal());
535 	if (threads == 1)
536 		mlt_service_unlock( MLT_TRANSITION_SERVICE( transition ) );
537 
538 	result.x *= scale_width;
539 	result.y *= scale_height;
540 	result.w *= scale_width;
541 	result.h *= scale_height;
542 
543 	double geometry_w = result.w;
544 	double geometry_h = result.h;
545 	int fill = mlt_properties_get_int( properties, "fill" );
546 	int distort = mlt_properties_get_int( properties, "distort" );
547 
548 	if ( !fill )
549 	{
550 		double geometry_dar = result.w * consumer_ar / result.h;
551 
552 		if ( b_dar > geometry_dar )
553 		{
554 			result.w = MIN( result.w, b_width * b_ar / consumer_ar );
555 			result.h = result.w * consumer_ar / b_dar;
556 		}
557 		else
558 		{
559 			result.h = MIN( result.h, b_height );
560 			result.w = result.h * b_dar / consumer_ar;
561 		}
562 	}
563 
564 	// Fetch the b frame image
565 	if (scale_width != 1.0 || scale_height != 1.0) {
566 			// Scale request of b frame image to consumer scale maintaining its aspect ratio.
567 			b_height = CLAMP(*height, 1, MLT_AFFINE_MAX_DIMENSION);
568 			b_width  = MAX(b_height * b_dar / b_ar, 1);
569 			if (b_width > MLT_AFFINE_MAX_DIMENSION) {
570 				b_width  = CLAMP(*width, 1, MLT_AFFINE_MAX_DIMENSION);
571 				b_height = MAX(b_width * b_ar / b_dar, 1);
572 			}
573 			// Set the rescale interpolation to match the frame
574 			mlt_properties_set( b_props, "rescale.interp", mlt_properties_get( a_props, "rescale.interp" ) );
575 			// Disable padding (resize filter)
576 			mlt_properties_set_int( b_props, "distort", 1 );
577 	} else if (!mlt_properties_get_int(b_props, "interpolation_not_required")
578 			   && (fill || distort || b_width > result.w || b_height > result.h
579 				   || mlt_properties_get_int(properties, "b_scaled") || mlt_properties_get_int(b_props, "always_scale"))) {
580 		// Request b frame image scaled to what is needed.
581 		b_height = CLAMP(result.h, 1, MLT_AFFINE_MAX_DIMENSION);
582 		b_width  = MAX(b_height * b_dar / b_ar, 1);
583 		if (b_width > MLT_AFFINE_MAX_DIMENSION) {
584 			b_width  = CLAMP(result.w, 1, MLT_AFFINE_MAX_DIMENSION);
585 			b_height = MAX(b_width * b_ar / b_dar, 1);
586 		}
587 		// Set the rescale interpolation to match the frame
588 		mlt_properties_set( b_props, "rescale.interp", mlt_properties_get( a_props, "rescale.interp" ) );
589 		// Disable padding (resize filter)
590 		mlt_properties_set_int( b_props, "distort", 1 );
591 	} else {
592 		// Request at resolution of b frame image. This only happens when not using fill or distort mode
593 		// and the image is smaller than the rect with the intention to prevent scaling of the
594 		// image and merely position and possibly transform.
595 		mlt_properties_set_int( b_props, "rescale_width", b_width );
596 		mlt_properties_set_int( b_props, "rescale_height", b_height );
597 
598 		const char* b_resource = mlt_properties_get(
599 			MLT_PRODUCER_PROPERTIES(mlt_frame_get_original_producer(b_frame)), "resource");
600 		// Check if we are applied as a filter inside a transition
601 		if (b_resource && !strcmp("<track>", b_resource)) {
602 			// Set the rescale interpolation to match the frame
603 			mlt_properties_set( b_props, "rescale.interp", mlt_properties_get( a_props, "rescale.interp" ) );
604 		} else {
605 			// Suppress padding and aspect normalization.
606 			mlt_properties_set( b_props, "rescale.interp", "none" );
607 		}
608 	}
609 	mlt_log_debug(MLT_TRANSITION_SERVICE(transition), "requesting image B at resolution %dx%d\n", b_width, b_height);
610 
611 	// This is not a field-aware transform.
612 	mlt_properties_set_int( b_props, "consumer_deinterlace", 1 );
613 
614 	error = mlt_frame_get_image( b_frame, &b_image, &b_format, &b_width, &b_height, 0 );
615 	if (error || !b_image) {
616 		// Remove potentially large image on the B frame.
617 		mlt_frame_set_image( b_frame, NULL, 0, NULL );
618 		if (threads != 1)
619 			mlt_service_unlock( MLT_TRANSITION_SERVICE( transition ) );
620 		return error;
621 	}
622 
623 	// Check that both images are of the correct format and process
624 	if ( *format == mlt_image_rgb24a && b_format == mlt_image_rgb24a )
625 	{
626 		double sw, sh;
627 		// Get values from the transition
628 		double scale_x = mlt_properties_anim_get_double( properties, "scale_x", position, length );
629 		double scale_y = mlt_properties_anim_get_double( properties, "scale_y", position, length );
630 		int scale = mlt_properties_get_int( properties, "scale" );
631 		double geom_scale_x = (double) b_width / result.w;
632 		double geom_scale_y = (double) b_height / result.h;
633 		struct sliced_desc desc = {
634 			.a_image = *image,
635 			.b_image = b_image,
636 			.interp = interpBL_b32,
637 			.a_width = *width,
638 			.a_height = *height,
639 			.b_width = b_width,
640 			.b_height = b_height,
641 			.lower_x = -(result.x + result.w / 2.0), // center
642 			.lower_y = -(result.y + result.h / 2.0), // middle
643 			.mix = result.o,
644 			.x_offset = (double) b_width / 2.0,
645 			.y_offset = (double) b_height / 2.0,
646 			.b_alpha = mlt_properties_get_int( properties, "b_alpha" ),
647 			// Affine boundaries
648 			.minima = 0,
649 			.xmax = b_width - 1,
650 			.ymax = b_height - 1
651 		};
652 
653 		// Recalculate vars if alignment supplied.
654 		if ( mlt_properties_get( properties, "halign" ) || mlt_properties_get( properties, "valign" ) )
655 		{
656 			double halign = alignment_parse( mlt_properties_get( properties, "halign" ) );
657 			double valign = alignment_parse( mlt_properties_get( properties, "valign" ) );
658 			desc.x_offset = halign * b_width / 2.0;
659 			desc.y_offset = valign * b_height / 2.0;
660 			desc.lower_x = -(result.x + geometry_w * halign / 2.0f);
661 			desc.lower_y = -(result.y + geometry_h * valign / 2.0f);
662 		}
663 
664 		affine_init( desc.affine.matrix );
665 
666 		// Compute the affine transform
667 		get_affine( &desc.affine, transition, ( double )position, length, scale_width, scale_height );
668 		desc.dz = MapZ( desc.affine.matrix, 0, 0 );
669 		if ( (int) fabs( desc.dz * 1000 ) < 25 ) {
670 			if (threads != 1)
671 				mlt_service_unlock( MLT_TRANSITION_SERVICE( transition ) );
672 			return 0;
673 		}
674 
675 		if (mlt_properties_get_int(properties, "invert_scale")) {
676 			scale_x = 1.0 / scale_x;
677 			scale_y = 1.0 / scale_y;
678 		}
679 
680 		// Factor scaling into the transformation based on output resolution.
681 		if ( distort )
682 		{
683 			scale_x = geom_scale_x * ( scale_x == 0 ? 1 : scale_x );
684 			scale_y = geom_scale_y * ( scale_y == 0 ? 1 : scale_y );
685 		}
686 		else
687 		{
688 			// Determine scale with respect to aspect ratio.
689 			double consumer_dar = consumer_ar * normalised_width / normalised_height;
690 
691 			if ( b_dar > consumer_dar )
692 			{
693 				scale_x = geom_scale_x * ( scale_x == 0 ? 1 : scale_x );
694 				scale_y = geom_scale_x * ( scale_y == 0 ? 1 : scale_y );
695 				scale_y *= b_ar / consumer_ar;
696 			}
697 			else
698 			{
699 				scale_x = geom_scale_y * ( scale_x == 0 ? 1 : scale_x );
700 				scale_y = geom_scale_y * ( scale_y == 0 ? 1 : scale_y );
701 				scale_x *= consumer_ar / b_ar;
702 			}
703 		}
704 		if ( scale )
705 		{
706 			affine_max_output( desc.affine.matrix, &sw, &sh, desc.dz, *width, *height );
707 			affine_scale( desc.affine.matrix, sw * MIN( geom_scale_x, geom_scale_y ), sh * MIN( geom_scale_x, geom_scale_y ) );
708 		}
709 		else if ( scale_x != 0 && scale_y != 0 )
710 		{
711 			affine_scale( desc.affine.matrix, scale_x, scale_y );
712 		}
713 
714 
715 		char *interps = mlt_properties_get( a_props, "rescale.interp" );
716 		// Copy in case string is changed.
717 		if ( interps )
718 			interps = strdup( interps );
719 
720 		// Set the interpolation function
721 		if ( interps == NULL || strcmp( interps, "nearest" ) == 0 || strcmp( interps, "neighbor" ) == 0 || strcmp( interps, "tiles" ) == 0 || strcmp( interps, "fast_bilinear" ) == 0 )
722 		{
723 			desc.interp = interpNN_b32;
724 			// uses lrintf. Values should be >= -0.5 and < max + 0.5
725 			desc.minima -= 0.5;
726 			desc.xmax += 0.49;
727 			desc.ymax += 0.49;
728 		}
729 		else if ( strcmp( interps, "bilinear" ) == 0 )
730 		{
731 			desc.interp = interpBL_b32;
732 			// uses floorf.
733 		}
734 		else if ( strcmp( interps, "bicubic" ) == 0 ||  strcmp( interps, "hyper" ) == 0 || strcmp( interps, "sinc" ) == 0 || strcmp( interps, "lanczos" ) == 0 || strcmp( interps, "spline" ) == 0 )
735 		{
736 			// TODO: lanczos 8x8
737 			// TODO: spline 4x4 or 6x6
738 			desc.interp = interpBC_b32;
739 			// uses ceilf. Values should be > -1 and <= max.
740 			desc.minima -= 1;
741 		}
742 		free( interps );
743 
744 		// Do the transform with interpolation
745 		if (threads == 1)
746 			sliced_proc(0, 0, 1, &desc);
747 		else
748 			mlt_slices_run_normal(threads, sliced_proc, &desc);
749 
750 		// Remove potentially large image on the B frame.
751 		mlt_frame_set_image( b_frame, NULL, 0, NULL );
752 	}
753 	if (threads != 1)
754 		mlt_service_unlock( MLT_TRANSITION_SERVICE( transition ) );
755 	return 0;
756 }
757 
758 /** Affine transition processing.
759 */
760 
transition_process(mlt_transition transition,mlt_frame a_frame,mlt_frame b_frame)761 static mlt_frame transition_process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame )
762 {
763 	// Push the transition on to the frame
764 	mlt_frame_push_service( a_frame, transition );
765 
766 	// Push the b_frame on to the stack
767 	mlt_frame_push_frame( a_frame, b_frame );
768 
769 	// Push the transition method
770 	mlt_frame_push_get_image( a_frame, transition_get_image );
771 
772 	return a_frame;
773 }
774 
775 /** Constructor for the filter.
776 */
777 
transition_affine_init(mlt_profile profile,mlt_service_type type,const char * id,char * arg)778 mlt_transition transition_affine_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
779 {
780 	mlt_transition transition = mlt_transition_new( );
781 	if ( transition != NULL )
782 	{
783 		mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "distort", 0 );
784 		mlt_properties_set( MLT_TRANSITION_PROPERTIES( transition ), "rect", "0%/0%:100%x100%:100%" );
785 		// Inform apps and framework that this is a video only transition
786 		mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "_transition_type", 1 );
787 		mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "fill", 1 );
788 		transition->process = transition_process;
789 	}
790 	return transition;
791 }
792