2 * transition_affine.c -- affine transformations
3 * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
4 * Author: Charles Yates <charles.yates@pandora.be>
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.
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.
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.
21 #include "transition_affine.h"
22 #include <framework/mlt.h>
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
46 struct geometry_s
*next
;
49 /** Parse a value from a geometry string.
52 static float parse_value( char **ptr
, int normalisation
, char delim
, float defaults
)
54 float value
= defaults
;
56 if ( *ptr
!= NULL
&& **ptr
!= '\0' )
59 value
= strtod( *ptr
, &end
);
63 value
= ( value
/ 100.0 ) * normalisation
;
64 while ( *end
== delim
|| *end
== '%' )
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.
78 static void geometry_parse( struct geometry_s
*geometry
, struct geometry_s
*defaults
, char *property
, int nw
, int nh
)
80 // Assign normalised width and height
84 // Assign from defaults if available
85 if ( defaults
!= NULL
)
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
;
99 // Parse the geomtry string
100 if ( property
!= NULL
&& strcmp( property
, "" ) )
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
);
111 /** Calculate real geometry.
114 static void geometry_calculate( struct geometry_s
*output
, struct geometry_s
*in
, float position
)
116 // Search in for position
117 struct geometry_s
*out
= in
->next
;
119 if ( position
>= 1.0 )
121 int section
= floor( position
);
123 if ( section
% 2 == 1 )
124 position
= 1.0 - position
;
127 while ( out
->next
!= NULL
)
129 if ( position
>= in
->position
&& position
< out
->position
)
136 position
= ( position
- in
->position
) / ( out
->position
- in
->position
);
138 // Calculate this frames geometry
139 if ( in
->frame
!= out
->frame
- 1 )
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
;
151 output
->nw
= out
->nw
;
152 output
->nh
= out
->nh
;
157 output
->mix
= out
->mix
;
161 void transition_destroy_keys( void *arg
)
163 struct geometry_s
*ptr
= arg
;
164 struct geometry_s
*next
= NULL
;
166 while ( ptr
!= NULL
)
174 static struct geometry_s
*transition_parse_keys( mlt_transition
this, int normalised_width
, int normalised_height
)
176 // Loop variable for property interrogation
179 // Get the properties of the transition
180 mlt_properties properties
= MLT_TRANSITION_PROPERTIES( this );
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 );
187 struct geometry_s
*start
= calloc( 1, sizeof( struct geometry_s
) );
189 // Create the end (we always need two entries)
190 struct geometry_s
*end
= calloc( 1, sizeof( struct geometry_s
) );
193 struct geometry_s
*ptr
= start
;
195 // Parse the start property
196 geometry_parse( start
, NULL
, mlt_properties_get( properties
, "start" ), normalised_width
, normalised_height
);
198 // Parse the keys in between
199 for ( i
= 0; i
< mlt_properties_count( properties
); i
++ )
201 // Get the name of the property
202 char *name
= mlt_properties_get_name( properties
, i
);
204 // Check that it's valid
205 if ( !strncmp( name
, "key[", 4 ) )
207 // Get the value of the property
208 char *value
= mlt_properties_get_value( properties
, i
);
210 // Determine the frame number
211 int frame
= atoi( name
+ 4 );
213 // Determine the position
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 );
221 // For now, we'll exclude all keys received out of order
222 if ( position
> ptr
->position
)
224 // Create a new geometry
225 struct geometry_s
*temp
= calloc( 1, sizeof( struct geometry_s
) );
227 // Parse and add to the list
228 geometry_parse( temp
, ptr
, value
, normalised_width
, normalised_height
);
230 // Assign the position and frame
232 temp
->position
= position
;
234 // Allow the next to be appended after this one
239 fprintf( stderr
, "Key out of order - skipping %s\n", name
);
245 geometry_parse( end
, ptr
, mlt_properties_get( properties
, "end" ), normalised_width
, normalised_height
);
247 end
->position
= ( float )( out
- in
) / ( float )( out
- in
+ 1 );
251 // Assign to properties to ensure we get destroyed
252 mlt_properties_set_data( properties
, "geometries", start
, 0, transition_destroy_keys
, NULL
);
257 struct geometry_s
*composite_calculate( struct geometry_s
*result
, mlt_transition
this, mlt_frame a_frame
, float position
)
259 // Get the properties from the transition
260 mlt_properties properties
= MLT_TRANSITION_PROPERTIES( this );
262 // Get the properties from the frame
263 mlt_properties a_props
= MLT_FRAME_PROPERTIES( a_frame
);
265 // Structures for geometry
266 struct geometry_s
*start
= mlt_properties_get_data( properties
, "geometries", NULL
);
268 // Now parse the geometries
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" );
275 // Parse the transitions properties
276 start
= transition_parse_keys( this, normalised_width
, normalised_height
);
279 // Do the calculation
280 geometry_calculate( result
, start
, position
);
291 static void affine_init( float this[3][3] )
304 // Multiply two this affine transform with that
305 static void affine_multiply( float this[3][3], float that
[3][3] )
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];
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];
326 // Rotate by a given angle
327 static void affine_rotate( float this[3][3], float angle
)
330 affine
[0][0] = cos( angle
* M_PI
/ 180 );
331 affine
[0][1] = 0 - sin( angle
* M_PI
/ 180 );
333 affine
[1][0] = sin( angle
* M_PI
/ 180 );
334 affine
[1][1] = cos( angle
* M_PI
/ 180 );
339 affine_multiply( this, affine
);
342 static void affine_rotate_y( float this[3][3], float angle
)
345 affine
[0][0] = cos( angle
* M_PI
/ 180 );
347 affine
[0][2] = 0 - sin( angle
* M_PI
/ 180 );
351 affine
[2][0] = sin( angle
* M_PI
/ 180 );
353 affine
[2][2] = cos( angle
* M_PI
/ 180 );
354 affine_multiply( this, affine
);
357 static void affine_rotate_z( float this[3][3], float angle
)
364 affine
[1][1] = cos( angle
* M_PI
/ 180 );
365 affine
[1][2] = sin( angle
* M_PI
/ 180 );
367 affine
[2][1] = - sin( angle
* M_PI
/ 180 );
368 affine
[2][2] = cos( angle
* M_PI
/ 180 );
369 affine_multiply( this, affine
);
372 static void affine_scale( float this[3][3], float sx
, float sy
)
384 affine_multiply( this, affine
);
387 // Shear by a given value
388 static void affine_shear( float this[3][3], float shear_x
, float shear_y
, float shear_z
)
392 affine
[0][1] = tan( shear_x
* M_PI
/ 180 );
394 affine
[1][0] = tan( shear_y
* M_PI
/ 180 );
396 affine
[1][2] = tan( shear_z
* M_PI
/ 180 );
400 affine_multiply( this, affine
);
403 static void affine_offset( float this[3][3], int x
, int y
)
409 // Obtain the mapped x coordinate of the input
410 static inline double MapX( float this[3][3], int x
, int y
)
412 return this[0][0] * x
+ this[0][1] * y
+ this[0][2];
415 // Obtain the mapped y coordinate of the input
416 static inline double MapY( float this[3][3], int x
, int y
)
418 return this[1][0] * x
+ this[1][1] * y
+ this[1][2];
421 static inline double MapZ( float this[3][3], int x
, int y
)
423 return this[2][0] * x
+ this[2][1] * y
+ this[2][2];
426 #define MAX( x, y ) x > y ? x : y
427 #define MIN( x, y ) x < y ? x : y
429 static void affine_max_output( float this[3][3], float *w
, float *h
)
431 int tlx
= MapX( this, -720, 576 );
432 int tly
= MapY( this, -720, 576 );
433 int trx
= MapX( this, 720, 576 );
434 int try = MapY( this, 720, 576 );
435 int blx
= MapX( this, -720, -576 );
436 int bly
= MapY( this, -720, -576 );
437 int brx
= MapX( this, 720, -576 );
438 int bry
= MapY( this, 720, -576 );
445 max_x
= MAX( tlx
, trx
);
446 max_x
= MAX( max_x
, blx
);
447 max_x
= MAX( max_x
, brx
);
449 min_x
= MIN( tlx
, trx
);
450 min_x
= MIN( min_x
, blx
);
451 min_x
= MIN( min_x
, brx
);
453 max_y
= MAX( tly
, try );
454 max_y
= MAX( max_y
, bly
);
455 max_y
= MAX( max_y
, bry
);
457 min_y
= MIN( tly
, try );
458 min_y
= MIN( min_y
, bly
);
459 min_y
= MIN( min_y
, bry
);
461 *w
= ( float )( max_x
- min_x
+ 1 ) / 1440.0;
462 *h
= ( float )( max_y
- min_y
+ 1 ) / 1152.0;
465 #define IN_RANGE( v, r ) ( v >= - r / 2 && v < r / 2 )
470 static int transition_get_image( mlt_frame a_frame
, uint8_t **image
, mlt_image_format
*format
, int *width
, int *height
, int writable
)
472 // Get the b frame from the stack
473 mlt_frame b_frame
= mlt_frame_pop_frame( a_frame
);
475 // Get the transition object
476 mlt_transition
this = mlt_frame_pop_service( a_frame
);
478 // Get the properties of the transition
479 mlt_properties properties
= MLT_TRANSITION_PROPERTIES( this );
481 // Get the properties of the a frame
482 mlt_properties a_props
= MLT_FRAME_PROPERTIES( a_frame
);
484 // Get the properties of the b frame
485 mlt_properties b_props
= MLT_FRAME_PROPERTIES( b_frame
);
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
;
493 // Get the unique name to retrieve the frame position
494 char *name
= mlt_properties_get( properties
, "_unique_id" );
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" );
501 // Structures for geometry
502 struct geometry_s
*start
= mlt_properties_get_data( properties
, "geometries", NULL
);
503 struct geometry_s result
;
505 // Now parse the geometries
508 // Obtain the normalised width and height from the a_frame
509 int normalised_width
= mlt_properties_get_int( a_props
, "normalised_width" );
510 int normalised_height
= mlt_properties_get_int( a_props
, "normalised_height" );
512 // Parse the transitions properties
513 start
= transition_parse_keys( this, normalised_width
, normalised_height
);
516 // Fetch the a frame image
517 mlt_frame_get_image( a_frame
, image
, format
, width
, height
, 1 );
519 // Calculate the region now
520 composite_calculate( &result
, this, a_frame
, ( float )( position
) / ( out
- in
+ 1 ) );
522 // Fetch the b frame image
523 result
.w
= ( int )( result
.w
* *width
/ result
.nw
);
524 result
.h
= ( int )( result
.h
* *height
/ result
.nh
);
525 result
.x
= ( int )( result
.x
* *width
/ result
.nw
);
526 result
.y
= ( int )( result
.y
* *height
/ result
.nh
);
527 result
.w
-= ( int )abs( result
.w
) % 2;
528 result
.x
-= ( int )abs( result
.x
) % 2;
532 if ( !strcmp( mlt_properties_get( a_props
, "rescale.interp" ), "none" ) )
534 mlt_properties_set( b_props
, "rescale.interp", "nearest" );
535 mlt_properties_set_double( b_props
, "consumer_aspect_ratio", mlt_properties_get_double( a_props
, "aspect_ratio" ) );
539 mlt_properties_set( b_props
, "rescale.interp", mlt_properties_get( a_props
, "rescale.interp" ) );
540 mlt_properties_set_double( b_props
, "consumer_aspect_ratio", mlt_properties_get_double( a_props
, "consumer_aspect_ratio" ) );
543 mlt_properties_set_int( b_props
, "distort", mlt_properties_get_int( properties
, "distort" ) );
544 mlt_frame_get_image( b_frame
, &b_image
, &b_format
, &b_width
, &b_height
, 0 );
548 // Check that both images are of the correct format and process
549 if ( *format
== mlt_image_yuv422
&& b_format
== mlt_image_yuv422
)
556 // Get values from the transition
557 float fix_rotate_x
= mlt_properties_get_double( properties
, "fix_rotate_x" );
558 float fix_rotate_y
= mlt_properties_get_double( properties
, "fix_rotate_y" );
559 float fix_rotate_z
= mlt_properties_get_double( properties
, "fix_rotate_z" );
560 float rotate_x
= mlt_properties_get_double( properties
, "rotate_x" );
561 float rotate_y
= mlt_properties_get_double( properties
, "rotate_y" );
562 float rotate_z
= mlt_properties_get_double( properties
, "rotate_z" );
563 float fix_shear_x
= mlt_properties_get_double( properties
, "fix_shear_x" );
564 float fix_shear_y
= mlt_properties_get_double( properties
, "fix_shear_y" );
565 float fix_shear_z
= mlt_properties_get_double( properties
, "fix_shear_z" );
566 float shear_x
= mlt_properties_get_double( properties
, "shear_x" );
567 float shear_y
= mlt_properties_get_double( properties
, "shear_y" );
568 float shear_z
= mlt_properties_get_double( properties
, "shear_z" );
569 float ox
= mlt_properties_get_double( properties
, "ox" );
570 float oy
= mlt_properties_get_double( properties
, "oy" );
571 int scale
= mlt_properties_get_int( properties
, "scale" );
576 int cx
= result
.x
+ ( b_width
>> 1 );
577 int cy
= result
.y
+ ( b_height
>> 1 );
579 int lower_x
= 0 - cx
;
580 int upper_x
= *width
- cx
;
581 int lower_y
= 0 - cy
;
582 int upper_y
= *height
- cy
;
584 int b_stride
= b_width
<< 1;
585 int a_stride
= *width
<< 1;
586 int x_offset
= ( int )result
.w
>> 1;
587 int y_offset
= ( int )result
.h
>> 1;
589 uint8_t *alpha
= mlt_frame_get_alpha_mask( b_frame
);
590 uint8_t *mask
= mlt_pool_alloc( b_width
* b_height
);
591 uint8_t *pmask
= mask
;
595 affine_init( affine
.matrix
);
596 affine_rotate( affine
.matrix
, fix_rotate_x
+ rotate_x
* ( position
- in
) );
597 affine_rotate_y( affine
.matrix
, fix_rotate_y
+ rotate_y
* ( position
- in
) );
598 affine_rotate_z( affine
.matrix
, fix_rotate_z
+ rotate_z
* ( position
- in
) );
599 affine_shear( affine
.matrix
,
600 fix_shear_x
+ shear_x
* ( position
- in
),
601 fix_shear_y
+ shear_y
* ( position
- in
),
602 fix_shear_z
+ shear_z
* ( position
- in
) );
603 affine_offset( affine
.matrix
, ox
, oy
);
607 affine_max_output( affine
.matrix
, &sw
, &sh
);
608 affine_scale( affine
.matrix
, sw
, sh
);
611 lower_x
-= ( lower_x
& 1 );
612 upper_x
-= ( upper_x
& 1 );
616 dz
= MapZ( affine
.matrix
, 0, 0 );
619 memset( mask
, 0, b_width
* b_height
);
621 for ( y
= lower_y
; y
< upper_y
; y
++ )
625 for ( x
= lower_x
; x
< upper_x
; x
++ )
627 dx
= MapX( affine
.matrix
, x
, y
) / dz
+ x_offset
;
628 dy
= MapY( affine
.matrix
, x
, y
) / dz
+ y_offset
;
630 if ( dx
>= 0 && dx
< b_width
&& dy
>=0 && dy
< b_height
)
636 *p
++ = *( b_image
+ dy
* b_stride
+ ( dx
<< 1 ) );
637 *p
++ = *( b_image
+ dy
* b_stride
+ ( dx
<< 1 ) + ( ( x
& 1 ) << 1 ) + 1 );
641 *pmask
++ = *( alpha
+ dy
* b_width
+ dx
);
642 mix
= ( float )*( alpha
+ dy
* b_width
+ dx
) / 255.0;
644 *p
= *p
* ( 1 - mix
) + mix
* *( b_image
+ dy
* b_stride
+ ( dx
<< 1 ) );
646 *p
= *p
* ( 1 - mix
) + mix
* *( b_image
+ dy
* b_stride
+ ( dx
<< 1 ) + ( ( x
& 1 ) << 1 ) + 1 );
660 b_frame
->get_alpha_mask
= NULL
;
661 mlt_properties_set_data( b_props
, "alpha", mask
, 0, mlt_pool_release
, NULL
);
667 /** Affine transition processing.
670 static mlt_frame
transition_process( mlt_transition transition
, mlt_frame a_frame
, mlt_frame b_frame
)
672 // Get a unique name to store the frame position
673 char *name
= mlt_properties_get( MLT_TRANSITION_PROPERTIES( transition
), "_unique_id" );
675 // Assign the current position to the name
676 mlt_properties a_props
= MLT_FRAME_PROPERTIES( a_frame
);
677 mlt_properties_set_position( a_props
, name
, mlt_frame_get_position( a_frame
) );
679 // Push the transition on to the frame
680 mlt_frame_push_service( a_frame
, transition
);
682 // Push the b_frame on to the stack
683 mlt_frame_push_frame( a_frame
, b_frame
);
685 // Push the transition method
686 mlt_frame_push_get_image( a_frame
, transition_get_image
);
691 /** Constructor for the filter.
694 mlt_transition
transition_affine_init( char *arg
)
696 mlt_transition transition
= mlt_transition_new( );
697 if ( transition
!= NULL
)
699 mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition
), "sx", 1 );
700 mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition
), "sy", 1 );
701 mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition
), "distort", 0 );
702 mlt_properties_set( MLT_TRANSITION_PROPERTIES( transition
), "start", "0,0:100%x100%" );
703 transition
->process
= transition_process
;