]> git.sesse.net Git - mlt/blob - src/modules/plus/transition_affine.c
Revise affine to use interpolation and sub-pixel positioning.
[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 this, const char *store, struct mlt_geometry_item_s *output, float position )
37 {
38         mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
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 this, 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( this );
62
63         // Try to fetch it first
64         mlt_geometry geometry = mlt_properties_get_data( properties, store, NULL );
65
66         // Get the in and out position
67         mlt_position in = mlt_transition_get_in( this );
68         mlt_position out = mlt_transition_get_out( this );
69
70         // Determine length and obtain cycle
71         int length = out - in + 1;
72         double cycle = mlt_properties_get_double( properties, "cycle" );
73
74         // Allow a geometry repeat cycle
75         if ( cycle >= 1 )
76                 length = cycle;
77         else if ( cycle > 0 )
78                 length *= cycle;
79
80         if ( geometry == NULL )
81         {
82                 // Get the new style geometry string
83                 char *property = mlt_properties_get( properties, name );
84
85                 // Create an empty geometries object
86                 geometry = mlt_geometry_init( );
87
88                 // Parse the geometry if we have one
89                 mlt_geometry_parse( geometry, property, length, normalised_width, normalised_height );
90
91                 // Store it
92                 mlt_properties_set_data( properties, store, geometry, 0, ( mlt_destructor )mlt_geometry_close, NULL );
93         }
94         else
95         {
96                 // Check for updates and refresh if necessary
97                 mlt_geometry_refresh( geometry, mlt_properties_get( properties, name ), length, normalised_width, normalised_height );
98         }
99
100         return geometry;
101 }
102
103 static mlt_geometry composite_calculate( mlt_transition this, struct mlt_geometry_item_s *result, int nw, int nh, float position )
104 {
105         // Structures for geometry
106         mlt_geometry start = transition_parse_keys( this, "geometry", "geometries", nw, nh );
107
108         // Do the calculation
109         geometry_calculate( this, "geometries", result, position );
110
111         return start;
112 }
113
114 static inline float composite_calculate_key( mlt_transition this, const char *name, const char *store, int norm, float position )
115 {
116         // Struct for the result
117         struct mlt_geometry_item_s result;
118
119         // Structures for geometry
120         transition_parse_keys( this, name, store, norm, 0 );
121
122         // Do the calculation
123         geometry_calculate( this, store, &result, position );
124
125         return result.x;
126 }
127
128 typedef struct
129 {
130         float matrix[3][3];
131 }
132 affine_t;
133
134 static void affine_init( float this[3][3] )
135 {
136         this[0][0] = 1;
137         this[0][1] = 0;
138         this[0][2] = 0;
139         this[1][0] = 0;
140         this[1][1] = 1;
141         this[1][2] = 0;
142         this[2][0] = 0;
143         this[2][1] = 0;
144         this[2][2] = 1;
145 }
146
147 // Multiply two this affine transform with that
148 static void affine_multiply( float this[3][3], float that[3][3] )
149 {
150         float output[3][3];
151         int i;
152         int j;
153
154         for ( i = 0; i < 3; i ++ )
155                 for ( j = 0; j < 3; j ++ )
156                         output[i][j] = this[i][0] * that[j][0] + this[i][1] * that[j][1] + this[i][2] * that[j][2];
157
158         this[0][0] = output[0][0];
159         this[0][1] = output[0][1];
160         this[0][2] = output[0][2];
161         this[1][0] = output[1][0];
162         this[1][1] = output[1][1];
163         this[1][2] = output[1][2];
164         this[2][0] = output[2][0];
165         this[2][1] = output[2][1];
166         this[2][2] = output[2][2];
167 }
168
169 // Rotate by a given angle
170 static void affine_rotate_x( float this[3][3], float angle )
171 {
172         float affine[3][3];
173         affine[0][0] = cos( angle * M_PI / 180 );
174         affine[0][1] = 0 - sin( angle * M_PI / 180 );
175         affine[0][2] = 0;
176         affine[1][0] = sin( angle * M_PI / 180 );
177         affine[1][1] = cos( angle * M_PI / 180 );
178         affine[1][2] = 0;
179         affine[2][0] = 0;
180         affine[2][1] = 0;
181         affine[2][2] = 1;
182         affine_multiply( this, affine );
183 }
184
185 static void affine_rotate_y( float this[3][3], float angle )
186 {
187         float affine[3][3];
188         affine[0][0] = cos( angle * M_PI / 180 );
189         affine[0][1] = 0;
190         affine[0][2] = 0 - sin( angle * M_PI / 180 );
191         affine[1][0] = 0;
192         affine[1][1] = 1;
193         affine[1][2] = 0;
194         affine[2][0] = sin( angle * M_PI / 180 );
195         affine[2][1] = 0;
196         affine[2][2] = cos( angle * M_PI / 180 );
197         affine_multiply( this, affine );
198 }
199
200 static void affine_rotate_z( float this[3][3], float angle )
201 {
202         float affine[3][3];
203         affine[0][0] = 1;
204         affine[0][1] = 0;
205         affine[0][2] = 0;
206         affine[1][0] = 0;
207         affine[1][1] = cos( angle * M_PI / 180 );
208         affine[1][2] = sin( angle * M_PI / 180 );
209         affine[2][0] = 0;
210         affine[2][1] = - sin( angle * M_PI / 180 );
211         affine[2][2] = cos( angle * M_PI / 180 );
212         affine_multiply( this, affine );
213 }
214
215 static void affine_scale( float this[3][3], float sx, float sy )
216 {
217         float affine[3][3];
218         affine[0][0] = sx;
219         affine[0][1] = 0;
220         affine[0][2] = 0;
221         affine[1][0] = 0;
222         affine[1][1] = sy;
223         affine[1][2] = 0;
224         affine[2][0] = 0;
225         affine[2][1] = 0;
226         affine[2][2] = 1;
227         affine_multiply( this, affine );
228 }
229
230 // Shear by a given value
231 static void affine_shear( float this[3][3], float shear_x, float shear_y, float shear_z )
232 {
233         float affine[3][3];
234         affine[0][0] = 1;
235         affine[0][1] = tan( shear_x * M_PI / 180 );
236         affine[0][2] = 0;
237         affine[1][0] = tan( shear_y * M_PI / 180 );
238         affine[1][1] = 1;
239         affine[1][2] = tan( shear_z * M_PI / 180 );
240         affine[2][0] = 0;
241         affine[2][1] = 0;
242         affine[2][2] = 1;
243         affine_multiply( this, affine );
244 }
245
246 static void affine_offset( float this[3][3], float x, float y )
247 {
248         this[0][2] += x;
249         this[1][2] += y;
250 }
251
252 // Obtain the mapped x coordinate of the input
253 static inline double MapX( float this[3][3], float x, float y )
254 {
255         return this[0][0] * x + this[0][1] * y + this[0][2];
256 }
257
258 // Obtain the mapped y coordinate of the input
259 static inline double MapY( float this[3][3], float x, float y )
260 {
261         return this[1][0] * x + this[1][1] * y + this[1][2];
262 }
263
264 static inline double MapZ( float this[3][3], float x, float y )
265 {
266         return this[2][0] * x + this[2][1] * y + this[2][2];
267 }
268
269 #define MAX( x, y ) x > y ? x : y
270 #define MIN( x, y ) x < y ? x : y
271
272 static void affine_max_output( float this[3][3], float *w, float *h, float dz, float max_width, float max_height )
273 {
274         int tlx = MapX( this, -max_width,  max_height ) / dz;
275         int tly = MapY( this, -max_width,  max_height ) / dz;
276         int trx = MapX( this,  max_width,  max_height ) / dz;
277         int try = MapY( this,  max_width,  max_height ) / dz;
278         int blx = MapX( this, -max_width, -max_height ) / dz;
279         int bly = MapY( this, -max_width, -max_height ) / dz;
280         int brx = MapX( this,  max_width, -max_height ) / dz;
281         int bry = MapY( this,  max_width, -max_height ) / dz;
282
283         int max_x;
284         int max_y;
285         int min_x;
286         int min_y;
287
288         max_x = MAX( tlx, trx );
289         max_x = MAX( max_x, blx );
290         max_x = MAX( max_x, brx );
291
292         min_x = MIN( tlx, trx );
293         min_x = MIN( min_x, blx );
294         min_x = MIN( min_x, brx );
295
296         max_y = MAX( tly, try );
297         max_y = MAX( max_y, bly );
298         max_y = MAX( max_y, bry );
299
300         min_y = MIN( tly, try );
301         min_y = MIN( min_y, bly );
302         min_y = MIN( min_y, bry );
303
304         *w = ( float )( max_x - min_x + 1 ) / max_width / 2.0;
305         *h = ( float )( max_y - min_y + 1 ) / max_height / 2.0;
306 }
307
308 #define IN_RANGE( v, r )        ( v >= - r / 2 && v < r / 2 )
309
310 static inline void get_affine( affine_t *affine, mlt_transition this, float position )
311 {
312         mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
313         int keyed = mlt_properties_get_int( properties, "keyed" );
314
315         if ( keyed == 0 )
316         {
317                 float fix_rotate_x = mlt_properties_get_double( properties, "fix_rotate_x" );
318                 float fix_rotate_y = mlt_properties_get_double( properties, "fix_rotate_y" );
319                 float fix_rotate_z = mlt_properties_get_double( properties, "fix_rotate_z" );
320                 float rotate_x = mlt_properties_get_double( properties, "rotate_x" );
321                 float rotate_y = mlt_properties_get_double( properties, "rotate_y" );
322                 float rotate_z = mlt_properties_get_double( properties, "rotate_z" );
323                 float fix_shear_x = mlt_properties_get_double( properties, "fix_shear_x" );
324                 float fix_shear_y = mlt_properties_get_double( properties, "fix_shear_y" );
325                 float fix_shear_z = mlt_properties_get_double( properties, "fix_shear_z" );
326                 float shear_x = mlt_properties_get_double( properties, "shear_x" );
327                 float shear_y = mlt_properties_get_double( properties, "shear_y" );
328                 float shear_z = mlt_properties_get_double( properties, "shear_z" );
329                 float ox = mlt_properties_get_double( properties, "ox" );
330                 float oy = mlt_properties_get_double( properties, "oy" );
331
332                 affine_rotate_x( affine->matrix, fix_rotate_x + rotate_x * position );
333                 affine_rotate_y( affine->matrix, fix_rotate_y + rotate_y * position );
334                 affine_rotate_z( affine->matrix, fix_rotate_z + rotate_z * position );
335                 affine_shear( affine->matrix,
336                                           fix_shear_x + shear_x * position,
337                                           fix_shear_y + shear_y * position,
338                                           fix_shear_z + shear_z * position );
339                 affine_offset( affine->matrix, ox, oy );
340         }
341         else
342         {
343                 float rotate_x = composite_calculate_key( this, "rotate_x", "rotate_x_info", 360, position );
344                 float rotate_y = composite_calculate_key( this, "rotate_y", "rotate_y_info", 360, position );
345                 float rotate_z = composite_calculate_key( this, "rotate_z", "rotate_z_info", 360, position );
346                 float shear_x = composite_calculate_key( this, "shear_x", "shear_x_info", 360, position );
347                 float shear_y = composite_calculate_key( this, "shear_y", "shear_y_info", 360, position );
348                 float shear_z = composite_calculate_key( this, "shear_z", "shear_z_info", 360, position );
349
350                 affine_rotate_x( affine->matrix, rotate_x );
351                 affine_rotate_y( affine->matrix, rotate_y );
352                 affine_rotate_z( affine->matrix, rotate_z );
353                 affine_shear( affine->matrix, shear_x, shear_y, shear_z );
354         }
355 }
356
357 /** Get the image.
358 */
359
360 static int transition_get_image( mlt_frame a_frame, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
361 {
362         // Get the b frame from the stack
363         mlt_frame b_frame = mlt_frame_pop_frame( a_frame );
364
365         // Get the transition object
366         mlt_transition this = mlt_frame_pop_service( a_frame );
367
368         // Get the properties of the transition
369         mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
370
371         // Get the properties of the a frame
372         mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame );
373
374         // Get the properties of the b frame
375         mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
376
377         // Image, format, width, height and image for the b frame
378         uint8_t *b_image = NULL;
379         mlt_image_format b_format = mlt_image_rgb24a;
380         int b_width;
381         int b_height;
382
383         // Get the unique name to retrieve the frame position
384         char *name = mlt_properties_get( properties, "_unique_id" );
385
386         // Assign the current position to the name
387         mlt_position position =  mlt_properties_get_position( a_props, name );
388         mlt_position in = mlt_properties_get_position( properties, "in" );
389         mlt_position out = mlt_properties_get_position( properties, "out" );
390         int mirror = mlt_properties_get_position( properties, "mirror" );
391         int length = out - in + 1;
392
393         // Obtain the normalised width and height from the a_frame
394         int normalised_width = mlt_properties_get_int( a_props, "normalised_width" );
395         int normalised_height = mlt_properties_get_int( a_props, "normalised_height" );
396
397         double consumer_ar = mlt_properties_get_double( a_props, "consumer_aspect_ratio" );
398         const char *interps = mlt_properties_get( b_props, "rescale.interp" );
399         mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE(this) );
400
401         // Structures for geometry
402         struct mlt_geometry_item_s result;
403
404         if ( mirror && position > length / 2 )
405                 position = abs( position - length );
406
407         // Fetch the a frame image
408         *format = mlt_image_rgb24a;
409         mlt_frame_get_image( a_frame, image, format, width, height, 1 );
410
411         // Calculate the region now
412         composite_calculate( this, &result, normalised_width, normalised_height, ( float )position );
413
414         // Fetch the b frame image
415         result.w = ( result.w * *width / normalised_width );
416         result.h = ( result.h * *height / normalised_height );
417         result.x = ( result.x * *width / normalised_width );
418         result.y = ( result.y * *height / normalised_height );
419         b_width = mlt_properties_get_int(b_props, "real_width");
420         b_height = mlt_properties_get_int(b_props, "real_height");
421         if ( 0 && b_width > result.w * 2 && b_height > result.h * 2 )
422         {
423                 // This downscale can reduce aliasing by acting as a low pass filter.
424                 b_width = b_width / 2;
425                 b_height = b_height / 2;
426         }
427         mlt_properties_set_int( b_props, "rescale_width", b_width );
428         mlt_properties_set_int( b_props, "rescale_height", b_height );
429         if ( mlt_properties_get_double( b_props, "aspect_ratio" ) == 0.0 )
430                 mlt_properties_set_double( b_props, "aspect_ratio", consumer_ar );
431         mlt_properties_set_int( b_props, "progressive", 1 );
432         if ( interps == NULL || !strcmp( interps, "none" ) )
433         {
434                 mlt_properties_set( b_props, "rescale.interp", "bilinear" );
435                 mlt_properties_set_double( b_props, "consumer_aspect_ratio", consumer_ar );
436         }
437         mlt_frame_get_image( b_frame, &b_image, &b_format, &b_width, &b_height, 0 );
438
439         // Check that both images are of the correct format and process
440         if ( *format == mlt_image_rgb24a && b_format == mlt_image_rgb24a )
441         {
442                 float x, y;
443                 float dx, dy;
444                 float dz;
445                 float sw, sh;
446                 uint8_t *p = *image;
447
448                 // Get values from the transition
449                 float scale_x = mlt_properties_get_double( properties, "scale_x" );
450                 float scale_y = mlt_properties_get_double( properties, "scale_y" );
451                 int scale = mlt_properties_get_int( properties, "scale" );
452                 float sar = (float) profile->sample_aspect_num / profile->sample_aspect_den;
453                 float geom_scale_x = (float) b_width / result.w / sar;
454                 float geom_scale_y = (float) b_height / result.h * sar;
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 upper_x = (float) *width - cx;
459                 float lower_y = - cy;
460                 float upper_y = (float) *height - cy;
461                 float x_offset = (float) b_width / 2.0;
462                 float y_offset = (float) b_height / 2.0;
463                 float sar_affine[3][3];
464                 affine_t affine;
465                 interpp interp = interpBL_b32;
466
467                 affine_init( affine.matrix );
468
469                 // Factor aspect ratio into transforms
470                 affine_init( sar_affine);
471                 sar_affine[0][0] /= sar;
472                 affine_multiply( affine.matrix, sar_affine );
473
474                 // Compute the affine transform
475                 get_affine( &affine, this, ( float )position );
476                 dz = MapZ( affine.matrix, 0, 0 );
477                 if ( ( int )abs( dz * 1000 ) < 25 )
478                         return 0;
479
480                 // Factor scaling into the transformation based on output resolution.
481                 if ( mlt_properties_get_int( properties, "distort" ) )
482                 {
483                         scale_x = geom_scale_x * ( scale_x == 0 ? 1 : scale_x );
484                         scale_y = geom_scale_y / sar * ( scale_y == 0 ? 1 : scale_y );
485                 }
486                 else
487                 {
488                         float scaling = MIN( geom_scale_x, geom_scale_y );
489                         if ( b_height / scaling > result.h / sar )
490                                 scaling = geom_scale_y / sar;
491                         else if ( b_width / scaling > result.w * sar )
492                                 scaling = geom_scale_x * sar;
493                         scale_x = scaling * ( scale_x == 0 ? 1 : scale_x );
494                         scale_y = scaling * ( scale_y == 0 ? 1 : scale_y );
495                 }
496                 if ( scale )
497                 {
498                         affine_max_output( affine.matrix, &sw, &sh, dz, profile->width, profile->height );
499                         affine_scale( affine.matrix, sw * MIN( geom_scale_x, geom_scale_y ), sh * MIN( geom_scale_x, geom_scale_y ) );
500                 }
501                 else if ( scale_x != 0 && scale_y != 0 )
502                 {
503                         affine_scale( affine.matrix, scale_x, scale_y );
504                 }
505
506                 // Invert transform aspect ratio factor
507                 sar_affine[0][0] *= sar; // return to identity matrix
508                 sar_affine[0][0] *= sar; // reverse the sample aspect adjustment
509                 affine_multiply( affine.matrix, sar_affine );
510
511                 // Set the interpolation function
512                 if ( interps == NULL || strcmp( interps, "nearest" ) == 0 || strcmp( interps, "neighbor" ) == 0 )
513                         interp = interpNN_b32;
514                 else if ( strcmp( interps, "tiles" ) == 0 || strcmp( interps, "fast_bilinear" ) == 0 )
515                         interp = interpNN_b32;
516                 else if ( strcmp( interps, "bilinear" ) == 0 )
517                         interp = interpBL_b32;
518                 else if ( strcmp( interps, "bicubic" ) == 0 )
519                         interp = interpBC_b32;
520                  // TODO: lanczos 8x8
521                 else if ( strcmp( interps, "hyper" ) == 0 || strcmp( interps, "sinc" ) == 0 || strcmp( interps, "lanczos" ) == 0 )
522                         interp = interpBC_b32;
523                 else if ( strcmp( interps, "spline" ) == 0 ) // TODO: spline 4x4 or 6x6
524                         interp = interpBC_b32;
525
526                 // Do the transform with interpolation
527                 for ( y = lower_y; y < upper_y; y ++ )
528                 {
529                         for ( x = lower_x; x < upper_x; x ++ )
530                         {
531                                 dx = MapX( affine.matrix, x, y ) / dz + x_offset;
532                                 dy = MapY( affine.matrix, x, y ) / dz + y_offset;
533                                 if ( dx >= 0 && dx < b_width && dy >=0 && dy < b_height )
534                                         interp( b_image, b_width, b_height, dx, dy, p );
535                                 p += 4;
536                         }
537                 }
538         }
539
540         return 0;
541 }
542
543 /** Affine transition processing.
544 */
545
546 static mlt_frame transition_process( mlt_transition transition, mlt_frame a_frame, mlt_frame b_frame )
547 {
548         // Get a unique name to store the frame position
549         char *name = mlt_properties_get( MLT_TRANSITION_PROPERTIES( transition ), "_unique_id" );
550
551         // Assign the current position to the name
552         mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame );
553         mlt_properties_set_position( a_props, name, mlt_frame_get_position( a_frame ) );
554
555         // Push the transition on to the frame
556         mlt_frame_push_service( a_frame, transition );
557
558         // Push the b_frame on to the stack
559         mlt_frame_push_frame( a_frame, b_frame );
560
561         // Push the transition method
562         mlt_frame_push_get_image( a_frame, transition_get_image );
563
564         return a_frame;
565 }
566
567 /** Constructor for the filter.
568 */
569
570 mlt_transition transition_affine_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
571 {
572         mlt_transition transition = mlt_transition_new( );
573         if ( transition != NULL )
574         {
575                 mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "distort", 0 );
576                 mlt_properties_set( MLT_TRANSITION_PROPERTIES( transition ), "geometry", "0,0:100%x100%" );
577                 // Inform apps and framework that this is a video only transition
578                 mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "_transition_type", 1 );
579                 transition->process = transition_process;
580         }
581         return transition;
582 }