3 * \brief geometry animation API (deprecated)
4 * \deprecated use mlt_animation_s instead
6 * Copyright (C) 2004-2005 Ushodaya Enterprises Limited
7 * \author Charles Yates <charles.yates@pandora.be>
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include "mlt_geometry.h"
25 #include "mlt_tokeniser.h"
26 #include "mlt_factory.h"
27 #include "mlt_profile.h"
33 /** private part of geometry animation item (deprecated)
34 * \deprecated use mlt_animation_s instead
37 typedef struct geometry_item_s
39 struct mlt_geometry_item_s data;
40 struct geometry_item_s *next, *prev;
44 /** private part of geometry object (deprecated)
45 * \deprecated use mlt_animation_s instead
56 geometry_s, *geometry;
58 // Create a new geometry structure
59 mlt_geometry mlt_geometry_init( )
61 mlt_geometry self = calloc( 1, sizeof( struct mlt_geometry_s ) );
64 self->local = calloc( 1, sizeof( geometry_s ) );
65 if ( self->local != NULL )
67 geometry g = self->local;
83 static inline double linearstep( double start, double end, double position, int length )
85 double o = ( end - start ) / length;
86 return start + position * o;
89 void mlt_geometry_interpolate( mlt_geometry self )
91 geometry g = self->local;
93 // Parse of all items to ensure unspecified keys are calculated correctly
94 if ( g->item != NULL )
97 for ( i = 0; i < 5; i ++ )
99 geometry_item current = g->item;
100 while( current != NULL )
102 int fixed = current->data.f[ i ];
105 geometry_item prev = current->prev;
106 geometry_item next = current->next;
108 double prev_value = 0;
109 double next_value = 0;
112 while( prev != NULL && !prev->data.f[ i ] ) prev = prev->prev;
113 while( next != NULL && !next->data.f[ i ] ) next = next->next;
118 if ( prev ) prev_value = prev->data.x;
119 if ( next ) next_value = next->data.x;
122 if ( prev ) prev_value = prev->data.y;
123 if ( next ) next_value = next->data.y;
126 if ( prev ) prev_value = prev->data.w;
127 if ( next ) next_value = next->data.w;
130 if ( prev ) prev_value = prev->data.h;
131 if ( next ) next_value = next->data.h;
134 if ( prev ) prev_value = prev->data.mix;
135 if ( next ) next_value = next->data.mix;
139 // This should never happen
141 current->data.f[ i ] = 1;
142 else if ( next == NULL )
145 value = linearstep( prev_value, next_value, current->data.frame - prev->data.frame, next->data.frame - prev->data.frame );
149 case 0: current->data.x = value; break;
150 case 1: current->data.y = value; break;
151 case 2: current->data.w = value; break;
152 case 3: current->data.h = value; break;
153 case 4: current->data.mix = value; break;
157 // Move to the next item
158 current = current->next;
164 static int mlt_geometry_drop( mlt_geometry self, geometry_item item )
166 geometry g = self->local;
168 if ( item == g->item )
170 g->item = item->next;
171 if ( g->item != NULL )
172 g->item->prev = NULL;
173 // To ensure correct seeding, ensure all values are fixed
174 if ( g->item != NULL )
176 g->item->data.f[0] = 1;
177 g->item->data.f[1] = 1;
178 g->item->data.f[2] = 1;
179 g->item->data.f[3] = 1;
180 g->item->data.f[4] = 1;
183 else if ( item->next != NULL && item->prev != NULL )
185 item->prev->next = item->next;
186 item->next->prev = item->prev;
188 else if ( item->next != NULL )
190 item->next->prev = item->prev;
192 else if ( item->prev != NULL )
194 item->prev->next = item->next;
202 static void mlt_geometry_clean( mlt_geometry self )
204 geometry g = self->local;
209 mlt_geometry_drop( self, g->item );
212 // Parse the geometry specification for a given length and normalised width/height (-1 for default)
213 // data is constructed as: [frame=]X/Y:WxH[:mix][!][;[frame=]X/Y:WxH[:mix][!]]*
214 // and X, Y, W and H can have trailing % chars to indicate percentage of normalised size
215 // Append a pair's value with ! to enable distort.
216 int mlt_geometry_parse( mlt_geometry self, char *data, int length, int nw, int nh )
220 // Create a tokeniser
221 mlt_tokeniser tokens = mlt_tokeniser_init( );
223 // Get the local/private structure
224 geometry g = self->local;
226 // Clean the existing geometry
227 mlt_geometry_clean( self );
229 // Update the info on the data
237 g->data = strdup( data );
241 mlt_tokeniser_parse_new( tokens, data, ";" );
243 // Iterate through each token
244 for ( i = 0; i < mlt_tokeniser_count( tokens ); i ++ )
246 struct mlt_geometry_item_s item;
247 char *value = mlt_tokeniser_get_string( tokens, i );
249 // If no data in keyframe, drop it (trailing semicolon)
250 if ( value == NULL || !strcmp( value, "" ) )
254 memset( &item, 0, sizeof( struct mlt_geometry_item_s ) );
256 // Now parse the item
257 mlt_geometry_parse_item( self, &item, value );
259 // Now insert into place
260 mlt_geometry_insert( self, &item );
262 mlt_geometry_interpolate( self );
264 // Remove the tokeniser
265 mlt_tokeniser_close( tokens );
271 // Conditionally refresh in case of a change
272 int mlt_geometry_refresh( mlt_geometry self, char *data, int length, int nw, int nh )
274 geometry g = self->local;
275 int changed = ( length != -1 && length != g->length );
276 changed = changed || ( nw != -1 && nw != g->nw );
277 changed = changed || ( nh != -1 && nh != g->nh );
278 changed = changed || ( data != NULL && ( g->data == NULL || strcmp( data, g->data ) ) );
280 return mlt_geometry_parse( self, data, length, nw, nh );
284 int mlt_geometry_get_length( mlt_geometry self )
286 // Get the local/private structure
287 geometry g = self->local;
293 void mlt_geometry_set_length( mlt_geometry self, int length )
295 // Get the local/private structure
296 geometry g = self->local;
302 int mlt_geometry_parse_item( mlt_geometry self, mlt_geometry_item item, char *value )
306 // Get the local/private structure
307 geometry g = self->local;
309 if ( value != NULL && strcmp( value, "" ) )
311 char *p = strchr( value, '=' );
315 // Determine if a position has been specified
318 temp = atof( value );
319 if ( temp > -1 && temp < 1 )
320 item->frame = temp * g->length;
326 // Special case - frame < 0
327 if ( item->frame < 0 )
328 item->frame += g->length;
330 // Obtain the current value at this position - self allows new
331 // frames to be created which don't specify all values
332 mlt_geometry_fetch( self, item, item->frame );
334 // Special case - when an empty string is specified, all values are fixed
335 // TODO: Check if this is logical - it's convenient, but it's also odd...
345 // Iterate through the remainder of value
349 temp = strtod( value, &p );
351 // Check if a value was specified
357 if ( count == 0 || count == 2 )
358 temp *= g->nw / 100.0;
359 else if ( count == 1 || count == 3 )
360 temp *= g->nh / 100.0;
364 // Special case - distort token
365 if ( *p == '!' || *p == '*' )
371 // Actually, we don't care about the delimiter at all..
374 // Assign to the item
377 case 0: item->x = temp; item->f[0] = 1; break;
378 case 1: item->y = temp; item->f[1] = 1; break;
379 case 2: item->w = temp; item->f[2] = 1; break;
380 case 3: item->h = temp; item->f[3] = 1; break;
381 case 4: item->mix = temp; item->f[4] = 1; break;
389 // Update the value pointer
402 // Fetch a geometry item for an absolute position
403 int mlt_geometry_fetch( mlt_geometry self, mlt_geometry_item item, float position )
405 // Get the local geometry
406 geometry g = self->local;
408 // Need to find the nearest key to the position specifed
409 geometry_item key = g->item;
411 // Iterate through the keys until we reach last or have
412 while( key != NULL && key->next != NULL && position >= key->next->data.frame )
417 // Position is situated before the first key - all zeroes
418 if ( position < key->data.frame )
420 memset( item, 0, sizeof( struct mlt_geometry_item_s ) );
423 // Position is a key itself - no iterpolation need
424 else if ( position == key->data.frame )
426 memcpy( item, &key->data, sizeof( struct mlt_geometry_item_s ) );
428 // Position is after the last key - no interpolation, but not a key frame
429 else if ( key->next == NULL )
431 memcpy( item, &key->data, sizeof( struct mlt_geometry_item_s ) );
439 // Interpolation is needed - position > key and there is a following key
443 item->frame = position;
444 position -= key->data.frame;
445 item->x = linearstep( key->data.x, key->next->data.x, position, key->next->data.frame - key->data.frame );
446 item->y = linearstep( key->data.y, key->next->data.y, position, key->next->data.frame - key->data.frame );
447 item->w = linearstep( key->data.w, key->next->data.w, position, key->next->data.frame - key->data.frame );
448 item->h = linearstep( key->data.h, key->next->data.h, position, key->next->data.frame - key->data.frame );
449 item->mix = linearstep( key->data.mix, key->next->data.mix, position, key->next->data.frame - key->data.frame );
450 item->distort = key->data.distort;
451 position += key->data.frame;
454 item->frame = position;
458 memset( item, 0, sizeof( struct mlt_geometry_item_s ) );
459 item->frame = position;
466 // Specify a geometry item at an absolute position
467 int mlt_geometry_insert( mlt_geometry self, mlt_geometry_item item )
469 // Get the local/private geometry structure
470 geometry g = self->local;
472 // Create a new local item (this may be removed if a key already exists at self position)
473 geometry_item gi = calloc( 1, sizeof( struct geometry_item_s ) );
474 memcpy( &gi->data, item, sizeof( struct mlt_geometry_item_s ) );
477 // Determine if we need to insert or append to the list, or if it's a new list
478 if ( g->item != NULL )
480 // Get the first item
481 geometry_item place = g->item;
483 // Locate an existing nearby item
484 while ( place->next != NULL && item->frame > place->data.frame )
487 if ( item->frame < place->data.frame )
489 if ( place == g->item )
492 place->prev->next = gi;
494 gi->prev = place->prev;
497 else if ( item->frame > place->data.frame )
500 place->next->prev = gi;
501 gi->next = place->next;
507 memcpy( &place->data, &gi->data, sizeof( struct mlt_geometry_item_s ) );
513 // Set the first item
516 // To ensure correct seeding, ensure all values are fixed
517 g->item->data.f[0] = 1;
518 g->item->data.f[1] = 1;
519 g->item->data.f[2] = 1;
520 g->item->data.f[3] = 1;
521 g->item->data.f[4] = 1;
524 // TODO: Error checking
528 // Remove the key at the specified position
529 int mlt_geometry_remove( mlt_geometry self, int position )
533 // Get the local/private geometry structure
534 geometry g = self->local;
536 // Get the first item
537 geometry_item place = g->item;
539 while( place != NULL && position != place->data.frame )
542 if ( place != NULL && position == place->data.frame )
543 ret = mlt_geometry_drop( self, place );
548 // Get the key at the position or the next following
549 int mlt_geometry_next_key( mlt_geometry self, mlt_geometry_item item, int position )
551 // Get the local/private geometry structure
552 geometry g = self->local;
554 // Get the first item
555 geometry_item place = g->item;
557 while( place != NULL && position > place->data.frame )
561 memcpy( item, &place->data, sizeof( struct mlt_geometry_item_s ) );
563 return place == NULL;
566 // Get the key at the position or the previous key
567 int mlt_geometry_prev_key( mlt_geometry self, mlt_geometry_item item, int position )
569 // Get the local/private geometry structure
570 geometry g = self->local;
572 // Get the first item
573 geometry_item place = g->item;
575 while( place != NULL && place->next != NULL && position >= place->next->data.frame )
579 memcpy( item, &place->data, sizeof( struct mlt_geometry_item_s ) );
581 return place == NULL;
584 char *mlt_geometry_serialise_cut( mlt_geometry self, int in, int out )
586 geometry g = self->local;
587 struct mlt_geometry_item_s item;
588 char *ret = malloc( 1000 );
595 out = mlt_geometry_get_length( self );
609 // If it's the first frame, then it's not necessarily a key
610 if ( item.frame == in )
612 if ( mlt_geometry_fetch( self, &item, item.frame ) )
615 // If the first key is larger than the current position
616 // then do nothing here
617 if ( g->item->data.frame > item.frame )
623 // To ensure correct seeding, ensure all values are fixed
630 // Typically, we move from key to key
631 else if ( item.frame < out )
633 if ( mlt_geometry_next_key( self, &item, item.frame ) )
636 // Special case - crop at the out point
637 if ( item.frame > out )
638 mlt_geometry_fetch( self, &item, out );
640 // We've handled the last key
646 if ( item.frame - in != 0 )
647 sprintf( temp, "%d=", item.frame - in );
650 sprintf( temp + strlen( temp ), "%g", item.x );
653 sprintf( temp + strlen( temp ), "%g", item.y );
657 sprintf( temp + strlen( temp ), "%g", item.w );
661 sprintf( temp + strlen( temp ), "%g", item.h );
665 sprintf( temp + strlen( temp ), "%g", item.mix );
668 if ( used + strlen( temp ) + 2 > size ) // +2 for ';' and NULL
671 ret = realloc( ret, size );
674 if ( ret != NULL && used != 0 )
681 used += strlen( temp );
692 // Serialise the current geometry
693 char *mlt_geometry_serialise( mlt_geometry self )
695 geometry g = self->local;
696 char *ret = mlt_geometry_serialise_cut( self, 0, g->length );
703 return strdup( ret );
706 // Close the geometry
707 void mlt_geometry_close( mlt_geometry self )
711 mlt_geometry_clean( self );