]> git.sesse.net Git - mlt/blob - src/framework/mlt_geometry.c
A little debugging.
[mlt] / src / framework / mlt_geometry.c
1 /**
2  * \file mlt_geometry.c
3  * \brief geometry animation API (deprecated)
4  * \deprecated use mlt_animation_s instead
5  *
6  * Copyright (C) 2004-2005 Ushodaya Enterprises Limited
7  * \author Charles Yates <charles.yates@pandora.be>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include "mlt_geometry.h"
25 #include "mlt_tokeniser.h"
26 #include "mlt_factory.h"
27 #include "mlt_profile.h"
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 /** private part of geometry animation item (deprecated)
34  * \deprecated use mlt_animation_s instead
35  */
36
37 typedef struct geometry_item_s
38 {
39         struct mlt_geometry_item_s data;
40         struct geometry_item_s *next, *prev;
41 }
42 *geometry_item;
43
44 /** private part of geometry object (deprecated)
45  * \deprecated use mlt_animation_s instead
46  */
47
48 typedef struct
49 {
50         char *data;
51         int length;
52         int nw;
53         int nh;
54         geometry_item item;
55 }
56 geometry_s, *geometry;
57
58 // Create a new geometry structure
59 mlt_geometry mlt_geometry_init( )
60 {
61         mlt_geometry self = calloc( 1, sizeof( struct mlt_geometry_s ) );
62         if ( self != NULL )
63         {
64                 self->local = calloc( 1, sizeof( geometry_s ) );
65                 if ( self->local != NULL )
66                 {
67                         geometry g = self->local;
68                         g->nw = 720;
69                         g->nh = 576;
70                 }
71                 else
72                 {
73                         free( self );
74                         self = NULL;
75                 }
76         }
77         return self;
78 }
79
80 /** A linear step
81 */
82
83 static inline double linearstep( double start, double end, double position, int length )
84 {
85         double o = ( end - start ) / length;
86         return start + position * o;
87 }
88
89 void mlt_geometry_interpolate( mlt_geometry self )
90 {
91         geometry g = self->local;
92
93         // Parse of all items to ensure unspecified keys are calculated correctly
94         if ( g->item != NULL )
95         {
96                 int i = 0;
97                 for ( i = 0; i < 5; i ++ )
98                 {
99                         geometry_item current = g->item;
100                         while( current != NULL )
101                         {
102                                 int fixed = current->data.f[ i ];
103                                 if ( !fixed )
104                                 {
105                                         geometry_item prev = current->prev;
106                                         geometry_item next = current->next;
107
108                                         double prev_value = 0;
109                                         double next_value = 0;
110                                         double value = 0;
111
112                                         while( prev != NULL && !prev->data.f[ i ] ) prev = prev->prev;
113                                         while( next != NULL && !next->data.f[ i ] ) next = next->next;
114
115                                         switch( i )
116                                         {
117                                                 case 0:
118                                                         if ( prev ) prev_value = prev->data.x;
119                                                         if ( next ) next_value = next->data.x;
120                                                         break;
121                                                 case 1:
122                                                         if ( prev ) prev_value = prev->data.y;
123                                                         if ( next ) next_value = next->data.y;
124                                                         break;
125                                                 case 2:
126                                                         if ( prev ) prev_value = prev->data.w;
127                                                         if ( next ) next_value = next->data.w;
128                                                         break;
129                                                 case 3:
130                                                         if ( prev ) prev_value = prev->data.h;
131                                                         if ( next ) next_value = next->data.h;
132                                                         break;
133                                                 case 4:
134                                                         if ( prev ) prev_value = prev->data.mix;
135                                                         if ( next ) next_value = next->data.mix;
136                                                         break;
137                                         }
138
139                                         // This should never happen
140                                         if ( prev == NULL )
141                                                 current->data.f[ i ] = 1;
142                                         else if ( next == NULL )
143                                                 value = prev_value;
144                                         else 
145                                                 value = linearstep( prev_value, next_value, current->data.frame - prev->data.frame, next->data.frame - prev->data.frame );
146
147                                         switch( i )
148                                         {
149                                                 case 0: current->data.x = value; break;
150                                                 case 1: current->data.y = value; break;
151                                                 case 2: current->data.w = value; break;
152                                                 case 3: current->data.h = value; break;
153                                                 case 4: current->data.mix = value; break;
154                                         }
155                                 }
156
157                                 // Move to the next item
158                                 current = current->next;
159                         }
160                 }
161         }
162 }
163
164 static int mlt_geometry_drop( mlt_geometry self, geometry_item item )
165 {
166         geometry g = self->local;
167
168         if ( item == g->item )
169         {
170                 g->item = item->next;
171                 if ( g->item != NULL )
172                         g->item->prev = NULL;
173                 // To ensure correct seeding, ensure all values are fixed
174                 if ( g->item != NULL )
175                 {
176                         g->item->data.f[0] = 1;
177                         g->item->data.f[1] = 1;
178                         g->item->data.f[2] = 1;
179                         g->item->data.f[3] = 1;
180                         g->item->data.f[4] = 1;
181                 }
182         }
183         else if ( item->next != NULL && item->prev != NULL )
184         {
185                 item->prev->next = item->next;
186                 item->next->prev = item->prev;
187         }
188         else if ( item->next != NULL )
189         {
190                 item->next->prev = item->prev;
191         }
192         else if ( item->prev != NULL )
193         {
194                 item->prev->next = item->next;
195         }
196
197         free( item );
198
199         return 0;
200 }
201
202 static void mlt_geometry_clean( mlt_geometry self )
203 {
204         geometry g = self->local;
205         if ( g->data )
206                 free( g->data );
207         g->data = NULL;
208         while( g->item )
209                 mlt_geometry_drop( self, g->item );
210 }
211
212 // Parse the geometry specification for a given length and normalised width/height (-1 for default)
213 // data is constructed as: [frame=]X/Y:WxH[:mix][!][;[frame=]X/Y:WxH[:mix][!]]*
214 // and X, Y, W and H can have trailing % chars to indicate percentage of normalised size
215 // Append a pair's value with ! to enable distort.
216 int mlt_geometry_parse( mlt_geometry self, char *data, int length, int nw, int nh )
217 {
218         int i = 0;
219
220         // Create a tokeniser
221         mlt_tokeniser tokens = mlt_tokeniser_init( );
222
223         // Get the local/private structure
224         geometry g = self->local;
225
226         // Clean the existing geometry
227         mlt_geometry_clean( self );
228
229         // Update the info on the data
230         if ( length != -1 )
231                 g->length = length;
232         if ( nw != -1 )
233                 g->nw = nw;
234         if ( nh != -1 )
235                 g->nh = nh;
236         if ( data != NULL )
237                 g->data = strdup( data );
238
239         // Tokenise
240         if ( data != NULL )
241                 mlt_tokeniser_parse_new( tokens, data, ";" );
242
243         // Iterate through each token
244         for ( i = 0; i < mlt_tokeniser_count( tokens ); i ++ )
245         {
246                 struct mlt_geometry_item_s item;
247                 char *value = mlt_tokeniser_get_string( tokens, i );
248
249                 // If no data in keyframe, drop it (trailing semicolon)
250                 if ( value == NULL || !strcmp( value, "" ) )
251                         continue;
252
253                 // Set item to 0
254                 memset( &item, 0, sizeof( struct mlt_geometry_item_s ) );
255
256                 // Now parse the item
257                 mlt_geometry_parse_item( self, &item, value );
258
259                 // Now insert into place
260                 mlt_geometry_insert( self, &item );
261         }
262         mlt_geometry_interpolate( self );
263
264         // Remove the tokeniser
265         mlt_tokeniser_close( tokens );
266
267         // ???
268         return 0;
269 }
270
271 // Conditionally refresh in case of a change
272 int mlt_geometry_refresh( mlt_geometry self, char *data, int length, int nw, int nh )
273 {
274         geometry g = self->local;
275         int changed = ( length != -1 && length != g->length );
276         changed = changed || ( nw != -1 && nw != g->nw );
277         changed = changed || ( nh != -1 && nh != g->nh );
278         changed = changed || ( data != NULL && ( g->data == NULL || strcmp( data, g->data ) ) );
279         if ( changed )
280                 return mlt_geometry_parse( self, data, length, nw, nh );
281         return -1;
282 }
283
284 int mlt_geometry_get_length( mlt_geometry self )
285 {
286         // Get the local/private structure
287         geometry g = self->local;
288
289         // return the length
290         return g->length;
291 }
292
293 void mlt_geometry_set_length( mlt_geometry self, int length )
294 {
295         // Get the local/private structure
296         geometry g = self->local;
297
298         // set the length
299         g->length = length;
300 }
301
302 int mlt_geometry_parse_item( mlt_geometry self, mlt_geometry_item item, char *value )
303 {
304         int ret = 0;
305
306         // Get the local/private structure
307         geometry g = self->local;
308
309         if ( value != NULL && strcmp( value, "" ) )
310         {
311                 char *p = strchr( value, '=' );
312                 int count = 0;
313                 double temp;
314
315                 // Determine if a position has been specified
316                 if ( p != NULL )
317                 {
318                         temp = atof( value );
319                         if ( temp > -1 && temp < 1 )
320                                 item->frame = temp * g->length;
321                         else
322                                 item->frame = temp;
323                         value = p + 1;
324                 }
325
326                 // Special case - frame < 0
327                 if ( item->frame < 0 )
328                         item->frame += g->length;
329
330                 // Obtain the current value at this position - self allows new
331                 // frames to be created which don't specify all values
332                 mlt_geometry_fetch( self, item, item->frame );
333
334                 // Special case - when an empty string is specified, all values are fixed
335                 // TODO: Check if this is logical - it's convenient, but it's also odd...
336                 if ( !*value )
337                 {
338                         item->f[0] = 1;
339                         item->f[1] = 1;
340                         item->f[2] = 1;
341                         item->f[3] = 1;
342                         item->f[4] = 1;
343                 }
344
345                 // Iterate through the remainder of value
346                 while( *value )
347                 {
348                         // Get the value
349                         temp = strtod( value, &p );
350
351                         // Check if a value was specified
352                         if ( p != value )
353                         {
354                                 // Handle the % case
355                                 if ( *p == '%' )
356                                 {
357                                         if ( count == 0 || count == 2 )
358                                                 temp *= g->nw / 100.0;
359                                         else if ( count == 1 || count == 3 )
360                                                 temp *= g->nh / 100.0;
361                                         p ++;
362                                 }
363
364                                 // Special case - distort token
365                                 if ( *p == '!' || *p == '*' )
366                                 {
367                                         p ++;
368                                         item->distort = 1;
369                                 }
370
371                                 // Actually, we don't care about the delimiter at all..
372                                 if ( *p ) p ++;
373
374                                 // Assign to the item
375                                 switch( count )
376                                 {
377                                         case 0: item->x = temp; item->f[0] = 1; break;
378                                         case 1: item->y = temp; item->f[1] = 1; break;
379                                         case 2: item->w = temp; item->f[2] = 1; break;
380                                         case 3: item->h = temp; item->f[3] = 1; break;
381                                         case 4: item->mix = temp; item->f[4] = 1; break;
382                                 }
383                         }
384                         else
385                         {
386                                 p ++;
387                         }
388
389                         // Update the value pointer
390                         value = p;
391                         count ++;
392                 }
393         }
394         else
395         {
396                 ret = 1;
397         }
398
399         return ret;
400 }
401
402 // Fetch a geometry item for an absolute position
403 int mlt_geometry_fetch( mlt_geometry self, mlt_geometry_item item, float position )
404 {
405         // Get the local geometry
406         geometry g = self->local;
407
408         // Need to find the nearest key to the position specifed
409         geometry_item key = g->item;
410
411         // Iterate through the keys until we reach last or have 
412         while( key != NULL && key->next != NULL && position >= key->next->data.frame )
413                 key = key->next;
414
415         if ( key != NULL )
416         {
417                 // Position is situated before the first key - all zeroes
418                 if ( position < key->data.frame )
419                 {
420                         memset( item, 0, sizeof( struct mlt_geometry_item_s ) );
421                         item->mix = 100;
422                 }
423                 // Position is a key itself - no iterpolation need
424                 else if ( position == key->data.frame )
425                 {
426                         memcpy( item, &key->data, sizeof( struct mlt_geometry_item_s ) );
427                 }
428                 // Position is after the last key - no interpolation, but not a key frame
429                 else if ( key->next == NULL )
430                 {
431                         memcpy( item, &key->data, sizeof( struct mlt_geometry_item_s ) );
432                         item->key = 0;
433                         item->f[ 0 ] = 0;
434                         item->f[ 1 ] = 0;
435                         item->f[ 2 ] = 0;
436                         item->f[ 3 ] = 0;
437                         item->f[ 4 ] = 0;
438                 }
439                 // Interpolation is needed - position > key and there is a following key
440                 else
441                 {
442                         item->key = 0;
443                         item->frame = position;
444                         position -= key->data.frame;
445                         item->x = linearstep( key->data.x, key->next->data.x, position, key->next->data.frame - key->data.frame );
446                         item->y = linearstep( key->data.y, key->next->data.y, position, key->next->data.frame - key->data.frame );
447                         item->w = linearstep( key->data.w, key->next->data.w, position, key->next->data.frame - key->data.frame );
448                         item->h = linearstep( key->data.h, key->next->data.h, position, key->next->data.frame - key->data.frame );
449                         item->mix = linearstep( key->data.mix, key->next->data.mix, position, key->next->data.frame - key->data.frame );
450                         item->distort = key->data.distort;
451                         position += key->data.frame;
452                 }
453
454                 item->frame = position;
455         }
456         else
457         {
458                 memset( item, 0, sizeof( struct mlt_geometry_item_s ) );
459                 item->frame = position;
460                 item->mix = 100;
461         }
462
463         return key == NULL;
464 }
465
466 // Specify a geometry item at an absolute position
467 int mlt_geometry_insert( mlt_geometry self, mlt_geometry_item item )
468 {
469         // Get the local/private geometry structure
470         geometry g = self->local;
471
472         // Create a new local item (this may be removed if a key already exists at self position)
473         geometry_item gi = calloc( 1, sizeof( struct geometry_item_s ) );
474         memcpy( &gi->data, item, sizeof( struct mlt_geometry_item_s ) );
475         gi->data.key = 1;
476
477         // Determine if we need to insert or append to the list, or if it's a new list
478         if ( g->item != NULL )
479         {
480                 // Get the first item
481                 geometry_item place = g->item;
482
483                 // Locate an existing nearby item
484                 while ( place->next != NULL && item->frame > place->data.frame )
485                         place = place->next;
486
487                 if ( item->frame < place->data.frame )
488                 {
489                         if ( place == g->item )
490                                 g->item = gi;
491                         if ( place->prev )
492                                 place->prev->next = gi;
493                         gi->next = place;
494                         gi->prev = place->prev;
495                         place->prev = gi;
496                 }
497                 else if ( item->frame > place->data.frame )
498                 {
499                         if ( place->next )
500                                 place->next->prev = gi;
501                         gi->next = place->next;
502                         gi->prev = place;
503                         place->next = gi;
504                 }
505                 else
506                 {
507                         memcpy( &place->data, &gi->data, sizeof( struct mlt_geometry_item_s ) );
508                         free( gi );
509                 }
510         }
511         else
512         {
513                 // Set the first item
514                 g->item = gi;
515
516                 // To ensure correct seeding, ensure all values are fixed
517                 g->item->data.f[0] = 1;
518                 g->item->data.f[1] = 1;
519                 g->item->data.f[2] = 1;
520                 g->item->data.f[3] = 1;
521                 g->item->data.f[4] = 1;
522         }
523
524         // TODO: Error checking
525         return 0;
526 }
527
528 // Remove the key at the specified position
529 int mlt_geometry_remove( mlt_geometry self, int position )
530 {
531         int ret = 1;
532
533         // Get the local/private geometry structure
534         geometry g = self->local;
535
536         // Get the first item
537         geometry_item place = g->item;
538
539         while( place != NULL && position != place->data.frame )
540                 place = place->next;
541
542         if ( place != NULL && position == place->data.frame )
543                 ret = mlt_geometry_drop( self, place );
544
545         return ret;
546 }
547
548 // Get the key at the position or the next following
549 int mlt_geometry_next_key( mlt_geometry self, mlt_geometry_item item, int position )
550 {
551         // Get the local/private geometry structure
552         geometry g = self->local;
553
554         // Get the first item
555         geometry_item place = g->item;
556
557         while( place != NULL && position > place->data.frame )
558                 place = place->next;
559
560         if ( place != NULL )
561                 memcpy( item, &place->data, sizeof( struct mlt_geometry_item_s ) );
562
563         return place == NULL;
564 }
565
566 // Get the key at the position or the previous key
567 int mlt_geometry_prev_key( mlt_geometry self, mlt_geometry_item item, int position )
568 {
569         // Get the local/private geometry structure
570         geometry g = self->local;
571
572         // Get the first item
573         geometry_item place = g->item;
574
575         while( place != NULL && place->next != NULL && position >= place->next->data.frame )
576                 place = place->next;
577
578         if ( place != NULL )
579                 memcpy( item, &place->data, sizeof( struct mlt_geometry_item_s ) );
580
581         return place == NULL;
582 }
583
584 char *mlt_geometry_serialise_cut( mlt_geometry self, int in, int out )
585 {
586         geometry g = self->local;
587         struct mlt_geometry_item_s item;
588         char *ret = malloc( 1000 );
589         int used = 0;
590         int size = 1000;
591
592         if ( in == -1 )
593                 in = 0;
594         if ( out == -1 )
595                 out = mlt_geometry_get_length( self );
596
597         if ( ret != NULL )
598         {
599                 char temp[ 100 ];
600
601                 strcpy( ret, "" );
602
603                 item.frame = in;
604
605                 while( 1 )
606                 {
607                         strcpy( temp, "" );
608
609                         // If it's the first frame, then it's not necessarily a key
610                         if ( item.frame == in )
611                         {
612                                 if ( mlt_geometry_fetch( self, &item, item.frame ) )
613                                         break;
614
615                                 // If the first key is larger than the current position
616                                 // then do nothing here
617                                 if ( g->item->data.frame > item.frame )
618                                 {
619                                         item.frame ++;
620                                         continue;
621                                 }
622
623                                 // To ensure correct seeding, ensure all values are fixed
624                                 item.f[0] = 1;
625                                 item.f[1] = 1;
626                                 item.f[2] = 1;
627                                 item.f[3] = 1;
628                                 item.f[4] = 1;
629                         }
630                         // Typically, we move from key to key
631                         else if ( item.frame < out )
632                         {
633                                 if ( mlt_geometry_next_key( self, &item, item.frame ) )
634                                         break;
635
636                                 // Special case - crop at the out point
637                                 if ( item.frame > out )
638                                         mlt_geometry_fetch( self, &item, out );
639                         }
640                         // We've handled the last key
641                         else
642                         {
643                                 break;
644                         }
645
646                         if ( item.frame - in != 0 )
647                                 sprintf( temp, "%d=", item.frame - in );
648
649                         if ( item.f[0] )
650                                 sprintf( temp + strlen( temp ), "%g", item.x );
651                         if ( item.f[1] ) {
652                                 strcat( temp, "/" );
653                                 sprintf( temp + strlen( temp ), "%g", item.y );
654                         }
655                         if ( item.f[2] ) {
656                                 strcat( temp, ":" );
657                                 sprintf( temp + strlen( temp ), "%g", item.w );
658                         }
659                         if ( item.f[3] ) {
660                                 strcat( temp, "x" );
661                                 sprintf( temp + strlen( temp ), "%g", item.h );
662                         }
663                         if ( item.f[4] ) {
664                                 strcat( temp, ":" );
665                                 sprintf( temp + strlen( temp ), "%g", item.mix );
666                         }
667
668                         if ( used + strlen( temp ) + 2 > size ) // +2 for ';' and NULL
669                         {
670                                 size += 1000;
671                                 ret = realloc( ret, size );
672                         }
673
674                         if ( ret != NULL && used != 0 )
675                         {
676                                 used ++;
677                                 strcat( ret, ";" );
678                         }
679                         if ( ret != NULL )
680                         {
681                                 used += strlen( temp );
682                                 strcat( ret, temp );
683                         }
684
685                         item.frame ++;
686                 }
687         }
688
689         return ret;
690 }
691
692 // Serialise the current geometry
693 char *mlt_geometry_serialise( mlt_geometry self )
694 {
695         geometry g = self->local;
696         char *ret = mlt_geometry_serialise_cut( self, 0, g->length );
697         if ( ret )
698         {
699                 if ( g->data )
700                         free( g->data );
701                 g->data = ret;
702         }
703         return strdup( ret );
704 }
705
706 // Close the geometry
707 void mlt_geometry_close( mlt_geometry self )
708 {
709         if ( self != NULL )
710         {
711                 mlt_geometry_clean( self );
712                 free( self->local );
713                 free( self );
714         }
715 }
716
717