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