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