2 * mlt_geometry.h -- provides the geometry API
3 * Copyright (C) 2004-2005 Ushodaya Enterprises Limited
4 * Author: Charles Yates <charles.yates@pandora.be>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program 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
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * 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"
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
this = calloc( 1, sizeof( struct mlt_geometry_s
) );
53 this->local
= calloc( 1, sizeof( geometry_s
) );
54 if ( this->local
!= NULL
)
56 geometry self
= this->local
;
57 char *normalisation
= mlt_environment( "MLT_NORMALISATION" );
59 if ( normalisation
== NULL
|| strcmp( normalisation
, "NTSC" ) )
76 static inline double linearstep( double start
, double end
, double position
, int length
)
78 double o
= ( end
- start
) / length
;
79 return start
+ position
* o
;
82 static void mlt_geometry_virtual_refresh( mlt_geometry
this )
84 geometry self
= this->local
;
86 // Parse of all items to ensure unspecified keys are calculated correctly
87 if ( self
->item
!= NULL
)
90 for ( i
= 0; i
< 5; i
++ )
92 geometry_item current
= self
->item
;
93 while( current
!= NULL
)
95 int fixed
= current
->data
.f
[ i
];
98 geometry_item prev
= current
->prev
;
99 geometry_item next
= current
->next
;
101 float prev_value
= 0;
102 float next_value
= 0;
105 while( prev
!= NULL
&& !prev
->data
.f
[ i
] ) prev
= prev
->prev
;
106 while( next
!= NULL
&& !next
->data
.f
[ i
] ) next
= next
->next
;
111 if ( prev
) prev_value
= prev
->data
.x
;
112 if ( next
) next_value
= next
->data
.x
;
115 if ( prev
) prev_value
= prev
->data
.y
;
116 if ( next
) next_value
= next
->data
.y
;
119 if ( prev
) prev_value
= prev
->data
.w
;
120 if ( next
) next_value
= next
->data
.w
;
123 if ( prev
) prev_value
= prev
->data
.h
;
124 if ( next
) next_value
= next
->data
.h
;
127 if ( prev
) prev_value
= prev
->data
.mix
;
128 if ( next
) next_value
= next
->data
.mix
;
132 // This should never happen
134 current
->data
.f
[ i
] = 1;
135 else if ( next
== NULL
)
138 value
= linearstep( prev_value
, next_value
, current
->data
.frame
- prev
->data
.frame
, next
->data
.frame
- prev
->data
.frame
);
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;
150 // Move to the next item
151 current
= current
->next
;
157 static int mlt_geometry_drop( mlt_geometry
this, geometry_item item
)
159 geometry self
= this->local
;
161 if ( item
== self
->item
)
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
)
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;
176 else if ( item
->next
!= NULL
)
178 item
->next
->prev
= item
->prev
;
180 else if ( item
->prev
!= NULL
)
182 item
->prev
->next
= item
->next
;
183 item
->next
->prev
= item
->prev
;
191 static void mlt_geometry_clean( mlt_geometry
this )
193 geometry self
= this->local
;
197 mlt_geometry_drop( this, self
->item
);
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
)
207 // Create a tokeniser
208 mlt_tokeniser tokens
= mlt_tokeniser_init( );
210 // Get the local/private structure
211 geometry self
= this->local
;
213 // Clean the existing geometry
214 mlt_geometry_clean( this );
216 // Update the info on the data
218 self
->length
= length
;
224 self
->data
= strdup( data
);
228 mlt_tokeniser_parse_new( tokens
, data
, ";" );
230 // Iterate through each token
231 for ( i
= 0; i
< mlt_tokeniser_count( tokens
); i
++ )
233 struct mlt_geometry_item_s item
;
234 char *value
= mlt_tokeniser_get_string( tokens
, i
);
237 memset( &item
, 0, sizeof( struct mlt_geometry_item_s
) );
239 // Now parse the item
240 mlt_geometry_parse_item( this, &item
, value
);
242 // Now insert into place
243 mlt_geometry_insert( this, &item
);
246 // Remove the tokeniser
247 mlt_tokeniser_close( tokens
);
253 // Conditionally refresh in case of a change
254 int mlt_geometry_refresh( mlt_geometry
this, char *data
, int length
, int nw
, int nh
)
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
) ) );
262 return mlt_geometry_parse( this, data
, length
, nw
, nh
);
266 int mlt_geometry_get_length( mlt_geometry
this )
268 // Get the local/private structure
269 geometry self
= this->local
;
275 void mlt_geometry_set_length( mlt_geometry
this, int length
)
277 // Get the local/private structure
278 geometry self
= this->local
;
281 self
->length
= length
;
284 int mlt_geometry_parse_item( mlt_geometry
this, mlt_geometry_item item
, char *value
)
288 // Get the local/private structure
289 geometry self
= this->local
;
291 if ( value
!= NULL
&& strcmp( value
, "" ) )
293 char *p
= strchr( value
, '=' );
297 // Determine if a position has been specified
300 temp
= atof( value
);
301 if ( temp
> -1 && temp
< 1 )
302 item
->frame
= temp
* self
->length
;
308 // Special case - frame < 0
309 if ( item
->frame
< 0 )
310 item
->frame
+= self
->length
;
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
);
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...
327 // Iterate through the remainder of value
331 temp
= strtod( value
, &p
);
333 // Check if a value was specified
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;
346 // Special case - distort token
347 if ( *p
== '!' || *p
== '*' )
353 // Actually, we don't care about the delimiter at all..
356 // Assign to the item
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;
371 // Update the value pointer
384 // Fetch a geometry item for an absolute position
385 int mlt_geometry_fetch( mlt_geometry
this, mlt_geometry_item item
, float position
)
387 // Get the local geometry
388 geometry self
= this->local
;
390 // Need to find the nearest key to the position specifed
391 geometry_item key
= self
->item
;
393 // Iterate through the keys until we reach last or have
394 while( key
!= NULL
&& key
->next
!= NULL
&& position
>= key
->next
->data
.frame
)
399 // Position is situated before the first key - all zeroes
400 if ( position
< key
->data
.frame
)
402 memset( item
, 0, sizeof( struct mlt_geometry_item_s
) );
405 // Position is a key itself - no iterpolation need
406 else if ( position
== key
->data
.frame
)
408 memcpy( item
, &key
->data
, sizeof( struct mlt_geometry_item_s
) );
410 // Position is after the last key - no interpolation, but not a key frame
411 else if ( key
->next
== NULL
)
413 memcpy( item
, &key
->data
, sizeof( struct mlt_geometry_item_s
) );
421 // Interpolation is needed - position > key and there is a following key
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
;
436 item
->frame
= position
;
440 memset( item
, 0, sizeof( struct mlt_geometry_item_s
) );
447 // Specify a geometry item at an absolute position
448 int mlt_geometry_insert( mlt_geometry
this, mlt_geometry_item item
)
450 // Get the local/private geometry structure
451 geometry self
= this->local
;
453 // Create a new local item (this may be removed if a key already exists at this position)
454 geometry_item
new = calloc( 1, sizeof( struct geometry_item_s
) );
455 memcpy( &new->data
, item
, sizeof( struct mlt_geometry_item_s
) );
458 // Determine if we need to insert or append to the list, or if it's a new list
459 if ( self
->item
!= NULL
)
461 // Get the first item
462 geometry_item place
= self
->item
;
464 // Locate an existing nearby item
465 while ( place
->next
!= NULL
&& item
->frame
> place
->data
.frame
)
468 if ( item
->frame
< place
->data
.frame
)
470 if ( place
== self
->item
)
473 place
->prev
->next
= new;
475 new->prev
= place
->prev
;
478 else if ( item
->frame
> place
->data
.frame
)
481 place
->next
->prev
= new;
482 new->next
= place
->next
;
488 memcpy( &place
->data
, &new->data
, sizeof( struct mlt_geometry_item_s
) );
494 // Set the first item
497 // To ensure correct seeding, ensure all values are fixed
498 self
->item
->data
.f
[0] = 1;
499 self
->item
->data
.f
[1] = 1;
500 self
->item
->data
.f
[2] = 1;
501 self
->item
->data
.f
[3] = 1;
502 self
->item
->data
.f
[4] = 1;
505 // Refresh all geometries
506 mlt_geometry_virtual_refresh( this );
508 // TODO: Error checking
512 // Remove the key at the specified position
513 int mlt_geometry_remove( mlt_geometry
this, int position
)
517 // Get the local/private geometry structure
518 geometry self
= this->local
;
520 // Get the first item
521 geometry_item place
= self
->item
;
523 while( place
!= NULL
&& position
< place
->data
.frame
)
526 if ( place
!= NULL
&& position
== place
->data
.frame
)
527 ret
= mlt_geometry_drop( this, place
);
529 // Refresh all geometries
530 mlt_geometry_virtual_refresh( this );
535 // Get the key at the position or the next following
536 int mlt_geometry_key( mlt_geometry
this, mlt_geometry_item item
, int position
)
538 // Get the local/private geometry structure
539 geometry self
= this->local
;
541 // Get the first item
542 geometry_item place
= self
->item
;
544 while( place
!= NULL
&& position
> place
->data
.frame
)
548 memcpy( item
, &place
->data
, sizeof( struct mlt_geometry_item_s
) );
550 return place
== NULL
;
553 char *mlt_geometry_serialise_cut( mlt_geometry
this, int in
, int out
)
555 struct mlt_geometry_item_s item
;
556 char *ret
= malloc( 1000 );
563 out
= mlt_geometry_get_length( this );
577 // If it's the first frame, then it's not necessarily a key
578 if ( item
.frame
== in
)
580 if ( mlt_geometry_fetch( this, &item
, item
.frame
) )
583 // To ensure correct seeding, ensure all values are fixed
590 // Typically, we move from key to key
591 else if ( item
.frame
< out
)
593 if ( mlt_geometry_key( this, &item
, item
.frame
) )
596 // Special case - crop at the out point
597 if ( item
.frame
> out
)
598 mlt_geometry_fetch( this, &item
, out
);
600 // We've handled the last key
606 if ( item
.frame
- in
!= 0 )
607 sprintf( temp
, "%d=", item
.frame
- in
);
610 sprintf( temp
+ strlen( temp
), "%.0f", item
.x
);
613 sprintf( temp
+ strlen( temp
), "%.0f", item
.y
);
616 sprintf( temp
+ strlen( temp
), "%.0f", item
.w
);
619 sprintf( temp
+ strlen( temp
), "%.0f", item
.h
);
621 sprintf( temp
+ strlen( temp
), ":%.0f", item
.mix
);
623 if ( used
+ strlen( temp
) > size
)
626 ret
= realloc( ret
, size
);
629 if ( ret
!= NULL
&& used
!= 0 )
636 used
+= strlen( temp
);
647 // Serialise the current geometry
648 char *mlt_geometry_serialise( mlt_geometry
this )
650 geometry self
= this->local
;
651 char *ret
= mlt_geometry_serialise_cut( this, 0, self
->length
);
660 // Close the geometry
661 void mlt_geometry_close( mlt_geometry
this )
665 mlt_geometry_clean( this );