2 * mlt_geometry.c -- provides the geometry API
3 * Copyright (C) 2004-2005 Ushodaya Enterprises Limited
4 * Author: Charles Yates <charles.yates@pandora.be>
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.
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.
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
21 #include "mlt_geometry.h"
22 #include "mlt_tokeniser.h"
23 #include "mlt_factory.h"
24 #include "mlt_profile.h"
30 typedef struct geometry_item_s
32 struct mlt_geometry_item_s data;
33 struct geometry_item_s *next, *prev;
45 geometry_s, *geometry;
47 // Create a new geometry structure
48 mlt_geometry mlt_geometry_init( )
50 mlt_geometry self = calloc( 1, sizeof( struct mlt_geometry_s ) );
53 self->local = calloc( 1, sizeof( geometry_s ) );
54 if ( self->local != NULL )
56 geometry g = self->local;
72 static inline double linearstep( double start, double end, double position, int length )
74 double o = ( end - start ) / length;
75 return start + position * o;
78 static void mlt_geometry_virtual_refresh( mlt_geometry self )
80 geometry g = self->local;
82 // Parse of all items to ensure unspecified keys are calculated correctly
83 if ( g->item != NULL )
86 for ( i = 0; i < 5; i ++ )
88 geometry_item current = g->item;
89 while( current != NULL )
91 int fixed = current->data.f[ i ];
94 geometry_item prev = current->prev;
95 geometry_item next = current->next;
97 double prev_value = 0;
98 double next_value = 0;
101 while( prev != NULL && !prev->data.f[ i ] ) prev = prev->prev;
102 while( next != NULL && !next->data.f[ i ] ) next = next->next;
107 if ( prev ) prev_value = prev->data.x;
108 if ( next ) next_value = next->data.x;
111 if ( prev ) prev_value = prev->data.y;
112 if ( next ) next_value = next->data.y;
115 if ( prev ) prev_value = prev->data.w;
116 if ( next ) next_value = next->data.w;
119 if ( prev ) prev_value = prev->data.h;
120 if ( next ) next_value = next->data.h;
123 if ( prev ) prev_value = prev->data.mix;
124 if ( next ) next_value = next->data.mix;
128 // This should never happen
130 current->data.f[ i ] = 1;
131 else if ( next == NULL )
134 value = linearstep( prev_value, next_value, current->data.frame - prev->data.frame, next->data.frame - prev->data.frame );
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;
146 // Move to the next item
147 current = current->next;
153 static int mlt_geometry_drop( mlt_geometry self, geometry_item item )
155 geometry g = self->local;
157 if ( item == g->item )
159 g->item = item->next;
160 if ( g->item != NULL )
161 g->item->prev = NULL;
162 // To ensure correct seeding, ensure all values are fixed
163 if ( g->item != NULL )
165 g->item->data.f[0] = 1;
166 g->item->data.f[1] = 1;
167 g->item->data.f[2] = 1;
168 g->item->data.f[3] = 1;
169 g->item->data.f[4] = 1;
172 else if ( item->next != NULL && item->prev != NULL )
174 item->prev->next = item->next;
175 item->next->prev = item->prev;
177 else if ( item->next != NULL )
179 item->next->prev = item->prev;
181 else if ( item->prev != NULL )
183 item->prev->next = item->next;
191 static void mlt_geometry_clean( mlt_geometry self )
193 geometry g = self->local;
198 mlt_geometry_drop( self, g->item );
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 // Append a pair's value with ! to enable distort.
205 int mlt_geometry_parse( mlt_geometry self, char *data, int length, int nw, int nh )
209 // Create a tokeniser
210 mlt_tokeniser tokens = mlt_tokeniser_init( );
212 // Get the local/private structure
213 geometry g = self->local;
215 // Clean the existing geometry
216 mlt_geometry_clean( self );
218 // Update the info on the data
226 g->data = strdup( data );
230 mlt_tokeniser_parse_new( tokens, data, ";" );
232 // Iterate through each token
233 for ( i = 0; i < mlt_tokeniser_count( tokens ); i ++ )
235 struct mlt_geometry_item_s item;
236 char *value = mlt_tokeniser_get_string( tokens, i );
239 memset( &item, 0, sizeof( struct mlt_geometry_item_s ) );
241 // Now parse the item
242 mlt_geometry_parse_item( self, &item, value );
244 // Now insert into place
245 mlt_geometry_insert( self, &item );
248 // Remove the tokeniser
249 mlt_tokeniser_close( tokens );
255 // Conditionally refresh in case of a change
256 int mlt_geometry_refresh( mlt_geometry self, char *data, int length, int nw, int nh )
258 geometry g = self->local;
259 int changed = ( length != -1 && length != g->length );
260 changed = changed || ( nw != -1 && nw != g->nw );
261 changed = changed || ( nh != -1 && nh != g->nh );
262 changed = changed || ( data != NULL && ( g->data == NULL || strcmp( data, g->data ) ) );
264 return mlt_geometry_parse( self, data, length, nw, nh );
268 int mlt_geometry_get_length( mlt_geometry self )
270 // Get the local/private structure
271 geometry g = self->local;
277 void mlt_geometry_set_length( mlt_geometry self, int length )
279 // Get the local/private structure
280 geometry g = self->local;
286 int mlt_geometry_parse_item( mlt_geometry self, mlt_geometry_item item, char *value )
290 // Get the local/private structure
291 geometry g = self->local;
293 if ( value != NULL && strcmp( value, "" ) )
295 char *p = strchr( value, '=' );
299 // Determine if a position has been specified
302 temp = atof( value );
303 if ( temp > -1 && temp < 1 )
304 item->frame = temp * g->length;
310 // Special case - frame < 0
311 if ( item->frame < 0 )
312 item->frame += g->length;
314 // Obtain the current value at this position - self allows new
315 // frames to be created which don't specify all values
316 mlt_geometry_fetch( self, item, item->frame );
318 // Special case - when an empty string is specified, all values are fixed
319 // TODO: Check if this is logical - it's convenient, but it's also odd...
329 // Iterate through the remainder of value
333 temp = strtod( value, &p );
335 // Check if a value was specified
341 if ( count == 0 || count == 2 )
342 temp *= g->nw / 100.0;
343 else if ( count == 1 || count == 3 )
344 temp *= g->nh / 100.0;
348 // Special case - distort token
349 if ( *p == '!' || *p == '*' )
355 // Actually, we don't care about the delimiter at all..
358 // Assign to the item
361 case 0: item->x = temp; item->f[0] = 1; break;
362 case 1: item->y = temp; item->f[1] = 1; break;
363 case 2: item->w = temp; item->f[2] = 1; break;
364 case 3: item->h = temp; item->f[3] = 1; break;
365 case 4: item->mix = temp; item->f[4] = 1; break;
373 // Update the value pointer
386 // Fetch a geometry item for an absolute position
387 int mlt_geometry_fetch( mlt_geometry self, mlt_geometry_item item, float position )
389 // Get the local geometry
390 geometry g = self->local;
392 // Need to find the nearest key to the position specifed
393 geometry_item key = g->item;
395 // Iterate through the keys until we reach last or have
396 while( key != NULL && key->next != NULL && position >= key->next->data.frame )
401 // Position is situated before the first key - all zeroes
402 if ( position < key->data.frame )
404 memset( item, 0, sizeof( struct mlt_geometry_item_s ) );
407 // Position is a key itself - no iterpolation need
408 else if ( position == key->data.frame )
410 memcpy( item, &key->data, sizeof( struct mlt_geometry_item_s ) );
412 // Position is after the last key - no interpolation, but not a key frame
413 else if ( key->next == NULL )
415 memcpy( item, &key->data, sizeof( struct mlt_geometry_item_s ) );
423 // Interpolation is needed - position > key and there is a following key
427 item->frame = position;
428 position -= key->data.frame;
429 item->x = linearstep( key->data.x, key->next->data.x, position, key->next->data.frame - key->data.frame );
430 item->y = linearstep( key->data.y, key->next->data.y, position, key->next->data.frame - key->data.frame );
431 item->w = linearstep( key->data.w, key->next->data.w, position, key->next->data.frame - key->data.frame );
432 item->h = linearstep( key->data.h, key->next->data.h, position, key->next->data.frame - key->data.frame );
433 item->mix = linearstep( key->data.mix, key->next->data.mix, position, key->next->data.frame - key->data.frame );
434 item->distort = key->data.distort;
435 position += key->data.frame;
438 item->frame = position;
442 memset( item, 0, sizeof( struct mlt_geometry_item_s ) );
443 item->frame = position;
450 // Specify a geometry item at an absolute position
451 int mlt_geometry_insert( mlt_geometry self, mlt_geometry_item item )
453 // Get the local/private geometry structure
454 geometry g = self->local;
456 // Create a new local item (this may be removed if a key already exists at self position)
457 geometry_item gi = calloc( 1, sizeof( struct geometry_item_s ) );
458 memcpy( &gi->data, item, sizeof( struct mlt_geometry_item_s ) );
461 // Determine if we need to insert or append to the list, or if it's a new list
462 if ( g->item != NULL )
464 // Get the first item
465 geometry_item place = g->item;
467 // Locate an existing nearby item
468 while ( place->next != NULL && item->frame > place->data.frame )
471 if ( item->frame < place->data.frame )
473 if ( place == g->item )
476 place->prev->next = gi;
478 gi->prev = place->prev;
481 else if ( item->frame > place->data.frame )
484 place->next->prev = gi;
485 gi->next = place->next;
491 memcpy( &place->data, &gi->data, sizeof( struct mlt_geometry_item_s ) );
497 // Set the first item
500 // To ensure correct seeding, ensure all values are fixed
501 g->item->data.f[0] = 1;
502 g->item->data.f[1] = 1;
503 g->item->data.f[2] = 1;
504 g->item->data.f[3] = 1;
505 g->item->data.f[4] = 1;
508 // Refresh all geometries
509 mlt_geometry_virtual_refresh( self );
511 // TODO: Error checking
515 // Remove the key at the specified position
516 int mlt_geometry_remove( mlt_geometry self, int position )
520 // Get the local/private geometry structure
521 geometry g = self->local;
523 // Get the first item
524 geometry_item place = g->item;
526 while( place != NULL && position != place->data.frame )
529 if ( place != NULL && position == place->data.frame )
530 ret = mlt_geometry_drop( self, place );
532 // Refresh all geometries
533 mlt_geometry_virtual_refresh( self );
538 // Get the key at the position or the next following
539 int mlt_geometry_next_key( mlt_geometry self, mlt_geometry_item item, int position )
541 // Get the local/private geometry structure
542 geometry g = self->local;
544 // Get the first item
545 geometry_item place = g->item;
547 while( place != NULL && position > place->data.frame )
551 memcpy( item, &place->data, sizeof( struct mlt_geometry_item_s ) );
553 return place == NULL;
556 // Get the key at the position or the previous key
557 int mlt_geometry_prev_key( mlt_geometry self, mlt_geometry_item item, int position )
559 // Get the local/private geometry structure
560 geometry g = self->local;
562 // Get the first item
563 geometry_item place = g->item;
565 while( place != NULL && place->next != NULL && position >= place->next->data.frame )
569 memcpy( item, &place->data, sizeof( struct mlt_geometry_item_s ) );
571 return place == NULL;
574 #define ISINT(x) ( (x) == (int) (x) )
575 #define PICKFMT(x) ( ISINT(x) ? "%.0f" : "%f" )
577 char *mlt_geometry_serialise_cut( mlt_geometry self, int in, int out )
579 geometry g = self->local;
580 struct mlt_geometry_item_s item;
581 char *ret = malloc( 1000 );
588 out = mlt_geometry_get_length( self );
602 // If it's the first frame, then it's not necessarily a key
603 if ( item.frame == in )
605 if ( mlt_geometry_fetch( self, &item, item.frame ) )
608 // If the first key is larger than the current position
609 // then do nothing here
610 if ( g->item->data.frame > item.frame )
616 // To ensure correct seeding, ensure all values are fixed
623 // Typically, we move from key to key
624 else if ( item.frame < out )
626 if ( mlt_geometry_next_key( self, &item, item.frame ) )
629 // Special case - crop at the out point
630 if ( item.frame > out )
631 mlt_geometry_fetch( self, &item, out );
633 // We've handled the last key
639 if ( item.frame - in != 0 )
640 sprintf( temp, "%d=", item.frame - in );
643 sprintf( temp + strlen( temp ), PICKFMT( item.x ), item.x );
646 sprintf( temp + strlen( temp ), PICKFMT( item.y ), item.y );
650 sprintf( temp + strlen( temp ), PICKFMT( item.w ), item.w );
654 sprintf( temp + strlen( temp ), PICKFMT( item.h ), item.h );
658 sprintf( temp + strlen( temp ), PICKFMT( item.mix ), item.mix );
661 if ( used + strlen( temp ) > size )
664 ret = realloc( ret, size );
667 if ( ret != NULL && used != 0 )
674 used += strlen( temp );
685 // Serialise the current geometry
686 char *mlt_geometry_serialise( mlt_geometry self )
688 geometry g = self->local;
689 char *ret = mlt_geometry_serialise_cut( self, 0, g->length );
699 // Close the geometry
700 void mlt_geometry_close( mlt_geometry self )
704 mlt_geometry_clean( self );