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"
31 typedef struct geometry_item_s
33 struct mlt_geometry_item_s data
;
34 struct geometry_item_s
*next
, *prev
;
46 geometry_s
, *geometry
;
48 // Create a new geometry structure
49 mlt_geometry
mlt_geometry_init( )
51 mlt_geometry
this = calloc( 1, sizeof( struct mlt_geometry_s
) );
54 this->local
= calloc( 1, sizeof( geometry_s
) );
55 if ( this->local
!= NULL
)
57 geometry self
= this->local
;
73 static inline double linearstep( double start
, double end
, double position
, int length
)
75 double o
= ( end
- start
) / length
;
76 return start
+ position
* o
;
79 static void mlt_geometry_virtual_refresh( mlt_geometry
this )
81 geometry self
= this->local
;
83 // Parse of all items to ensure unspecified keys are calculated correctly
84 if ( self
->item
!= NULL
)
87 for ( i
= 0; i
< 5; i
++ )
89 geometry_item current
= self
->item
;
90 while( current
!= NULL
)
92 int fixed
= current
->data
.f
[ i
];
95 geometry_item prev
= current
->prev
;
96 geometry_item next
= current
->next
;
98 double prev_value
= 0;
99 double next_value
= 0;
102 while( prev
!= NULL
&& !prev
->data
.f
[ i
] ) prev
= prev
->prev
;
103 while( next
!= NULL
&& !next
->data
.f
[ i
] ) next
= next
->next
;
108 if ( prev
) prev_value
= prev
->data
.x
;
109 if ( next
) next_value
= next
->data
.x
;
112 if ( prev
) prev_value
= prev
->data
.y
;
113 if ( next
) next_value
= next
->data
.y
;
116 if ( prev
) prev_value
= prev
->data
.w
;
117 if ( next
) next_value
= next
->data
.w
;
120 if ( prev
) prev_value
= prev
->data
.h
;
121 if ( next
) next_value
= next
->data
.h
;
124 if ( prev
) prev_value
= prev
->data
.mix
;
125 if ( next
) next_value
= next
->data
.mix
;
129 // This should never happen
131 current
->data
.f
[ i
] = 1;
132 else if ( next
== NULL
)
135 value
= linearstep( prev_value
, next_value
, current
->data
.frame
- prev
->data
.frame
, next
->data
.frame
- prev
->data
.frame
);
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;
147 // Move to the next item
148 current
= current
->next
;
154 static int mlt_geometry_drop( mlt_geometry
this, geometry_item item
)
156 geometry self
= this->local
;
158 if ( item
== self
->item
)
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
)
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;
173 else if ( item
->next
!= NULL
&& item
->prev
!= NULL
)
175 item
->prev
->next
= item
->next
;
176 item
->next
->prev
= item
->prev
;
178 else if ( item
->next
!= NULL
)
180 item
->next
->prev
= item
->prev
;
182 else if ( item
->prev
!= NULL
)
184 item
->prev
->next
= item
->next
;
192 static void mlt_geometry_clean( mlt_geometry
this )
194 geometry self
= this->local
;
198 mlt_geometry_drop( this, self
->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 int mlt_geometry_parse( mlt_geometry
this, char *data
, int length
, int nw
, int nh
)
208 // Create a tokeniser
209 mlt_tokeniser tokens
= mlt_tokeniser_init( );
211 // Get the local/private structure
212 geometry self
= this->local
;
214 // Clean the existing geometry
215 mlt_geometry_clean( this );
217 // Update the info on the data
219 self
->length
= length
;
225 self
->data
= strdup( data
);
229 mlt_tokeniser_parse_new( tokens
, data
, ";" );
231 // Iterate through each token
232 for ( i
= 0; i
< mlt_tokeniser_count( tokens
); i
++ )
234 struct mlt_geometry_item_s item
;
235 char *value
= mlt_tokeniser_get_string( tokens
, i
);
238 memset( &item
, 0, sizeof( struct mlt_geometry_item_s
) );
240 // Now parse the item
241 mlt_geometry_parse_item( this, &item
, value
);
243 // Now insert into place
244 mlt_geometry_insert( this, &item
);
247 // Remove the tokeniser
248 mlt_tokeniser_close( tokens
);
254 // Conditionally refresh in case of a change
255 int mlt_geometry_refresh( mlt_geometry
this, char *data
, int length
, int nw
, int nh
)
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
) ) );
263 return mlt_geometry_parse( this, data
, length
, nw
, nh
);
267 int mlt_geometry_get_length( mlt_geometry
this )
269 // Get the local/private structure
270 geometry self
= this->local
;
276 void mlt_geometry_set_length( mlt_geometry
this, int length
)
278 // Get the local/private structure
279 geometry self
= this->local
;
282 self
->length
= length
;
285 int mlt_geometry_parse_item( mlt_geometry
this, mlt_geometry_item item
, char *value
)
289 // Get the local/private structure
290 geometry self
= this->local
;
292 if ( value
!= NULL
&& strcmp( value
, "" ) )
294 char *p
= strchr( value
, '=' );
298 // Determine if a position has been specified
301 temp
= atof( value
);
302 if ( temp
> -1 && temp
< 1 )
303 item
->frame
= temp
* self
->length
;
309 // Special case - frame < 0
310 if ( item
->frame
< 0 )
311 item
->frame
+= self
->length
;
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
);
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...
328 // Iterate through the remainder of value
332 temp
= strtod( value
, &p
);
334 // Check if a value was specified
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;
347 // Special case - distort token
348 if ( *p
== '!' || *p
== '*' )
354 // Actually, we don't care about the delimiter at all..
357 // Assign to the item
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;
372 // Update the value pointer
385 // Fetch a geometry item for an absolute position
386 int mlt_geometry_fetch( mlt_geometry
this, mlt_geometry_item item
, float position
)
388 // Get the local geometry
389 geometry self
= this->local
;
391 // Need to find the nearest key to the position specifed
392 geometry_item key
= self
->item
;
394 // Iterate through the keys until we reach last or have
395 while( key
!= NULL
&& key
->next
!= NULL
&& position
>= key
->next
->data
.frame
)
400 // Position is situated before the first key - all zeroes
401 if ( position
< key
->data
.frame
)
403 memset( item
, 0, sizeof( struct mlt_geometry_item_s
) );
406 // Position is a key itself - no iterpolation need
407 else if ( position
== key
->data
.frame
)
409 memcpy( item
, &key
->data
, sizeof( struct mlt_geometry_item_s
) );
411 // Position is after the last key - no interpolation, but not a key frame
412 else if ( key
->next
== NULL
)
414 memcpy( item
, &key
->data
, sizeof( struct mlt_geometry_item_s
) );
422 // Interpolation is needed - position > key and there is a following key
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
;
437 item
->frame
= position
;
441 memset( item
, 0, sizeof( struct mlt_geometry_item_s
) );
442 item
->frame
= position
;
449 // Specify a geometry item at an absolute position
450 int mlt_geometry_insert( mlt_geometry
this, mlt_geometry_item item
)
452 // Get the local/private geometry structure
453 geometry self
= this->local
;
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
) );
460 // Determine if we need to insert or append to the list, or if it's a new list
461 if ( self
->item
!= NULL
)
463 // Get the first item
464 geometry_item place
= self
->item
;
466 // Locate an existing nearby item
467 while ( place
->next
!= NULL
&& item
->frame
> place
->data
.frame
)
470 if ( item
->frame
< place
->data
.frame
)
472 if ( place
== self
->item
)
475 place
->prev
->next
= new;
477 new->prev
= place
->prev
;
480 else if ( item
->frame
> place
->data
.frame
)
483 place
->next
->prev
= new;
484 new->next
= place
->next
;
490 memcpy( &place
->data
, &new->data
, sizeof( struct mlt_geometry_item_s
) );
496 // Set the first item
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;
507 // Refresh all geometries
508 mlt_geometry_virtual_refresh( this );
510 // TODO: Error checking
514 // Remove the key at the specified position
515 int mlt_geometry_remove( mlt_geometry
this, int position
)
519 // Get the local/private geometry structure
520 geometry self
= this->local
;
522 // Get the first item
523 geometry_item place
= self
->item
;
525 while( place
!= NULL
&& position
!= place
->data
.frame
)
528 if ( place
!= NULL
&& position
== place
->data
.frame
)
529 ret
= mlt_geometry_drop( this, place
);
531 // Refresh all geometries
532 mlt_geometry_virtual_refresh( this );
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
)
540 // Get the local/private geometry structure
541 geometry self
= this->local
;
543 // Get the first item
544 geometry_item place
= self
->item
;
546 while( place
!= NULL
&& position
> place
->data
.frame
)
550 memcpy( item
, &place
->data
, sizeof( struct mlt_geometry_item_s
) );
552 return place
== NULL
;
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
)
558 // Get the local/private geometry structure
559 geometry self
= this->local
;
561 // Get the first item
562 geometry_item place
= self
->item
;
564 while( place
!= NULL
&& place
->next
!= NULL
&& position
>= place
->next
->data
.frame
)
568 memcpy( item
, &place
->data
, sizeof( struct mlt_geometry_item_s
) );
570 return place
== NULL
;
573 char *mlt_geometry_serialise_cut( mlt_geometry
this, int in
, int out
)
575 geometry self
= this->local
;
576 struct mlt_geometry_item_s item
;
577 char *ret
= malloc( 1000 );
584 out
= mlt_geometry_get_length( this );
598 // If it's the first frame, then it's not necessarily a key
599 if ( item
.frame
== in
)
601 if ( mlt_geometry_fetch( this, &item
, item
.frame
) )
604 // If the first key is larger than the current position
605 // then do nothing here
606 if ( self
->item
->data
.frame
> item
.frame
)
612 // To ensure correct seeding, ensure all values are fixed
619 // Typically, we move from key to key
620 else if ( item
.frame
< out
)
622 if ( mlt_geometry_next_key( this, &item
, item
.frame
) )
625 // Special case - crop at the out point
626 if ( item
.frame
> out
)
627 mlt_geometry_fetch( this, &item
, out
);
629 // We've handled the last key
635 if ( item
.frame
- in
!= 0 )
636 sprintf( temp
, "%d=", item
.frame
- in
);
639 sprintf( temp
+ strlen( temp
), "%.0f", item
.x
);
642 sprintf( temp
+ strlen( temp
), "%.0f", item
.y
);
645 sprintf( temp
+ strlen( temp
), "%.0f", item
.w
);
648 sprintf( temp
+ strlen( temp
), "%.0f", item
.h
);
650 sprintf( temp
+ strlen( temp
), ":%.0f", item
.mix
);
652 if ( used
+ strlen( temp
) > size
)
655 ret
= realloc( ret
, size
);
658 if ( ret
!= NULL
&& used
!= 0 )
665 used
+= strlen( temp
);
676 // Serialise the current geometry
677 char *mlt_geometry_serialise( mlt_geometry
this )
679 geometry self
= this->local
;
680 char *ret
= mlt_geometry_serialise_cut( this, 0, self
->length
);
689 // Close the geometry
690 void mlt_geometry_close( mlt_geometry
this )
694 mlt_geometry_clean( this );