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