]> git.sesse.net Git - mlt/blob - src/modules/plus/transition_affine.c
Minor corrections with alpha and affines
[mlt] / src / modules / plus / transition_affine.c
1 /*
2  * transition_affine.c -- affine transformations
3  * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
4  * Author: Charles Yates <charles.yates@pandora.be>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 #include "transition_affine.h"
22 #include <framework/mlt.h>
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <ctype.h>
27 #include <string.h>
28 #include <math.h>
29
30 /** Geometry struct.
31 */
32
33 struct geometry_s
34 {
35         int frame;
36         float position;
37         float mix;
38         int nw; // normalised width
39         int nh; // normalised height
40         int sw; // scaled width, not including consumer scale based upon w/nw
41         int sh; // scaled height, not including consumer scale based upon h/nh
42         float x;
43         float y;
44         float w;
45         float h;
46         struct geometry_s *next;
47 };
48
49 /** Parse a value from a geometry string.
50 */
51
52 static float parse_value( char **ptr, int normalisation, char delim, float defaults )
53 {
54         float value = defaults;
55
56         if ( *ptr != NULL && **ptr != '\0' )
57         {
58                 char *end = NULL;
59                 value = strtod( *ptr, &end );
60                 if ( end != NULL )
61                 {
62                         if ( *end == '%' )
63                                 value = ( value / 100.0 ) * normalisation;
64                         while ( *end == delim || *end == '%' )
65                                 end ++;
66                 }
67                 *ptr = end;
68         }
69
70         return value;
71 }
72
73 /** Parse a geometry property string with the syntax X,Y:WxH:MIX. Any value can be 
74         expressed as a percentage by appending a % after the value, otherwise values are
75         assumed to be relative to the normalised dimensions of the consumer.
76 */
77
78 static void geometry_parse( struct geometry_s *geometry, struct geometry_s *defaults, char *property, int nw, int nh )
79 {
80         // Assign normalised width and height
81         geometry->nw = nw;
82         geometry->nh = nh;
83
84         // Assign from defaults if available
85         if ( defaults != NULL )
86         {
87                 geometry->x = defaults->x;
88                 geometry->y = defaults->y;
89                 geometry->w = geometry->sw = defaults->w;
90                 geometry->h = geometry->sh = defaults->h;
91                 geometry->mix = defaults->mix;
92                 defaults->next = geometry;
93         }
94         else
95         {
96                 geometry->mix = 100;
97         }
98
99         // Parse the geomtry string
100         if ( property != NULL && strcmp( property, "" ) )
101         {
102                 char *ptr = property;
103                 geometry->x = parse_value( &ptr, nw, ',', geometry->x );
104                 geometry->y = parse_value( &ptr, nh, ':', geometry->y );
105                 geometry->w = geometry->sw = parse_value( &ptr, nw, 'x', geometry->w );
106                 geometry->h = geometry->sh = parse_value( &ptr, nh, ':', geometry->h );
107                 geometry->mix = parse_value( &ptr, 100, ' ', geometry->mix );
108         }
109 }
110
111 /** Calculate real geometry.
112 */
113
114 static void geometry_calculate( struct geometry_s *output, struct geometry_s *in, float position )
115 {
116         // Search in for position
117         struct geometry_s *out = in->next;
118
119         if ( position >= 1.0 )
120         {
121                 int section = floor( position );
122                 position -= section;
123                 if ( section % 2 == 1 )
124                         position = 1.0 - position;
125         }
126
127         while ( out->next != NULL )
128         {
129                 if ( position >= in->position && position < out->position )
130                         break;
131
132                 in = out;
133                 out = in->next;
134         }
135
136         position = ( position - in->position ) / ( out->position - in->position );
137
138         // Calculate this frames geometry
139         if ( in->frame != out->frame - 1 )
140         {
141                 output->nw = in->nw;
142                 output->nh = in->nh;
143                 output->x = in->x + ( out->x - in->x ) * position;
144                 output->y = in->y + ( out->y - in->y ) * position;
145                 output->w = in->w + ( out->w - in->w ) * position;
146                 output->h = in->h + ( out->h - in->h ) * position;
147                 output->mix = in->mix + ( out->mix - in->mix ) * position;
148         }
149         else
150         {
151                 output->nw = out->nw;
152                 output->nh = out->nh;
153                 output->x = out->x;
154                 output->y = out->y;
155                 output->w = out->w;
156                 output->h = out->h;
157                 output->mix = out->mix;
158         }
159 }
160
161 void transition_destroy_keys( void *arg )
162 {
163         struct geometry_s *ptr = arg;
164         struct geometry_s *next = NULL;
165
166         while ( ptr != NULL )
167         {
168                 next = ptr->next;
169                 free( ptr );
170                 ptr = next;
171         }
172 }
173
174 static struct geometry_s *transition_parse_keys( mlt_transition this,  int normalised_width, int normalised_height )
175 {
176         // Loop variable for property interrogation
177         int i = 0;
178
179         // Get the properties of the transition
180         mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
181
182         // Get the in and out position
183         mlt_position in = mlt_transition_get_in( this );
184         mlt_position out = mlt_transition_get_out( this );
185
186         // Create the start
187         struct geometry_s *start = calloc( 1, sizeof( struct geometry_s ) );
188
189         // Create the end (we always need two entries)
190         struct geometry_s *end = calloc( 1, sizeof( struct geometry_s ) );
191
192         // Pointer
193         struct geometry_s *ptr = start;
194
195         // Parse the start property
196         geometry_parse( start, NULL, mlt_properties_get( properties, "start" ), normalised_width, normalised_height );
197
198         // Parse the keys in between
199         for ( i = 0; i < mlt_properties_count( properties ); i ++ )
200         {
201                 // Get the name of the property
202                 char *name = mlt_properties_get_name( properties, i );
203
204                 // Check that it's valid
205                 if ( !strncmp( name, "key[", 4 ) )
206                 {
207                         // Get the value of the property
208                         char *value = mlt_properties_get_value( properties, i );
209
210                         // Determine the frame number
211                         int frame = atoi( name + 4 );
212
213                         // Determine the position
214                         float position = 0;
215                         
216                         if ( frame >= 0 && frame < ( out - in ) )
217                                 position = ( float )frame / ( float )( out - in + 1 );
218                         else if ( frame < 0 && - frame < ( out - in ) )
219                                 position = ( float )( out - in + frame ) / ( float )( out - in + 1 );
220
221                         // For now, we'll exclude all keys received out of order
222                         if ( position > ptr->position )
223                         {
224                                 // Create a new geometry
225                                 struct geometry_s *temp = calloc( 1, sizeof( struct geometry_s ) );
226
227                                 // Parse and add to the list
228                                 geometry_parse( temp, ptr, value, normalised_width, normalised_height );
229
230                                 // Assign the position and frame
231                                 temp->frame = frame;
232                                 temp->position = position;
233
234                                 // Allow the next to be appended after this one
235                                 ptr = temp;
236                         }
237                         else
238                         {
239                                 fprintf( stderr, "Key out of order - skipping %s\n", name );
240                         }
241                 }
242         }
243         
244         // Parse the end
245         geometry_parse( end, ptr, mlt_properties_get( properties, "end" ), normalised_width, normalised_height );
246         if ( out > 0 )
247                 end->position = ( float )( out - in ) / ( float )( out - in + 1 );
248         else
249                 end->position = 1;
250
251         // Assign to properties to ensure we get destroyed
252         mlt_properties_set_data( properties, "geometries", start, 0, transition_destroy_keys, NULL );
253
254         return start;
255 }
256
257 struct geometry_s *composite_calculate( struct geometry_s *result, mlt_transition this, mlt_frame a_frame, float position )
258 {
259         // Get the properties from the transition
260         mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
261
262         // Get the properties from the frame
263         mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame );
264         
265         // Structures for geometry
266         struct geometry_s *start = mlt_properties_get_data( properties, "geometries", NULL );
267
268         // Now parse the geometries
269         if ( start == NULL )
270         {
271                 // Obtain the normalised width and height from the a_frame
272                 int normalised_width = mlt_properties_get_int( a_props, "normalised_width" );
273                 int normalised_height = mlt_properties_get_int( a_props, "normalised_height" );
274
275                 // Parse the transitions properties
276                 start = transition_parse_keys( this, normalised_width, normalised_height );
277         }
278
279         // Do the calculation
280         geometry_calculate( result, start, position );
281
282         return start;
283 }
284
285 typedef struct 
286 {
287         float matrix[3][3];
288 }
289 affine_t;
290
291 static void affine_init( float this[3][3] )
292 {
293         this[0][0] = 1;
294         this[0][1] = 0;
295         this[0][2] = 0;
296         this[1][0] = 0;
297         this[1][1] = 1;
298         this[1][2] = 0;
299         this[2][0] = 0;
300         this[2][1] = 0;
301         this[2][2] = 1;
302 }
303
304 // Multiply two this affine transform with that
305 static void affine_multiply( float this[3][3], float that[3][3] )
306 {
307         float output[3][3];
308         int i;
309         int j;
310
311         for ( i = 0; i < 3; i ++ )
312                 for ( j = 0; j < 3; j ++ )
313                         output[i][j] = this[i][0] * that[j][0] + this[i][1] * that[j][1] + this[i][2] * that[j][2];
314
315         this[0][0] = output[0][0];
316         this[0][1] = output[0][1];
317         this[0][2] = output[0][2];
318         this[1][0] = output[1][0];
319         this[1][1] = output[1][1];
320         this[1][2] = output[1][2];
321         this[2][0] = output[2][0];
322         this[2][1] = output[2][1];
323         this[2][2] = output[2][2];
324 }
325
326 // Rotate by a given angle
327 static void affine_rotate( float this[3][3], float angle )
328 {
329         float affine[3][3];
330         affine[0][0] = cos( angle * M_PI / 180 );
331         affine[0][1] = 0 - sin( angle * M_PI / 180 );
332         affine[0][2] = 0;
333         affine[1][0] = sin( angle * M_PI / 180 );
334         affine[1][1] = cos( angle * M_PI / 180 );
335         affine[1][2] = 0;
336         affine[2][0] = 0;
337         affine[2][1] = 0;
338         affine[2][2] = 1;
339         affine_multiply( this, affine );
340 }
341
342 static void affine_rotate_y( float this[3][3], float angle )
343 {
344         float affine[3][3];
345         affine[0][0] = cos( angle * M_PI / 180 );
346         affine[0][1] = 0;
347         affine[0][2] = 0 - sin( angle * M_PI / 180 );
348         affine[1][0] = 0;
349         affine[1][1] = 1;
350         affine[1][2] = 0;
351         affine[2][0] = sin( angle * M_PI / 180 );
352         affine[2][1] = 0;
353         affine[2][2] = cos( angle * M_PI / 180 );
354         affine_multiply( this, affine );
355 }
356
357 static void affine_rotate_z( float this[3][3], float angle )
358 {
359         float affine[3][3];
360         affine[0][0] = 1;
361         affine[0][1] = 0;
362         affine[0][2] = 0;
363         affine[1][0] = 0;
364         affine[1][1] = cos( angle * M_PI / 180 );
365         affine[1][2] = sin( angle * M_PI / 180 );
366         affine[2][0] = 0;
367         affine[2][1] = - sin( angle * M_PI / 180 );
368         affine[2][2] = cos( angle * M_PI / 180 );
369         affine_multiply( this, affine );
370 }
371
372 static void affine_scale( float this[3][3], float sx, float sy )
373 {
374         float affine[3][3];
375         affine[0][0] = sx;
376         affine[0][1] = 0;
377         affine[0][2] = 0;
378         affine[1][0] = 0;
379         affine[1][1] = sy;
380         affine[1][2] = 0;
381         affine[2][0] = 0;
382         affine[2][1] = 0;
383         affine[2][2] = 1;
384         affine_multiply( this, affine );
385 }
386
387 // Shear by a given value
388 static void affine_shear( float this[3][3], float shear_x, float shear_y, float shear_z )
389 {
390         float affine[3][3];
391         affine[0][0] = 1;
392         affine[0][1] = tan( shear_x * M_PI / 180 );
393         affine[0][2] = 0;
394         affine[1][0] = tan( shear_y * M_PI / 180 );
395         affine[1][1] = 1;
396         affine[1][2] = tan( shear_z * M_PI / 180 );
397         affine[2][0] = 0;
398         affine[2][1] = 0;
399         affine[2][2] = 1;
400         affine_multiply( this, affine );
401 }
402
403 static void affine_offset( float this[3][3], int x, int y )
404 {
405         this[0][2] += x;
406         this[1][2] += y;
407 }
408
409 // Obtain the mapped x coordinate of the input
410 static inline double MapX( float this[3][3], int x, int y )
411 {
412         return this[0][0] * x + this[0][1] * y + this[0][2];
413 }
414
415 // Obtain the mapped y coordinate of the input
416 static inline double MapY( float this[3][3], int x, int y )
417 {
418         return this[1][0] * x + this[1][1] * y + this[1][2];
419 }
420
421 static inline double MapZ( float this[3][3], int x, int y )
422 {
423         return this[2][0] * x + this[2][1] * y + this[2][2];
424 }
425
426 #define MAX( x, y ) x > y ? x : y
427 #define MIN( x, y ) x < y ? x : y
428
429 static void affine_max_output( float this[3][3], float *w, float *h, float dz )
430 {
431         int tlx = MapX( this, -720, 576 ) / dz;
432         int tly = MapY( this, -720, 576 ) / dz;
433         int trx = MapX( this, 720, 576 ) / dz;
434         int try = MapY( this, 720, 576 ) / dz;
435         int blx = MapX( this, -720, -576 ) / dz;
436         int bly = MapY( this, -720, -576 ) / dz;
437         int brx = MapX( this, 720, -576 ) / dz;
438         int bry = MapY( this, 720, -576 ) / dz;
439
440         int max_x;
441         int max_y;
442         int min_x;
443         int min_y;
444
445         max_x = MAX( tlx, trx );
446         max_x = MAX( max_x, blx );
447         max_x = MAX( max_x, brx );
448
449         min_x = MIN( tlx, trx );
450         min_x = MIN( min_x, blx );
451         min_x = MIN( min_x, brx );
452
453         max_y = MAX( tly, try );
454         max_y = MAX( max_y, bly );
455         max_y = MAX( max_y, bry );
456
457         min_y = MIN( tly, try );
458         min_y = MIN( min_y, bly );
459         min_y = MIN( min_y, bry );
460
461         *w = ( float )( max_x - min_x + 1 ) / 1440.0;
462         *h = ( float )( max_y - min_y + 1 ) / 1152.0;
463 }
464
465 #define IN_RANGE( v, r )        ( v >= - r / 2 && v < r / 2 )
466
467 /** Get the image.
468 */
469
470 static int transition_get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
471 {
472         // Get the b frame from the stack
473         mlt_frame b_frame = mlt_frame_pop_frame( a_frame );
474
475         // Get the transition object
476         mlt_transition this = mlt_frame_pop_service( a_frame );
477
478         // Get the properties of the transition
479         mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
480
481         // Get the properties of the a frame
482         mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame );
483
484         // Get the properties of the b frame
485         mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
486
487         // Image, format, width, height and image for the b frame
488         uint8_t *b_image = NULL;
489         mlt_image_format b_format = mlt_image_yuv422;
490         int b_width;
491         int b_height;
492
493         // Get the unique name to retrieve the frame position
494         char *name = mlt_properties_get( properties, "_unique_id" );
495
496         // Assign the current position to the name
497         mlt_position position =  mlt_properties_get_position( a_props, name );
498         mlt_position in = mlt_properties_get_position( properties, "in" );
499         mlt_position out = mlt_properties_get_position( properties, "out" );
500         int mirror = mlt_properties_get_position( properties, "mirror" );
501         int length = out - in + 1;
502
503         // Structures for geometry
504         struct geometry_s *start = mlt_properties_get_data( properties, "geometries", NULL );
505         struct geometry_s result;
506
507         if ( mirror && position > length / 2 )
508                 position = abs( position - length );
509
510         // Now parse the geometries
511         if ( start == NULL )
512         {
513                 // Obtain the normalised width and height from the a_frame
514                 int normalised_width = mlt_properties_get_int( a_props, "normalised_width" );
515                 int normalised_height = mlt_properties_get_int( a_props, "normalised_height" );
516
517                 // Parse the transitions properties
518                 start = transition_parse_keys( this, normalised_width, normalised_height );
519         }
520
521         // Fetch the a frame image
522         mlt_frame_get_image( a_frame, image, format, width, height, 1 );
523
524         // Calculate the region now
525         composite_calculate( &result, this, a_frame, ( float )position / length );
526
527         // Fetch the b frame image
528         result.w = ( int )( result.w * *width / result.nw );
529         result.h = ( int )( result.h * *height / result.nh );
530         result.x = ( int )( result.x * *width / result.nw );
531         result.y = ( int )( result.y * *height / result.nh );
532         result.w -= ( int )abs( result.w ) % 2;
533         result.x -= ( int )abs( result.x ) % 2;
534         b_width = result.w;
535         b_height = result.h;
536
537         if ( !strcmp( mlt_properties_get( a_props, "rescale.interp" ), "none" ) )
538         {
539                 mlt_properties_set( b_props, "rescale.interp", "nearest" );
540                 mlt_properties_set_double( b_props, "consumer_aspect_ratio", mlt_properties_get_double( a_props, "aspect_ratio" ) );
541         }
542         else
543         {
544                 mlt_properties_set( b_props, "rescale.interp", mlt_properties_get( a_props, "rescale.interp" ) );
545                 mlt_properties_set_double( b_props, "consumer_aspect_ratio", mlt_properties_get_double( a_props, "consumer_aspect_ratio" ) );
546         }
547
548         mlt_properties_set_int( b_props, "distort", mlt_properties_get_int( properties, "distort" ) );
549         mlt_frame_get_image( b_frame, &b_image, &b_format, &b_width, &b_height, 0 );
550         result.w = b_width;
551         result.h = b_height;
552
553         // Check that both images are of the correct format and process
554         if ( *format == mlt_image_yuv422 && b_format == mlt_image_yuv422 )
555         {
556                 register int x, y;
557                 register int dx, dy;
558                 double dz;
559                 float sw, sh;
560
561                 // Get values from the transition
562                 float fix_rotate_x = mlt_properties_get_double( properties, "fix_rotate_x" );
563                 float fix_rotate_y = mlt_properties_get_double( properties, "fix_rotate_y" );
564                 float fix_rotate_z = mlt_properties_get_double( properties, "fix_rotate_z" );
565                 float rotate_x = mlt_properties_get_double( properties, "rotate_x" );
566                 float rotate_y = mlt_properties_get_double( properties, "rotate_y" );
567                 float rotate_z = mlt_properties_get_double( properties, "rotate_z" );
568                 float fix_shear_x = mlt_properties_get_double( properties, "fix_shear_x" );
569                 float fix_shear_y = mlt_properties_get_double( properties, "fix_shear_y" );
570                 float fix_shear_z = mlt_properties_get_double( properties, "fix_shear_z" );
571                 float scale_x = mlt_properties_get_double( properties, "scale_x" );
572                 float scale_y = mlt_properties_get_double( properties, "scale_y" );
573                 float shear_x = mlt_properties_get_double( properties, "shear_x" );
574                 float shear_y = mlt_properties_get_double( properties, "shear_y" );
575                 float shear_z = mlt_properties_get_double( properties, "shear_z" );
576                 float ox = mlt_properties_get_double( properties, "ox" );
577                 float oy = mlt_properties_get_double( properties, "oy" );
578                 int scale = mlt_properties_get_int( properties, "scale" );
579
580                 uint8_t *p = *image;
581                 uint8_t *q = *image;
582
583                 int cx = result.x + ( b_width >> 1 );
584                 int cy = result.y + ( b_height >> 1 );
585         
586                 int lower_x = 0 - cx;
587                 int upper_x = *width - cx;
588                 int lower_y = 0 - cy;
589                 int upper_y = *height - cy;
590
591                 int b_stride = b_width << 1;
592                 int a_stride = *width << 1;
593                 int x_offset = ( int )result.w >> 1;
594                 int y_offset = ( int )result.h >> 1;
595
596                 uint8_t *alpha = mlt_frame_get_alpha_mask( b_frame );
597                 uint8_t *mask = mlt_frame_get_alpha_mask( a_frame );
598                 uint8_t *pmask = mask;
599                 float mix;
600
601                 affine_t affine;
602                 affine_init( affine.matrix );
603                 affine_rotate( affine.matrix, fix_rotate_x + rotate_x * position );
604                 affine_rotate_y( affine.matrix, fix_rotate_y + rotate_y * position );
605                 affine_rotate_z( affine.matrix, fix_rotate_z + rotate_z * position );
606                 affine_shear( affine.matrix, 
607                                           fix_shear_x + shear_x * position, 
608                                           fix_shear_y + shear_y * position,
609                                           fix_shear_z + shear_z * position );
610                 affine_offset( affine.matrix, ox, oy );
611
612                 lower_x -= ( lower_x & 1 );
613                 upper_x -= ( upper_x & 1 );
614
615                 q = *image;
616
617                 dz = MapZ( affine.matrix, 0, 0 );
618
619                 if ( mask == NULL )
620                 {
621                         mask = mlt_pool_alloc( *width * *height );
622                         pmask = mask;
623                         memset( mask, 255, *width * *height );
624                 }
625
626                 if ( ( int )abs( dz * 1000 ) < 25 )
627                         goto getout;
628
629                 if ( scale )
630                 {
631                         affine_max_output( affine.matrix, &sw, &sh, dz );
632                         affine_scale( affine.matrix, sw, sh );
633                 }
634                 else if ( scale_x != 0 && scale_y != 0 )
635                 {
636                         affine_scale( affine.matrix, scale_x, scale_y );
637                 }
638
639                 for ( y = lower_y; y < upper_y; y ++ )
640                 {
641                         p = q;
642
643                         for ( x = lower_x; x < upper_x; x ++ )
644                         {
645                                 dx = MapX( affine.matrix, x, y ) / dz + x_offset;
646                                 dy = MapY( affine.matrix, x, y ) / dz + y_offset;
647
648                                 if ( dx >= 0 && dx < b_width && dy >=0 && dy < b_height )
649                                 {
650                                         if ( alpha == NULL )
651                                         {
652                                                 *pmask ++;
653                                                 dx += dx & 1;
654                                                 *p ++ = *( b_image + dy * b_stride + ( dx << 1 ) );
655                                                 *p ++ = *( b_image + dy * b_stride + ( dx << 1 ) + ( ( x & 1 ) << 1 ) + 1 );
656                                         }
657                                         else
658                                         {
659                                                 *pmask ++ = *( alpha + dy * b_width + dx );
660                                                 mix = ( float )*( alpha + dy * b_width + dx ) / 255.0;
661                                                 dx += dx & 1;
662                                                 *p = *p * ( 1 - mix ) + mix * *( b_image + dy * b_stride + ( dx << 1 ) );
663                                                 p ++;
664                                                 *p = *p * ( 1 - mix ) + mix * *( b_image + dy * b_stride + ( dx << 1 ) + ( ( x & 1 ) << 1 ) + 1 );
665                                                 p ++;
666                                         }
667                                 }
668                                 else
669                                 {
670                                         p += 2;
671                                         pmask ++;
672                                 }
673                         }
674
675                         q += a_stride;
676                 }
677
678 getout:
679                 a_frame->get_alpha_mask = NULL;
680                 mlt_properties_set_data( a_props, "alpha", mask, 0, mlt_pool_release, NULL );
681         }
682
683         return 0;
684 }
685
686 /** Affine transition processing.
687 */
688
689 static mlt_frame transition_process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame )
690 {
691         // Get a unique name to store the frame position
692         char *name = mlt_properties_get( MLT_TRANSITION_PROPERTIES( transition ), "_unique_id" );
693
694         // Assign the current position to the name
695         mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame );
696         mlt_properties_set_position( a_props, name, mlt_frame_get_position( a_frame ) );
697
698         // Push the transition on to the frame
699         mlt_frame_push_service( a_frame, transition );
700
701         // Push the b_frame on to the stack
702         mlt_frame_push_frame( a_frame, b_frame );
703
704         // Push the transition method
705         mlt_frame_push_get_image( a_frame, transition_get_image );
706
707         return a_frame;
708 }
709
710 /** Constructor for the filter.
711 */
712
713 mlt_transition transition_affine_init( char *arg )
714 {
715         mlt_transition transition = mlt_transition_new( );
716         if ( transition != NULL )
717         {
718                 mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "sx", 1 );
719                 mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "sy", 1 );
720                 mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "distort", 0 );
721                 mlt_properties_set( MLT_TRANSITION_PROPERTIES( transition ), "start", "0,0:100%x100%" );
722                 // Inform apps and framework that this is a video only transition
723                 mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "_transition_type", 1 );
724                 transition->process = transition_process;
725         }
726         return transition;
727 }