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