4e894fcccb3246405e7604cb43e15133e80dc82a
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 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
&& item
->prev
!= NULL
)
178 item
->prev
->next
= item
->next
;
179 item
->next
->prev
= item
->prev
;
181 else if ( item
->next
!= NULL
)
183 item
->next
->prev
= item
->prev
;
185 else if ( item
->prev
!= NULL
)
187 item
->prev
->next
= item
->next
;
195 static void mlt_geometry_clean( mlt_geometry
this )
197 geometry self
= this->local
;
201 mlt_geometry_drop( this, self
->item
);
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
)
211 // Create a tokeniser
212 mlt_tokeniser tokens
= mlt_tokeniser_init( );
214 // Get the local/private structure
215 geometry self
= this->local
;
217 // Clean the existing geometry
218 mlt_geometry_clean( this );
220 // Update the info on the data
222 self
->length
= length
;
228 self
->data
= strdup( data
);
232 mlt_tokeniser_parse_new( tokens
, data
, ";" );
234 // Iterate through each token
235 for ( i
= 0; i
< mlt_tokeniser_count( tokens
); i
++ )
237 struct mlt_geometry_item_s item
;
238 char *value
= mlt_tokeniser_get_string( tokens
, i
);
241 memset( &item
, 0, sizeof( struct mlt_geometry_item_s
) );
243 // Now parse the item
244 mlt_geometry_parse_item( this, &item
, value
);
246 // Now insert into place
247 mlt_geometry_insert( this, &item
);
250 // Remove the tokeniser
251 mlt_tokeniser_close( tokens
);
257 // Conditionally refresh in case of a change
258 int mlt_geometry_refresh( mlt_geometry
this, char *data
, int length
, int nw
, int nh
)
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
) ) );
266 return mlt_geometry_parse( this, data
, length
, nw
, nh
);
270 int mlt_geometry_get_length( mlt_geometry
this )
272 // Get the local/private structure
273 geometry self
= this->local
;
279 void mlt_geometry_set_length( mlt_geometry
this, int length
)
281 // Get the local/private structure
282 geometry self
= this->local
;
285 self
->length
= length
;
288 int mlt_geometry_parse_item( mlt_geometry
this, mlt_geometry_item item
, char *value
)
292 // Get the local/private structure
293 geometry self
= this->local
;
295 if ( value
!= NULL
&& strcmp( value
, "" ) )
297 char *p
= strchr( value
, '=' );
301 // Determine if a position has been specified
304 temp
= atof( value
);
305 if ( temp
> -1 && temp
< 1 )
306 item
->frame
= temp
* self
->length
;
312 // Special case - frame < 0
313 if ( item
->frame
< 0 )
314 item
->frame
+= self
->length
;
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
);
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...
331 // Iterate through the remainder of value
335 temp
= strtod( value
, &p
);
337 // Check if a value was specified
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;
350 // Special case - distort token
351 if ( *p
== '!' || *p
== '*' )
357 // Actually, we don't care about the delimiter at all..
360 // Assign to the item
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;
375 // Update the value pointer
388 // Fetch a geometry item for an absolute position
389 int mlt_geometry_fetch( mlt_geometry
this, mlt_geometry_item item
, float position
)
391 // Get the local geometry
392 geometry self
= this->local
;
394 // Need to find the nearest key to the position specifed
395 geometry_item key
= self
->item
;
397 // Iterate through the keys until we reach last or have
398 while( key
!= NULL
&& key
->next
!= NULL
&& position
>= key
->next
->data
.frame
)
403 // Position is situated before the first key - all zeroes
404 if ( position
< key
->data
.frame
)
406 memset( item
, 0, sizeof( struct mlt_geometry_item_s
) );
409 // Position is a key itself - no iterpolation need
410 else if ( position
== key
->data
.frame
)
412 memcpy( item
, &key
->data
, sizeof( struct mlt_geometry_item_s
) );
414 // Position is after the last key - no interpolation, but not a key frame
415 else if ( key
->next
== NULL
)
417 memcpy( item
, &key
->data
, sizeof( struct mlt_geometry_item_s
) );
425 // Interpolation is needed - position > key and there is a following key
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
;
440 item
->frame
= position
;
444 memset( item
, 0, sizeof( struct mlt_geometry_item_s
) );
445 item
->frame
= position
;
452 // Specify a geometry item at an absolute position
453 int mlt_geometry_insert( mlt_geometry
this, mlt_geometry_item item
)
455 // Get the local/private geometry structure
456 geometry self
= this->local
;
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
) );
463 // Determine if we need to insert or append to the list, or if it's a new list
464 if ( self
->item
!= NULL
)
466 // Get the first item
467 geometry_item place
= self
->item
;
469 // Locate an existing nearby item
470 while ( place
->next
!= NULL
&& item
->frame
> place
->data
.frame
)
473 if ( item
->frame
< place
->data
.frame
)
475 if ( place
== self
->item
)
478 place
->prev
->next
= new;
480 new->prev
= place
->prev
;
483 else if ( item
->frame
> place
->data
.frame
)
486 place
->next
->prev
= new;
487 new->next
= place
->next
;
493 memcpy( &place
->data
, &new->data
, sizeof( struct mlt_geometry_item_s
) );
499 // Set the first item
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;
510 // Refresh all geometries
511 mlt_geometry_virtual_refresh( this );
513 // TODO: Error checking
517 // Remove the key at the specified position
518 int mlt_geometry_remove( mlt_geometry
this, int position
)
522 // Get the local/private geometry structure
523 geometry self
= this->local
;
525 // Get the first item
526 geometry_item place
= self
->item
;
528 while( place
!= NULL
&& position
!= place
->data
.frame
)
531 if ( place
!= NULL
&& position
== place
->data
.frame
)
532 ret
= mlt_geometry_drop( this, place
);
534 // Refresh all geometries
535 mlt_geometry_virtual_refresh( this );
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
)
543 // Get the local/private geometry structure
544 geometry self
= this->local
;
546 // Get the first item
547 geometry_item place
= self
->item
;
549 while( place
!= NULL
&& position
> place
->data
.frame
)
553 memcpy( item
, &place
->data
, sizeof( struct mlt_geometry_item_s
) );
555 return place
== NULL
;
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
)
561 // Get the local/private geometry structure
562 geometry self
= this->local
;
564 // Get the first item
565 geometry_item place
= self
->item
;
567 while( place
!= NULL
&& place
->next
!= NULL
&& position
>= place
->next
->data
.frame
)
571 memcpy( item
, &place
->data
, sizeof( struct mlt_geometry_item_s
) );
573 return place
== NULL
;
576 char *mlt_geometry_serialise_cut( mlt_geometry
this, int in
, int out
)
578 geometry self
= this->local
;
579 struct mlt_geometry_item_s item
;
580 char *ret
= malloc( 1000 );
587 out
= mlt_geometry_get_length( this );
601 // If it's the first frame, then it's not necessarily a key
602 if ( item
.frame
== in
)
604 if ( mlt_geometry_fetch( this, &item
, item
.frame
) )
607 // If the first key is larger than the current position
608 // then do nothing here
609 if ( self
->item
->data
.frame
> item
.frame
)
615 // To ensure correct seeding, ensure all values are fixed
622 // Typically, we move from key to key
623 else if ( item
.frame
< out
)
625 if ( mlt_geometry_next_key( this, &item
, item
.frame
) )
628 // Special case - crop at the out point
629 if ( item
.frame
> out
)
630 mlt_geometry_fetch( this, &item
, out
);
632 // We've handled the last key
638 if ( item
.frame
- in
!= 0 )
639 sprintf( temp
, "%d=", item
.frame
- in
);
642 sprintf( temp
+ strlen( temp
), "%.0f", item
.x
);
645 sprintf( temp
+ strlen( temp
), "%.0f", item
.y
);
648 sprintf( temp
+ strlen( temp
), "%.0f", item
.w
);
651 sprintf( temp
+ strlen( temp
), "%.0f", item
.h
);
653 sprintf( temp
+ strlen( temp
), ":%.0f", item
.mix
);
655 if ( used
+ strlen( temp
) > size
)
658 ret
= realloc( ret
, size
);
661 if ( ret
!= NULL
&& used
!= 0 )
668 used
+= strlen( temp
);
679 // Serialise the current geometry
680 char *mlt_geometry_serialise( mlt_geometry
this )
682 geometry self
= this->local
;
683 char *ret
= mlt_geometry_serialise_cut( this, 0, self
->length
);
692 // Close the geometry
693 void mlt_geometry_close( mlt_geometry
this )
697 mlt_geometry_clean( this );