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 typedef struct
82 {
83 	double matrix[3][3];
84 }
85 affine_t;
86 
affine_init(double affine[3][3])87 static void affine_init( double affine[3][3] )
88 {
89 	affine[0][0] = 1;
90 	affine[0][1] = 0;
91 	affine[0][2] = 0;
92 	affine[1][0] = 0;
93 	affine[1][1] = 1;
94 	affine[1][2] = 0;
95 	affine[2][0] = 0;
96 	affine[2][1] = 0;
97 	affine[2][2] = 1;
98 }
99 
100 // Multiply two this affine transform with that
affine_multiply(double affine[3][3],double matrix[3][3])101 static void affine_multiply( double affine[3][3], double matrix[3][3] )
102 {
103 	double output[3][3];
104 	int i;
105 	int j;
106 
107 	for ( i = 0; i < 3; i ++ )
108 		for ( j = 0; j < 3; j ++ )
109 			output[i][j] = affine[i][0] * matrix[j][0] + affine[i][1] * matrix[j][1] + affine[i][2] * matrix[j][2];
110 
111 	affine[0][0] = output[0][0];
112 	affine[0][1] = output[0][1];
113 	affine[0][2] = output[0][2];
114 	affine[1][0] = output[1][0];
115 	affine[1][1] = output[1][1];
116 	affine[1][2] = output[1][2];
117 	affine[2][0] = output[2][0];
118 	affine[2][1] = output[2][1];
119 	affine[2][2] = output[2][2];
120 }
121 
122 // Rotate by a given angle
affine_rotate_x(double affine[3][3],double angle)123 static void affine_rotate_x( double affine[3][3], double angle )
124 {
125 	double matrix[3][3];
126 	matrix[0][0] = cos( angle * M_PI / 180 );
127 	matrix[0][1] = 0 - sin( angle * M_PI / 180 );
128 	matrix[0][2] = 0;
129 	matrix[1][0] = sin( angle * M_PI / 180 );
130 	matrix[1][1] = cos( angle * M_PI / 180 );
131 	matrix[1][2] = 0;
132 	matrix[2][0] = 0;
133 	matrix[2][1] = 0;
134 	matrix[2][2] = 1;
135 	affine_multiply( affine, matrix );
136 }
137 
affine_rotate_y(double affine[3][3],double angle)138 static void affine_rotate_y( double affine[3][3], double angle )
139 {
140 	double matrix[3][3];
141 	matrix[0][0] = cos( angle * M_PI / 180 );
142 	matrix[0][1] = 0;
143 	matrix[0][2] = 0 - sin( angle * M_PI / 180 );
144 	matrix[1][0] = 0;
145 	matrix[1][1] = 1;
146 	matrix[1][2] = 0;
147 	matrix[2][0] = sin( angle * M_PI / 180 );
148 	matrix[2][1] = 0;
149 	matrix[2][2] = cos( angle * M_PI / 180 );
150 	affine_multiply( affine, matrix );
151 }
152 
affine_rotate_z(double affine[3][3],double angle)153 static void affine_rotate_z( double affine[3][3], double angle )
154 {
155 	double matrix[3][3];
156 	matrix[0][0] = 1;
157 	matrix[0][1] = 0;
158 	matrix[0][2] = 0;
159 	matrix[1][0] = 0;
160 	matrix[1][1] = cos( angle * M_PI / 180 );
161 	matrix[1][2] = sin( angle * M_PI / 180 );
162 	matrix[2][0] = 0;
163 	matrix[2][1] = - sin( angle * M_PI / 180 );
164 	matrix[2][2] = cos( angle * M_PI / 180 );
165 	affine_multiply( affine, matrix );
166 }
167 
affine_scale(double affine[3][3],double sx,double sy)168 static void affine_scale( double affine[3][3], double sx, double sy )
169 {
170 	double matrix[3][3];
171 	matrix[0][0] = sx;
172 	matrix[0][1] = 0;
173 	matrix[0][2] = 0;
174 	matrix[1][0] = 0;
175 	matrix[1][1] = sy;
176 	matrix[1][2] = 0;
177 	matrix[2][0] = 0;
178 	matrix[2][1] = 0;
179 	matrix[2][2] = 1;
180 	affine_multiply( affine, matrix );
181 }
182 
183 // Shear by a given value
affine_shear(double affine[3][3],double shear_x,double shear_y,double shear_z)184 static void affine_shear( double affine[3][3], double shear_x, double shear_y, double shear_z )
185 {
186 	double matrix[3][3];
187 	matrix[0][0] = 1;
188 	matrix[0][1] = tan( shear_x * M_PI / 180 );
189 	matrix[0][2] = 0;
190 	matrix[1][0] = tan( shear_y * M_PI / 180 );
191 	matrix[1][1] = 1;
192 	matrix[1][2] = tan( shear_z * M_PI / 180 );
193 	matrix[2][0] = 0;
194 	matrix[2][1] = 0;
195 	matrix[2][2] = 1;
196 	affine_multiply( affine, matrix );
197 }
198 
affine_offset(double affine[3][3],double x,double y)199 static void affine_offset( double affine[3][3], double x, double y )
200 {
201 	affine[0][2] += x;
202 	affine[1][2] += y;
203 }
204 
205 // Obtain the mapped x coordinate of the input
MapX(double affine[3][3],double x,double y)206 static inline double MapX( double affine[3][3], double x, double y )
207 {
208 	return affine[0][0] * x + affine[0][1] * y + affine[0][2];
209 }
210 
211 // Obtain the mapped y coordinate of the input
MapY(double affine[3][3],double x,double y)212 static inline double MapY( double affine[3][3], double x, double y )
213 {
214 	return affine[1][0] * x + affine[1][1] * y + affine[1][2];
215 }
216 
MapZ(double affine[3][3],double x,double y)217 static inline double MapZ( double affine[3][3], double x, double y )
218 {
219 	return affine[2][0] * x + affine[2][1] * y + affine[2][2];
220 }
221 
affine_max_output(double affine[3][3],double * w,double * h,double dz,double max_width,double max_height)222 static void affine_max_output( double affine[3][3], double *w, double *h, double dz, double max_width, double max_height )
223 {
224 	int tlx = MapX( affine, -max_width,  max_height ) / dz;
225 	int tly = MapY( affine, -max_width,  max_height ) / dz;
226 	int trx = MapX( affine,  max_width,  max_height ) / dz;
227 	int try = MapY( affine,  max_width,  max_height ) / dz;
228 	int blx = MapX( affine, -max_width, -max_height ) / dz;
229 	int bly = MapY( affine, -max_width, -max_height ) / dz;
230 	int brx = MapX( affine,  max_width, -max_height ) / dz;
231 	int bry = MapY( affine,  max_width, -max_height ) / dz;
232 
233 	int max_x;
234 	int max_y;
235 	int min_x;
236 	int min_y;
237 
238 	max_x = MAX( tlx, trx );
239 	max_x = MAX( max_x, blx );
240 	max_x = MAX( max_x, brx );
241 
242 	min_x = MIN( tlx, trx );
243 	min_x = MIN( min_x, blx );
244 	min_x = MIN( min_x, brx );
245 
246 	max_y = MAX( tly, try );
247 	max_y = MAX( max_y, bly );
248 	max_y = MAX( max_y, bry );
249 
250 	min_y = MIN( tly, try );
251 	min_y = MIN( min_y, bly );
252 	min_y = MIN( min_y, bry );
253 
254 	*w = ( double )( max_x - min_x + 1 ) / max_width / 2.0;
255 	*h = ( double )( max_y - min_y + 1 ) / max_height / 2.0;
256 }
257 
258 #define IN_RANGE( v, r )	( v >= - r / 2 && v < r / 2 )
259 
get_affine(affine_t * affine,mlt_transition transition,double position,int length,double scale_width,double scale_height)260 static inline void get_affine( affine_t *affine, mlt_transition transition,
261 	double position, int length, double scale_width, double scale_height )
262 {
263 	mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition );
264 	int keyed = mlt_properties_get_int( properties, "keyed" );
265 
266 	if ( keyed == 0 )
267 	{
268 		double fix_rotate_x = anim_get_angle( properties, "fix_rotate_x", position, length );
269 		double fix_rotate_y = anim_get_angle( properties, "fix_rotate_y", position, length );
270 		double fix_rotate_z = anim_get_angle( properties, "fix_rotate_z", position, length );
271 		double rotate_x = mlt_properties_get_double( properties, "rotate_x" );
272 		double rotate_y = mlt_properties_get_double( properties, "rotate_y" );
273 		double rotate_z = mlt_properties_get_double( properties, "rotate_z" );
274 		double fix_shear_x = anim_get_angle( properties, "fix_shear_x", position, length );
275 		double fix_shear_y = anim_get_angle( properties, "fix_shear_y", position, length );
276 		double fix_shear_z = anim_get_angle( properties, "fix_shear_z", position, length );
277 		double shear_x = mlt_properties_get_double( properties, "shear_x" );
278 		double shear_y = mlt_properties_get_double( properties, "shear_y" );
279 		double shear_z = mlt_properties_get_double( properties, "shear_z" );
280 		double ox = mlt_properties_anim_get_double( properties, "ox", position, length );
281 		double oy = mlt_properties_anim_get_double( properties, "oy", position, length );
282 
283 		affine_rotate_x( affine->matrix, fix_rotate_x + rotate_x * position );
284 		affine_rotate_y( affine->matrix, fix_rotate_y + rotate_y * position );
285 		affine_rotate_z( affine->matrix, fix_rotate_z + rotate_z * position );
286 		affine_shear( affine->matrix,
287 					  fix_shear_x + shear_x * position,
288 					  fix_shear_y + shear_y * position,
289 					  fix_shear_z + shear_z * position );
290 		affine_offset( affine->matrix, ox * scale_width, oy * scale_height );
291 	}
292 	else
293 	{
294 		double rotate_x = anim_get_angle(properties, "rotate_x", position, length);
295 		double rotate_y = anim_get_angle(properties, "rotate_y", position, length);
296 		double rotate_z = anim_get_angle(properties, "rotate_z", position, length);
297 		double shear_x = anim_get_angle(properties, "shear_x", position, length);
298 		double shear_y = anim_get_angle(properties, "shear_y", position, length);
299 		double shear_z = anim_get_angle(properties, "shear_z", position, length);
300 		double o_x = mlt_properties_anim_get_double(properties, "ox",
301 			repeat_position(properties, "ox", position, length), length);
302 		double o_y = mlt_properties_anim_get_double(properties, "oy",
303 			repeat_position(properties, "oy", position, length), length);
304 
305 		affine_rotate_x( affine->matrix, rotate_x );
306 		affine_rotate_y( affine->matrix, rotate_y );
307 		affine_rotate_z( affine->matrix, rotate_z );
308 		affine_shear( affine->matrix, shear_x, shear_y, shear_z );
309 		affine_offset( affine->matrix, o_x * scale_width, o_y * scale_height );
310 	}
311 }
312 
313 struct sliced_desc
314 {
315 	uint8_t *a_image, *b_image;
316 	interpp interp;
317 	affine_t affine;
318 	int a_width, a_height, b_width, b_height;
319 	double lower_x, lower_y;
320 	double dz, mix;
321 	double x_offset, y_offset;
322 	int b_alpha;
323 	double minima, xmax, ymax;
324 };
325 
sliced_proc(int id,int index,int jobs,void * cookie)326 static int sliced_proc( int id, int index, int jobs, void* cookie )
327 {
328 	(void) id; // unused
329 	struct sliced_desc ctx = *((struct sliced_desc*) cookie);
330 	int height_slice = (ctx.a_height + jobs / 2) / jobs;
331 	int starty = height_slice * index;
332 	double x, y;
333 	double dx, dy;
334 	int i, j;
335 
336 	ctx.a_image += (index * height_slice) * (ctx.a_width * 4);
337 	for (i = 0, y = ctx.lower_y; i < ctx.a_height; i++, y++) {
338 		if (i >= starty && i < (starty + height_slice)) {
339 			for (j = 0, x = ctx.lower_x; j < ctx.a_width; j++, x++) {
340 				dx = MapX( ctx.affine.matrix, x, y ) / ctx.dz + ctx.x_offset;
341 				dy = MapY( ctx.affine.matrix, x, y ) / ctx.dz + ctx.y_offset;
342 				if (dx >= ctx.minima && dx <= ctx.xmax && dy >= ctx.minima && dy <= ctx.ymax)
343 					ctx.interp(ctx.b_image, ctx.b_width, ctx.b_height, dx, dy, ctx.mix, ctx.a_image, ctx.b_alpha);
344 				ctx.a_image += 4;
345 			}
346 		}
347 	}
348 	return 0;
349 }
350 
351 /** Get the image.
352 */
353 
transition_get_image(mlt_frame a_frame,uint8_t ** image,mlt_image_format * format,int * width,int * height,int writable)354 static int transition_get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
355 {
356 	// Get the b frame from the stack
357 	mlt_frame b_frame = mlt_frame_pop_frame( a_frame );
358 
359 	// Get the transition object
360 	mlt_transition transition = mlt_frame_pop_service( a_frame );
361 
362 	// Get the properties of the transition
363 	mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition );
364 
365 	// Get the properties of the a frame
366 	mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame );
367 
368 	// Get the properties of the b frame
369 	mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
370 
371 	// Image, format, width, height and image for the b frame
372 	uint8_t *b_image = NULL;
373 	mlt_image_format b_format = mlt_image_rgba;
374 	int b_width = mlt_properties_get_int( b_props, "meta.media.width" );
375 	int b_height = mlt_properties_get_int( b_props, "meta.media.height" );
376 	double b_ar = mlt_frame_get_aspect_ratio( b_frame );
377 	double b_dar = b_ar * b_width / b_height;
378 
379 	// Assign the current position
380 	mlt_position position =  mlt_transition_get_position( transition, a_frame );
381 
382 	int mirror = mlt_properties_get_position( properties, "mirror" );
383 	int length = mlt_transition_get_length( transition );
384 	if ( mlt_properties_get_int( properties, "always_active" ) )
385 	{
386 		mlt_properties props = mlt_properties_get_data( b_props, "_producer", NULL );
387 		mlt_position in = mlt_properties_get_int( props, "in" );
388 		mlt_position out = mlt_properties_get_int( props, "out" );
389 		length = out - in + 1;
390 	}
391 
392 	// Obtain the normalised width and height from the a_frame
393 	mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( transition ) );
394 	int normalised_width = profile->width;
395 	int normalised_height = profile->height;
396 	double consumer_ar = mlt_profile_sar( profile );
397 
398 	if ( mirror && position > length / 2 )
399 		position = abs( position - length );
400 
401 	// Preview scaling is not working correctly when offsets are active. I have
402 	// not figured out the math; so, disable preview scaling for now.
403 	double ox = mlt_properties_anim_get_double( properties, "ox", position, length );
404 	double oy = mlt_properties_anim_get_double( properties, "oy", position, length );
405 	if (ox != 0.0 || oy != 0.0) {
406 		*width = normalised_width;
407 		*height = normalised_height;
408 	}
409 
410 	// Fetch the a frame image
411 	*format = mlt_image_rgba;
412 	int error = mlt_frame_get_image( a_frame, image, format, width, height, 1 );
413 	if (error || !image)
414 		return error;
415 
416 	// Calculate the region now
417 	double scale_width = mlt_profile_scale_width(profile, *width);
418 	double scale_height = mlt_profile_scale_height(profile, *height);
419 	mlt_rect result = {0, 0, normalised_width, normalised_height, 1.0};
420 
421 	mlt_service_lock( MLT_TRANSITION_SERVICE( transition ) );
422 
423 	if (mlt_properties_get(properties, "rect"))
424 	{
425 		// Determine length and obtain cycle
426 		double cycle = mlt_properties_get_double( properties, "cycle" );
427 
428 		// Allow a repeat cycle
429 		if ( cycle >= 1 )
430 			length = cycle;
431 		else if ( cycle > 0 )
432 			length *= cycle;
433 
434 		mlt_position anim_pos = repeat_position(properties, "rect", position, length);
435 		result = mlt_properties_anim_get_rect(properties, "rect", anim_pos, length);
436 		if (mlt_properties_get(properties, "rect") && strchr(mlt_properties_get(properties, "rect"), '%')) {
437 			result.x *= normalised_width;
438 			result.y *= normalised_height;
439 			result.w *= normalised_width;
440 			result.h *= normalised_height;
441 		}
442 		result.o = (result.o == DBL_MIN)? 1.0 : MIN(result.o, 1.0);
443 	}
444 
445 	int threads = mlt_properties_get_int(properties, "threads");
446 	threads = CLAMP(threads, 0, mlt_slices_count_normal());
447 	if (threads == 1)
448 		mlt_service_unlock( MLT_TRANSITION_SERVICE( transition ) );
449 
450 	result.x *= scale_width;
451 	result.y *= scale_height;
452 	result.w *= scale_width;
453 	result.h *= scale_height;
454 
455 	double geometry_w = result.w;
456 	double geometry_h = result.h;
457 	int fill = mlt_properties_get_int( properties, "fill" );
458 	int distort = mlt_properties_get_int( properties, "distort" );
459 
460 	if ( !fill )
461 	{
462 		double geometry_dar = result.w * consumer_ar / result.h;
463 
464 		if ( b_dar > geometry_dar )
465 		{
466 			result.w = MIN( result.w, b_width * b_ar / consumer_ar );
467 			result.h = result.w * consumer_ar / b_dar;
468 		}
469 		else
470 		{
471 			result.h = MIN( result.h, b_height );
472 			result.w = result.h * b_dar / consumer_ar;
473 		}
474 	}
475 
476 	// Fetch the b frame image
477 	if (scale_width != 1.0 || scale_height != 1.0) {
478 			// Scale request of b frame image to consumer scale maintaining its aspect ratio.
479 			b_height = CLAMP(*height, 1, MLT_AFFINE_MAX_DIMENSION);
480 			b_width  = MAX(b_height * b_dar / b_ar, 1);
481 			if (b_width > MLT_AFFINE_MAX_DIMENSION) {
482 				b_width  = CLAMP(*width, 1, MLT_AFFINE_MAX_DIMENSION);
483 				b_height = MAX(b_width * b_ar / b_dar, 1);
484 			}
485 			// Set the rescale interpolation to match the frame
486 			mlt_properties_set( b_props, "rescale.interp", mlt_properties_get( a_props, "rescale.interp" ) );
487 			// Disable padding (resize filter)
488 			mlt_properties_set_int( b_props, "distort", 1 );
489 	} else if (!mlt_properties_get_int(b_props, "interpolation_not_required")
490 			   && (fill || distort || b_width > result.w || b_height > result.h
491 				   || mlt_properties_get_int(properties, "b_scaled") || mlt_properties_get_int(b_props, "always_scale"))) {
492 		// Request b frame image scaled to what is needed.
493 		b_height = CLAMP(result.h, 1, MLT_AFFINE_MAX_DIMENSION);
494 		b_width  = MAX(b_height * b_dar / b_ar, 1);
495 		if (b_width > MLT_AFFINE_MAX_DIMENSION) {
496 			b_width  = CLAMP(result.w, 1, MLT_AFFINE_MAX_DIMENSION);
497 			b_height = MAX(b_width * b_ar / b_dar, 1);
498 		}
499 		// Set the rescale interpolation to match the frame
500 		mlt_properties_set( b_props, "rescale.interp", mlt_properties_get( a_props, "rescale.interp" ) );
501 		// Disable padding (resize filter)
502 		mlt_properties_set_int( b_props, "distort", 1 );
503 	} else {
504 		// Request at resolution of b frame image. This only happens when not using fill or distort mode
505 		// and the image is smaller than the rect with the intention to prevent scaling of the
506 		// image and merely position and possibly transform.
507 		mlt_properties_set_int( b_props, "rescale_width", b_width );
508 		mlt_properties_set_int( b_props, "rescale_height", b_height );
509 
510 		const char* b_resource = mlt_properties_get(
511 			MLT_PRODUCER_PROPERTIES(mlt_frame_get_original_producer(b_frame)), "resource");
512 		// Check if we are applied as a filter inside a transition
513 		if (b_resource && !strcmp("<track>", b_resource)) {
514 			// Set the rescale interpolation to match the frame
515 			mlt_properties_set( b_props, "rescale.interp", mlt_properties_get( a_props, "rescale.interp" ) );
516 		} else {
517 			// Suppress padding and aspect normalization.
518 			mlt_properties_set( b_props, "rescale.interp", "none" );
519 		}
520 	}
521 	mlt_log_debug(MLT_TRANSITION_SERVICE(transition), "requesting image B at resolution %dx%d\n", b_width, b_height);
522 
523 	// This is not a field-aware transform.
524 	mlt_properties_set_int( b_props, "consumer_deinterlace", 1 );
525 
526 	error = mlt_frame_get_image( b_frame, &b_image, &b_format, &b_width, &b_height, 0 );
527 	if (error || !b_image) {
528 		// Remove potentially large image on the B frame.
529 		mlt_frame_set_image( b_frame, NULL, 0, NULL );
530 		if (threads != 1)
531 			mlt_service_unlock( MLT_TRANSITION_SERVICE( transition ) );
532 		return error;
533 	}
534 
535 	// Check that both images are of the correct format and process
536 	if ( *format == mlt_image_rgba && b_format == mlt_image_rgba )
537 	{
538 		double sw, sh;
539 		// Get values from the transition
540 		double scale_x = mlt_properties_anim_get_double( properties, "scale_x", position, length );
541 		double scale_y = mlt_properties_anim_get_double( properties, "scale_y", position, length );
542 		int scale = mlt_properties_get_int( properties, "scale" );
543 		double geom_scale_x = (double) b_width / result.w;
544 		double geom_scale_y = (double) b_height / result.h;
545 		struct sliced_desc desc = {
546 			.a_image = *image,
547 			.b_image = b_image,
548 			.interp = interpBL_b32,
549 			.a_width = *width,
550 			.a_height = *height,
551 			.b_width = b_width,
552 			.b_height = b_height,
553 			.lower_x = -(result.x + result.w / 2.0), // center
554 			.lower_y = -(result.y + result.h / 2.0), // middle
555 			.mix = result.o,
556 			.x_offset = (double) b_width / 2.0,
557 			.y_offset = (double) b_height / 2.0,
558 			.b_alpha = mlt_properties_get_int( properties, "b_alpha" ),
559 			// Affine boundaries
560 			.minima = 0,
561 			.xmax = b_width - 1,
562 			.ymax = b_height - 1
563 		};
564 
565 		// Recalculate vars if alignment supplied.
566 		if ( mlt_properties_get( properties, "halign" ) || mlt_properties_get( properties, "valign" ) )
567 		{
568 			double halign = alignment_parse( mlt_properties_get( properties, "halign" ) );
569 			double valign = alignment_parse( mlt_properties_get( properties, "valign" ) );
570 			desc.x_offset = halign * b_width / 2.0;
571 			desc.y_offset = valign * b_height / 2.0;
572 			desc.lower_x = -(result.x + geometry_w * halign / 2.0f);
573 			desc.lower_y = -(result.y + geometry_h * valign / 2.0f);
574 		}
575 
576 		affine_init( desc.affine.matrix );
577 
578 		// Compute the affine transform
579 		get_affine( &desc.affine, transition, ( double )position, length, scale_width, scale_height );
580 		desc.dz = MapZ( desc.affine.matrix, 0, 0 );
581 		if ( (int) fabs( desc.dz * 1000 ) < 25 ) {
582 			if (threads != 1)
583 				mlt_service_unlock( MLT_TRANSITION_SERVICE( transition ) );
584 			return 0;
585 		}
586 
587 		if (mlt_properties_get_int(properties, "invert_scale")) {
588 			scale_x = 1.0 / scale_x;
589 			scale_y = 1.0 / scale_y;
590 		}
591 
592 		// Factor scaling into the transformation based on output resolution.
593 		if ( distort )
594 		{
595 			scale_x = geom_scale_x * ( scale_x == 0 ? 1 : scale_x );
596 			scale_y = geom_scale_y * ( scale_y == 0 ? 1 : scale_y );
597 		}
598 		else
599 		{
600 			// Determine scale with respect to aspect ratio.
601 			double consumer_dar = consumer_ar * normalised_width / normalised_height;
602 
603 			if ( b_dar > consumer_dar )
604 			{
605 				scale_x = geom_scale_x * ( scale_x == 0 ? 1 : scale_x );
606 				scale_y = geom_scale_x * ( scale_y == 0 ? 1 : scale_y );
607 				scale_y *= b_ar / consumer_ar;
608 			}
609 			else
610 			{
611 				scale_x = geom_scale_y * ( scale_x == 0 ? 1 : scale_x );
612 				scale_y = geom_scale_y * ( scale_y == 0 ? 1 : scale_y );
613 				scale_x *= consumer_ar / b_ar;
614 			}
615 		}
616 		if ( scale )
617 		{
618 			affine_max_output( desc.affine.matrix, &sw, &sh, desc.dz, *width, *height );
619 			affine_scale( desc.affine.matrix, sw * MIN( geom_scale_x, geom_scale_y ), sh * MIN( geom_scale_x, geom_scale_y ) );
620 		}
621 		else if ( scale_x != 0 && scale_y != 0 )
622 		{
623 			affine_scale( desc.affine.matrix, scale_x, scale_y );
624 		}
625 
626 
627 		char *interps = mlt_properties_get( a_props, "rescale.interp" );
628 		// Copy in case string is changed.
629 		if ( interps )
630 			interps = strdup( interps );
631 
632 		// Set the interpolation function
633 		if ( interps == NULL || strcmp( interps, "nearest" ) == 0 || strcmp( interps, "neighbor" ) == 0 || strcmp( interps, "tiles" ) == 0 || strcmp( interps, "fast_bilinear" ) == 0 )
634 		{
635 			desc.interp = interpNN_b32;
636 			// uses lrintf. Values should be >= -0.5 and < max + 0.5
637 			desc.minima -= 0.5;
638 			desc.xmax += 0.49;
639 			desc.ymax += 0.49;
640 		}
641 		else if ( strcmp( interps, "bilinear" ) == 0 )
642 		{
643 			desc.interp = interpBL_b32;
644 			// uses floorf.
645 		}
646 		else if ( strcmp( interps, "bicubic" ) == 0 ||  strcmp( interps, "hyper" ) == 0 || strcmp( interps, "sinc" ) == 0 || strcmp( interps, "lanczos" ) == 0 || strcmp( interps, "spline" ) == 0 )
647 		{
648 			// TODO: lanczos 8x8
649 			// TODO: spline 4x4 or 6x6
650 			desc.interp = interpBC_b32;
651 			// uses ceilf. Values should be > -1 and <= max.
652 			desc.minima -= 1;
653 		}
654 		free( interps );
655 
656 		// Do the transform with interpolation
657 		if (threads == 1)
658 			sliced_proc(0, 0, 1, &desc);
659 		else
660 			mlt_slices_run_normal(threads, sliced_proc, &desc);
661 
662 		// Remove potentially large image on the B frame.
663 		mlt_frame_set_image( b_frame, NULL, 0, NULL );
664 	}
665 	if (threads != 1)
666 		mlt_service_unlock( MLT_TRANSITION_SERVICE( transition ) );
667 	return 0;
668 }
669 
670 /** Affine transition processing.
671 */
672 
transition_process(mlt_transition transition,mlt_frame a_frame,mlt_frame b_frame)673 static mlt_frame transition_process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame )
674 {
675 	// Push the transition on to the frame
676 	mlt_frame_push_service( a_frame, transition );
677 
678 	// Push the b_frame on to the stack
679 	mlt_frame_push_frame( a_frame, b_frame );
680 
681 	// Push the transition method
682 	mlt_frame_push_get_image( a_frame, transition_get_image );
683 
684 	return a_frame;
685 }
686 
687 /** Constructor for the filter.
688 */
689 
transition_affine_init(mlt_profile profile,mlt_service_type type,const char * id,char * arg)690 mlt_transition transition_affine_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
691 {
692 	mlt_transition transition = mlt_transition_new( );
693 	if ( transition != NULL )
694 	{
695 		mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "distort", 0 );
696 		mlt_properties_set( MLT_TRANSITION_PROPERTIES( transition ), "rect", "0%/0%:100%x100%:100%" );
697 		// Inform apps and framework that this is a video only transition
698 		mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "_transition_type", 1 );
699 		mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "fill", 1 );
700 		transition->process = transition_process;
701 	}
702 	return transition;
703 }
704