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