]> git.sesse.net Git - mlt/blob - src/modules/plus/transition_affine.c
remove usage of normalised_width and _height properties from services
[mlt] / src / modules / plus / transition_affine.c
1 /*
2  * transition_affine.c -- affine transformations
3  * Copyright (C) 2003-2010 Ushodaya Enterprises Limited
4  * Author: Charles Yates <charles.yates@pandora.be>
5  * Author: Dan Dennedy <dan@dennedy.org>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  */
21
22 #include <framework/mlt_transition.h>
23 #include <framework/mlt.h>
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <ctype.h>
28 #include <string.h>
29 #include <math.h>
30
31 #include "interp.h"
32
33 /** Calculate real geometry.
34 */
35
36 static void geometry_calculate( mlt_transition transition, const char *store, struct mlt_geometry_item_s *output, float position )
37 {
38         mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition );
39         mlt_geometry geometry = mlt_properties_get_data( properties, store, NULL );
40         int mirror_off = mlt_properties_get_int( properties, "mirror_off" );
41         int repeat_off = mlt_properties_get_int( properties, "repeat_off" );
42         int length = mlt_geometry_get_length( geometry );
43
44         // Allow wrapping
45         if ( !repeat_off && position >= length && length != 0 )
46         {
47                 int section = position / length;
48                 position -= section * length;
49                 if ( !mirror_off && section % 2 == 1 )
50                         position = length - position;
51         }
52
53         // Fetch the key for the position
54         mlt_geometry_fetch( geometry, output, position );
55 }
56
57
58 static mlt_geometry transition_parse_keys( mlt_transition transition, const char *name, const char *store, int normalised_width, int normalised_height )
59 {
60         // Get the properties of the transition
61         mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition );
62
63         // Try to fetch it first
64         mlt_geometry geometry = mlt_properties_get_data( properties, store, NULL );
65
66         // Determine length and obtain cycle
67         mlt_position length = mlt_transition_get_length( transition );
68         double cycle = mlt_properties_get_double( properties, "cycle" );
69
70         // Allow a geometry repeat cycle
71         if ( cycle >= 1 )
72                 length = cycle;
73         else if ( cycle > 0 )
74                 length *= cycle;
75
76         if ( geometry == NULL )
77         {
78                 // Get the new style geometry string
79                 char *property = mlt_properties_get( properties, name );
80
81                 // Create an empty geometries object
82                 geometry = mlt_geometry_init( );
83
84                 // Parse the geometry if we have one
85                 mlt_geometry_parse( geometry, property, length, normalised_width, normalised_height );
86
87                 // Store it
88                 mlt_properties_set_data( properties, store, geometry, 0, ( mlt_destructor )mlt_geometry_close, NULL );
89         }
90         else
91         {
92                 // Check for updates and refresh if necessary
93                 mlt_geometry_refresh( geometry, mlt_properties_get( properties, name ), length, normalised_width, normalised_height );
94         }
95
96         return geometry;
97 }
98
99 static mlt_geometry composite_calculate( mlt_transition transition, struct mlt_geometry_item_s *result, int nw, int nh, float position )
100 {
101         // Structures for geometry
102         mlt_geometry start = transition_parse_keys( transition, "geometry", "geometries", nw, nh );
103
104         // Do the calculation
105         geometry_calculate( transition, "geometries", result, position );
106
107         return start;
108 }
109
110 static inline float composite_calculate_key( mlt_transition transition, const char *name, const char *store, int norm, float position )
111 {
112         // Struct for the result
113         struct mlt_geometry_item_s result;
114
115         // Structures for geometry
116         transition_parse_keys( transition, name, store, norm, 0 );
117
118         // Do the calculation
119         geometry_calculate( transition, store, &result, position );
120
121         return result.x;
122 }
123
124 typedef struct
125 {
126         float matrix[3][3];
127 }
128 affine_t;
129
130 static void affine_init( float affine[3][3] )
131 {
132         affine[0][0] = 1;
133         affine[0][1] = 0;
134         affine[0][2] = 0;
135         affine[1][0] = 0;
136         affine[1][1] = 1;
137         affine[1][2] = 0;
138         affine[2][0] = 0;
139         affine[2][1] = 0;
140         affine[2][2] = 1;
141 }
142
143 // Multiply two this affine transform with that
144 static void affine_multiply( float affine[3][3], float matrix[3][3] )
145 {
146         float output[3][3];
147         int i;
148         int j;
149
150         for ( i = 0; i < 3; i ++ )
151                 for ( j = 0; j < 3; j ++ )
152                         output[i][j] = affine[i][0] * matrix[j][0] + affine[i][1] * matrix[j][1] + affine[i][2] * matrix[j][2];
153
154         affine[0][0] = output[0][0];
155         affine[0][1] = output[0][1];
156         affine[0][2] = output[0][2];
157         affine[1][0] = output[1][0];
158         affine[1][1] = output[1][1];
159         affine[1][2] = output[1][2];
160         affine[2][0] = output[2][0];
161         affine[2][1] = output[2][1];
162         affine[2][2] = output[2][2];
163 }
164
165 // Rotate by a given angle
166 static void affine_rotate_x( float affine[3][3], float angle )
167 {
168         float matrix[3][3];
169         matrix[0][0] = cos( angle * M_PI / 180 );
170         matrix[0][1] = 0 - sin( angle * M_PI / 180 );
171         matrix[0][2] = 0;
172         matrix[1][0] = sin( angle * M_PI / 180 );
173         matrix[1][1] = cos( angle * M_PI / 180 );
174         matrix[1][2] = 0;
175         matrix[2][0] = 0;
176         matrix[2][1] = 0;
177         matrix[2][2] = 1;
178         affine_multiply( affine, affine );
179 }
180
181 static void affine_rotate_y( float affine[3][3], float angle )
182 {
183         float matrix[3][3];
184         matrix[0][0] = cos( angle * M_PI / 180 );
185         matrix[0][1] = 0;
186         matrix[0][2] = 0 - sin( angle * M_PI / 180 );
187         matrix[1][0] = 0;
188         matrix[1][1] = 1;
189         matrix[1][2] = 0;
190         matrix[2][0] = sin( angle * M_PI / 180 );
191         matrix[2][1] = 0;
192         matrix[2][2] = cos( angle * M_PI / 180 );
193         affine_multiply( affine, matrix );
194 }
195
196 static void affine_rotate_z( float affine[3][3], float angle )
197 {
198         float matrix[3][3];
199         matrix[0][0] = 1;
200         matrix[0][1] = 0;
201         matrix[0][2] = 0;
202         matrix[1][0] = 0;
203         matrix[1][1] = cos( angle * M_PI / 180 );
204         matrix[1][2] = sin( angle * M_PI / 180 );
205         matrix[2][0] = 0;
206         matrix[2][1] = - sin( angle * M_PI / 180 );
207         matrix[2][2] = cos( angle * M_PI / 180 );
208         affine_multiply( affine, matrix );
209 }
210
211 static void affine_scale( float affine[3][3], float sx, float sy )
212 {
213         float matrix[3][3];
214         matrix[0][0] = sx;
215         matrix[0][1] = 0;
216         matrix[0][2] = 0;
217         matrix[1][0] = 0;
218         matrix[1][1] = sy;
219         matrix[1][2] = 0;
220         matrix[2][0] = 0;
221         matrix[2][1] = 0;
222         matrix[2][2] = 1;
223         affine_multiply( affine, matrix );
224 }
225
226 // Shear by a given value
227 static void affine_shear( float affine[3][3], float shear_x, float shear_y, float shear_z )
228 {
229         float matrix[3][3];
230         matrix[0][0] = 1;
231         matrix[0][1] = tan( shear_x * M_PI / 180 );
232         matrix[0][2] = 0;
233         matrix[1][0] = tan( shear_y * M_PI / 180 );
234         matrix[1][1] = 1;
235         matrix[1][2] = tan( shear_z * M_PI / 180 );
236         matrix[2][0] = 0;
237         matrix[2][1] = 0;
238         matrix[2][2] = 1;
239         affine_multiply( affine, matrix );
240 }
241
242 static void affine_offset( float affine[3][3], float x, float y )
243 {
244         affine[0][2] += x;
245         affine[1][2] += y;
246 }
247
248 // Obtain the mapped x coordinate of the input
249 static inline double MapX( float affine[3][3], float x, float y )
250 {
251         return affine[0][0] * x + affine[0][1] * y + affine[0][2];
252 }
253
254 // Obtain the mapped y coordinate of the input
255 static inline double MapY( float affine[3][3], float x, float y )
256 {
257         return affine[1][0] * x + affine[1][1] * y + affine[1][2];
258 }
259
260 static inline double MapZ( float affine[3][3], float x, float y )
261 {
262         return affine[2][0] * x + affine[2][1] * y + affine[2][2];
263 }
264
265 #define MAX( x, y ) x > y ? x : y
266 #define MIN( x, y ) x < y ? x : y
267
268 static void affine_max_output( float affine[3][3], float *w, float *h, float dz, float max_width, float max_height )
269 {
270         int tlx = MapX( affine, -max_width,  max_height ) / dz;
271         int tly = MapY( affine, -max_width,  max_height ) / dz;
272         int trx = MapX( affine,  max_width,  max_height ) / dz;
273         int try = MapY( affine,  max_width,  max_height ) / dz;
274         int blx = MapX( affine, -max_width, -max_height ) / dz;
275         int bly = MapY( affine, -max_width, -max_height ) / dz;
276         int brx = MapX( affine,  max_width, -max_height ) / dz;
277         int bry = MapY( affine,  max_width, -max_height ) / dz;
278
279         int max_x;
280         int max_y;
281         int min_x;
282         int min_y;
283
284         max_x = MAX( tlx, trx );
285         max_x = MAX( max_x, blx );
286         max_x = MAX( max_x, brx );
287
288         min_x = MIN( tlx, trx );
289         min_x = MIN( min_x, blx );
290         min_x = MIN( min_x, brx );
291
292         max_y = MAX( tly, try );
293         max_y = MAX( max_y, bly );
294         max_y = MAX( max_y, bry );
295
296         min_y = MIN( tly, try );
297         min_y = MIN( min_y, bly );
298         min_y = MIN( min_y, bry );
299
300         *w = ( float )( max_x - min_x + 1 ) / max_width / 2.0;
301         *h = ( float )( max_y - min_y + 1 ) / max_height / 2.0;
302 }
303
304 #define IN_RANGE( v, r )        ( v >= - r / 2 && v < r / 2 )
305
306 static inline void get_affine( affine_t *affine, mlt_transition transition, float position )
307 {
308         mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition );
309         int keyed = mlt_properties_get_int( properties, "keyed" );
310
311         if ( keyed == 0 )
312         {
313                 float fix_rotate_x = mlt_properties_get_double( properties, "fix_rotate_x" );
314                 float fix_rotate_y = mlt_properties_get_double( properties, "fix_rotate_y" );
315                 float fix_rotate_z = mlt_properties_get_double( properties, "fix_rotate_z" );
316                 float rotate_x = mlt_properties_get_double( properties, "rotate_x" );
317                 float rotate_y = mlt_properties_get_double( properties, "rotate_y" );
318                 float rotate_z = mlt_properties_get_double( properties, "rotate_z" );
319                 float fix_shear_x = mlt_properties_get_double( properties, "fix_shear_x" );
320                 float fix_shear_y = mlt_properties_get_double( properties, "fix_shear_y" );
321                 float fix_shear_z = mlt_properties_get_double( properties, "fix_shear_z" );
322                 float shear_x = mlt_properties_get_double( properties, "shear_x" );
323                 float shear_y = mlt_properties_get_double( properties, "shear_y" );
324                 float shear_z = mlt_properties_get_double( properties, "shear_z" );
325                 float ox = mlt_properties_get_double( properties, "ox" );
326                 float oy = mlt_properties_get_double( properties, "oy" );
327
328                 affine_rotate_x( affine->matrix, fix_rotate_x + rotate_x * position );
329                 affine_rotate_y( affine->matrix, fix_rotate_y + rotate_y * position );
330                 affine_rotate_z( affine->matrix, fix_rotate_z + rotate_z * position );
331                 affine_shear( affine->matrix,
332                                           fix_shear_x + shear_x * position,
333                                           fix_shear_y + shear_y * position,
334                                           fix_shear_z + shear_z * position );
335                 affine_offset( affine->matrix, ox, oy );
336         }
337         else
338         {
339                 float rotate_x = composite_calculate_key( transition, "rotate_x", "rotate_x_info", 360, position );
340                 float rotate_y = composite_calculate_key( transition, "rotate_y", "rotate_y_info", 360, position );
341                 float rotate_z = composite_calculate_key( transition, "rotate_z", "rotate_z_info", 360, position );
342                 float shear_x = composite_calculate_key( transition, "shear_x", "shear_x_info", 360, position );
343                 float shear_y = composite_calculate_key( transition, "shear_y", "shear_y_info", 360, position );
344                 float shear_z = composite_calculate_key( transition, "shear_z", "shear_z_info", 360, position );
345                 float o_x = composite_calculate_key( transition, "ox", "ox_info", 0, position );
346                 float o_y = composite_calculate_key( transition, "oy", "oy_info", 0, position );
347                 
348                 affine_rotate_x( affine->matrix, rotate_x );
349                 affine_rotate_y( affine->matrix, rotate_y );
350                 affine_rotate_z( affine->matrix, rotate_z );
351                 affine_shear( affine->matrix, shear_x, shear_y, shear_z );
352                 affine_offset( affine->matrix, o_x, o_y );
353         }
354 }
355
356 /** Get the image.
357 */
358
359 static int transition_get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
360 {
361         // Get the b frame from the stack
362         mlt_frame b_frame = mlt_frame_pop_frame( a_frame );
363
364         // Get the transition object
365         mlt_transition transition = mlt_frame_pop_service( a_frame );
366
367         // Get the properties of the transition
368         mlt_properties properties = MLT_TRANSITION_PROPERTIES( transition );
369
370         // Get the properties of the a frame
371         mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame );
372
373         // Get the properties of the b frame
374         mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
375
376         // Image, format, width, height and image for the b frame
377         uint8_t *b_image = NULL;
378         mlt_image_format b_format = mlt_image_rgb24a;
379         int b_width;
380         int b_height;
381
382         // Assign the current position
383         mlt_position position =  mlt_transition_get_position( transition, a_frame );
384
385         int mirror = mlt_properties_get_position( properties, "mirror" );
386         int length = mlt_transition_get_length( transition );
387         if ( mlt_properties_get_int( properties, "always_active" ) )
388         {
389                 mlt_properties props = mlt_properties_get_data( b_props, "_producer", NULL );
390                 mlt_position in = mlt_properties_get_int( props, "in" );
391                 mlt_position out = mlt_properties_get_int( props, "out" );
392                 length = out - in + 1;
393         }
394
395         // Obtain the normalised width and height from the a_frame
396         mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE( transition ) );
397         int normalised_width = profile->width;
398         int normalised_height = profile->height;
399
400         double consumer_ar = mlt_properties_get_double( a_props, "consumer_aspect_ratio" );
401
402         // Structures for geometry
403         struct mlt_geometry_item_s result;
404
405         if ( mirror && position > length / 2 )
406                 position = abs( position - length );
407
408         // Fetch the a frame image
409         *format = mlt_image_rgb24a;
410         mlt_frame_get_image( a_frame, image, format, width, height, 1 );
411
412         // Calculate the region now
413         mlt_service_lock( MLT_TRANSITION_SERVICE( transition ) );
414         composite_calculate( transition, &result, normalised_width, normalised_height, ( float )position );
415         mlt_service_unlock( MLT_TRANSITION_SERVICE( transition ) );
416
417         // Fetch the b frame image
418         result.w = ( result.w * *width / normalised_width );
419         result.h = ( result.h * *height / normalised_height );
420         result.x = ( result.x * *width / normalised_width );
421         result.y = ( result.y * *height / normalised_height );
422
423         // Request full resolution of b frame image.
424         b_width = mlt_properties_get_int( b_props, "real_width" );
425         b_height = mlt_properties_get_int( b_props, "real_height" );
426         mlt_properties_set_int( b_props, "rescale_width", b_width );
427         mlt_properties_set_int( b_props, "rescale_height", b_height );
428
429         // Suppress padding and aspect normalization.
430         char *interps = mlt_properties_get( a_props, "rescale.interp" );
431         if ( interps )
432                 interps = strdup( interps );
433         mlt_properties_set( b_props, "rescale.interp", "none" );
434
435         // This is not a field-aware transform.
436         mlt_properties_set_int( b_props, "consumer_deinterlace", 1 );
437
438         mlt_frame_get_image( b_frame, &b_image, &b_format, &b_width, &b_height, 0 );
439
440         // Check that both images are of the correct format and process
441         if ( *format == mlt_image_rgb24a && b_format == mlt_image_rgb24a )
442         {
443                 float x, y;
444                 float dx, dy;
445                 float dz;
446                 float sw, sh;
447                 uint8_t *p = *image;
448
449                 // Get values from the transition
450                 float scale_x = mlt_properties_get_double( properties, "scale_x" );
451                 float scale_y = mlt_properties_get_double( properties, "scale_y" );
452                 int scale = mlt_properties_get_int( properties, "scale" );
453                 float geom_scale_x = (float) b_width / result.w;
454                 float geom_scale_y = (float) b_height / result.h;
455                 float cx = result.x + result.w / 2.0;
456                 float cy = result.y + result.h / 2.0;
457                 float lower_x = - cx;
458                 float lower_y = - cy;
459                 float x_offset = (float) b_width / 2.0;
460                 float y_offset = (float) b_height / 2.0;
461                 affine_t affine;
462                 interpp interp = interpBL_b32;
463                 int i, j; // loop counters
464
465                 affine_init( affine.matrix );
466
467                 // Compute the affine transform
468                 get_affine( &affine, transition, ( float )position );
469                 dz = MapZ( affine.matrix, 0, 0 );
470                 if ( ( int )abs( dz * 1000 ) < 25 )
471                 {
472                         if ( interps )
473                                 free( interps );
474                         return 0;
475                 }
476
477                 // Factor scaling into the transformation based on output resolution.
478                 if ( mlt_properties_get_int( properties, "distort" ) )
479                 {
480                         scale_x = geom_scale_x * ( scale_x == 0 ? 1 : scale_x );
481                         scale_y = geom_scale_y * ( scale_y == 0 ? 1 : scale_y );
482                 }
483                 else
484                 {
485                         // Determine scale with respect to aspect ratio.
486                         double consumer_dar = consumer_ar * normalised_width / normalised_height;
487                         double b_ar = mlt_properties_get_double( b_props, "aspect_ratio" );
488                         double b_dar = b_ar * b_width / b_height;
489                         
490                         if ( b_dar > consumer_dar )
491                         {
492                                 scale_x = geom_scale_x * ( scale_x == 0 ? 1 : scale_x );
493                                 scale_y = geom_scale_x * ( scale_y == 0 ? 1 : scale_y );
494                         }
495                         else
496                         {
497                                 scale_x = geom_scale_y * ( scale_x == 0 ? 1 : scale_x );
498                                 scale_y = geom_scale_y * ( scale_y == 0 ? 1 : scale_y );
499                         }
500                         scale_x *= consumer_ar / b_ar;
501                 }
502                 if ( scale )
503                 {
504                         affine_max_output( affine.matrix, &sw, &sh, dz, *width, *height );
505                         affine_scale( affine.matrix, sw * MIN( geom_scale_x, geom_scale_y ), sh * MIN( geom_scale_x, geom_scale_y ) );
506                 }
507                 else if ( scale_x != 0 && scale_y != 0 )
508                 {
509                         affine_scale( affine.matrix, scale_x, scale_y );
510                 }
511
512                 // Set the interpolation function
513                 if ( interps == NULL || strcmp( interps, "nearest" ) == 0 || strcmp( interps, "neighbor" ) == 0 )
514                         interp = interpNN_b32;
515                 else if ( strcmp( interps, "tiles" ) == 0 || strcmp( interps, "fast_bilinear" ) == 0 )
516                         interp = interpNN_b32;
517                 else if ( strcmp( interps, "bilinear" ) == 0 )
518                         interp = interpBL_b32;
519                 else if ( strcmp( interps, "bicubic" ) == 0 )
520                         interp = interpBC_b32;
521                  // TODO: lanczos 8x8
522                 else if ( strcmp( interps, "hyper" ) == 0 || strcmp( interps, "sinc" ) == 0 || strcmp( interps, "lanczos" ) == 0 )
523                         interp = interpBC_b32;
524                 else if ( strcmp( interps, "spline" ) == 0 ) // TODO: spline 4x4 or 6x6
525                         interp = interpBC_b32;
526
527                 // Do the transform with interpolation
528                 for ( i = 0, y = lower_y; i < *height; i++, y++ )
529                 {
530                         for ( j = 0, x = lower_x; j < *width; j++, x++ )
531                         {
532                                 dx = MapX( affine.matrix, x, y ) / dz + x_offset;
533                                 dy = MapY( affine.matrix, x, y ) / dz + y_offset;
534                                 if ( dx >= 0 && dx < (b_width - 1) && dy >=0 && dy < (b_height - 1) )
535                                         interp( b_image, b_width, b_height, dx, dy, result.mix/100.0, p );
536                                 p += 4;
537                         }
538                 }
539         }
540         if ( interps )
541                 free( interps );
542
543         return 0;
544 }
545
546 /** Affine transition processing.
547 */
548
549 static mlt_frame transition_process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame )
550 {
551         // Push the transition on to the frame
552         mlt_frame_push_service( a_frame, transition );
553
554         // Push the b_frame on to the stack
555         mlt_frame_push_frame( a_frame, b_frame );
556
557         // Push the transition method
558         mlt_frame_push_get_image( a_frame, transition_get_image );
559
560         return a_frame;
561 }
562
563 /** Constructor for the filter.
564 */
565
566 mlt_transition transition_affine_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
567 {
568         mlt_transition transition = mlt_transition_new( );
569         if ( transition != NULL )
570         {
571                 mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "distort", 0 );
572                 mlt_properties_set( MLT_TRANSITION_PROPERTIES( transition ), "geometry", "0/0:100%x100%" );
573                 // Inform apps and framework that this is a video only transition
574                 mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "_transition_type", 1 );
575                 transition->process = transition_process;
576         }
577         return transition;
578 }