50a0d6227fac32d3961f537d8106b6ab433b6bec
[melted] / src / framework / mlt_geometry.c
1 /*
2 * mlt_geometry.c -- provides the geometry API
3 * Copyright (C) 2004-2005 Ushodaya Enterprises Limited
4 * Author: Charles Yates <charles.yates@pandora.be>
5 *
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.
10 *
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.
15 *
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
19 */
20
21 #include "mlt_geometry.h"
22 #include "mlt_tokeniser.h"
23 #include "mlt_factory.h"
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <math.h>
29
30 typedef struct geometry_item_s
31 {
32 struct mlt_geometry_item_s data;
33 struct geometry_item_s *next, *prev;
34 }
35 *geometry_item;
36
37 typedef struct
38 {
39 char *data;
40 int length;
41 int nw;
42 int nh;
43 geometry_item item;
44 }
45 geometry_s, *geometry;
46
47 // Create a new geometry structure
48 mlt_geometry mlt_geometry_init( )
49 {
50 mlt_geometry this = calloc( 1, sizeof( struct mlt_geometry_s ) );
51 if ( this != NULL )
52 {
53 this->local = calloc( 1, sizeof( geometry_s ) );
54 if ( this->local != NULL )
55 {
56 geometry self = this->local;
57 char *normalisation = mlt_environment( "MLT_NORMALISATION" );
58 self->nw = 720;
59 if ( normalisation == NULL || strcmp( normalisation, "NTSC" ) )
60 self->nh = 576;
61 else
62 self->nh = 480;
63 }
64 else
65 {
66 free( this );
67 this = NULL;
68 }
69 }
70 return this;
71 }
72
73 /** A linear step
74 */
75
76 static inline double linearstep( double start, double end, double position, int length )
77 {
78 double o = ( end - start ) / length;
79 return start + position * o;
80 }
81
82 static void mlt_geometry_virtual_refresh( mlt_geometry this )
83 {
84 geometry self = this->local;
85
86 // Parse of all items to ensure unspecified keys are calculated correctly
87 if ( self->item != NULL )
88 {
89 int i = 0;
90 for ( i = 0; i < 5; i ++ )
91 {
92 geometry_item current = self->item;
93 while( current != NULL )
94 {
95 int fixed = current->data.f[ i ];
96 if ( !fixed )
97 {
98 geometry_item prev = current->prev;
99 geometry_item next = current->next;
100
101 double prev_value = 0;
102 double next_value = 0;
103 double value = 0;
104
105 while( prev != NULL && !prev->data.f[ i ] ) prev = prev->prev;
106 while( next != NULL && !next->data.f[ i ] ) next = next->next;
107
108 switch( i )
109 {
110 case 0:
111 if ( prev ) prev_value = prev->data.x;
112 if ( next ) next_value = next->data.x;
113 break;
114 case 1:
115 if ( prev ) prev_value = prev->data.y;
116 if ( next ) next_value = next->data.y;
117 break;
118 case 2:
119 if ( prev ) prev_value = prev->data.w;
120 if ( next ) next_value = next->data.w;
121 break;
122 case 3:
123 if ( prev ) prev_value = prev->data.h;
124 if ( next ) next_value = next->data.h;
125 break;
126 case 4:
127 if ( prev ) prev_value = prev->data.mix;
128 if ( next ) next_value = next->data.mix;
129 break;
130 }
131
132 // This should never happen
133 if ( prev == NULL )
134 current->data.f[ i ] = 1;
135 else if ( next == NULL )
136 value = prev_value;
137 else
138 value = linearstep( prev_value, next_value, current->data.frame - prev->data.frame, next->data.frame - prev->data.frame );
139
140 switch( i )
141 {
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;
147 }
148 }
149
150 // Move to the next item
151 current = current->next;
152 }
153 }
154 }
155 }
156
157 static int mlt_geometry_drop( mlt_geometry this, geometry_item item )
158 {
159 geometry self = this->local;
160
161 if ( item == self->item )
162 {
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 )
168 {
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;
174 }
175 }
176 else if ( item->next != NULL && item->prev != NULL )
177 {
178 item->prev->next = item->next;
179 item->next->prev = item->prev;
180 }
181 else if ( item->next != NULL )
182 {
183 item->next->prev = item->prev;
184 }
185 else if ( item->prev != NULL )
186 {
187 item->prev->next = item->next;
188 }
189
190 free( item );
191
192 return 0;
193 }
194
195 static void mlt_geometry_clean( mlt_geometry this )
196 {
197 geometry self = this->local;
198 free( self->data );
199 self->data = NULL;
200 while( self->item )
201 mlt_geometry_drop( this, self->item );
202 }
203
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 )
208 {
209 int i = 0;
210
211 // Create a tokeniser
212 mlt_tokeniser tokens = mlt_tokeniser_init( );
213
214 // Get the local/private structure
215 geometry self = this->local;
216
217 // Clean the existing geometry
218 mlt_geometry_clean( this );
219
220 // Update the info on the data
221 if ( length != -1 )
222 self->length = length;
223 if ( nw != -1 )
224 self->nw = nw;
225 if ( nh != -1 )
226 self->nh = nh;
227 if ( data != NULL )
228 self->data = strdup( data );
229
230 // Tokenise
231 if ( data != NULL )
232 mlt_tokeniser_parse_new( tokens, data, ";" );
233
234 // Iterate through each token
235 for ( i = 0; i < mlt_tokeniser_count( tokens ); i ++ )
236 {
237 struct mlt_geometry_item_s item;
238 char *value = mlt_tokeniser_get_string( tokens, i );
239
240 // Set item to 0
241 memset( &item, 0, sizeof( struct mlt_geometry_item_s ) );
242
243 // Now parse the item
244 mlt_geometry_parse_item( this, &item, value );
245
246 // Now insert into place
247 mlt_geometry_insert( this, &item );
248 }
249
250 // Remove the tokeniser
251 mlt_tokeniser_close( tokens );
252
253 // ???
254 return 0;
255 }
256
257 // Conditionally refresh in case of a change
258 int mlt_geometry_refresh( mlt_geometry this, char *data, int length, int nw, int nh )
259 {
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 ) ) );
265 if ( changed )
266 return mlt_geometry_parse( this, data, length, nw, nh );
267 return -1;
268 }
269
270 int mlt_geometry_get_length( mlt_geometry this )
271 {
272 // Get the local/private structure
273 geometry self = this->local;
274
275 // return the length
276 return self->length;
277 }
278
279 void mlt_geometry_set_length( mlt_geometry this, int length )
280 {
281 // Get the local/private structure
282 geometry self = this->local;
283
284 // set the length
285 self->length = length;
286 }
287
288 int mlt_geometry_parse_item( mlt_geometry this, mlt_geometry_item item, char *value )
289 {
290 int ret = 0;
291
292 // Get the local/private structure
293 geometry self = this->local;
294
295 if ( value != NULL && strcmp( value, "" ) )
296 {
297 char *p = strchr( value, '=' );
298 int count = 0;
299 double temp;
300
301 // Determine if a position has been specified
302 if ( p != NULL )
303 {
304 temp = atof( value );
305 if ( temp > -1 && temp < 1 )
306 item->frame = temp * self->length;
307 else
308 item->frame = temp;
309 value = p + 1;
310 }
311
312 // Special case - frame < 0
313 if ( item->frame < 0 )
314 item->frame += self->length;
315
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 );
319
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...
322 if ( !*value )
323 {
324 item->f[0] = 1;
325 item->f[1] = 1;
326 item->f[2] = 1;
327 item->f[3] = 1;
328 item->f[4] = 1;
329 }
330
331 // Iterate through the remainder of value
332 while( *value )
333 {
334 // Get the value
335 temp = strtod( value, &p );
336
337 // Check if a value was specified
338 if ( p != value )
339 {
340 // Handle the % case
341 if ( *p == '%' )
342 {
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;
347 p ++;
348 }
349
350 // Special case - distort token
351 if ( *p == '!' || *p == '*' )
352 {
353 p ++;
354 item->distort = 1;
355 }
356
357 // Actually, we don't care about the delimiter at all..
358 if ( *p ) p ++;
359
360 // Assign to the item
361 switch( count )
362 {
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;
368 }
369 }
370 else
371 {
372 p ++;
373 }
374
375 // Update the value pointer
376 value = p;
377 count ++;
378 }
379 }
380 else
381 {
382 ret = 1;
383 }
384
385 return ret;
386 }
387
388 // Fetch a geometry item for an absolute position
389 int mlt_geometry_fetch( mlt_geometry this, mlt_geometry_item item, float position )
390 {
391 // Get the local geometry
392 geometry self = this->local;
393
394 // Need to find the nearest key to the position specifed
395 geometry_item key = self->item;
396
397 // Iterate through the keys until we reach last or have
398 while( key != NULL && key->next != NULL && position >= key->next->data.frame )
399 key = key->next;
400
401 if ( key != NULL )
402 {
403 // Position is situated before the first key - all zeroes
404 if ( position < key->data.frame )
405 {
406 memset( item, 0, sizeof( struct mlt_geometry_item_s ) );
407 item->mix = 100;
408 }
409 // Position is a key itself - no iterpolation need
410 else if ( position == key->data.frame )
411 {
412 memcpy( item, &key->data, sizeof( struct mlt_geometry_item_s ) );
413 }
414 // Position is after the last key - no interpolation, but not a key frame
415 else if ( key->next == NULL )
416 {
417 memcpy( item, &key->data, sizeof( struct mlt_geometry_item_s ) );
418 item->key = 0;
419 item->f[ 0 ] = 0;
420 item->f[ 1 ] = 0;
421 item->f[ 2 ] = 0;
422 item->f[ 3 ] = 0;
423 item->f[ 4 ] = 0;
424 }
425 // Interpolation is needed - position > key and there is a following key
426 else
427 {
428 item->key = 0;
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;
438 }
439
440 item->frame = position;
441 }
442 else
443 {
444 memset( item, 0, sizeof( struct mlt_geometry_item_s ) );
445 item->frame = position;
446 item->mix = 100;
447 }
448
449 return key == NULL;
450 }
451
452 // Specify a geometry item at an absolute position
453 int mlt_geometry_insert( mlt_geometry this, mlt_geometry_item item )
454 {
455 // Get the local/private geometry structure
456 geometry self = this->local;
457
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 ) );
461 new->data.key = 1;
462
463 // Determine if we need to insert or append to the list, or if it's a new list
464 if ( self->item != NULL )
465 {
466 // Get the first item
467 geometry_item place = self->item;
468
469 // Locate an existing nearby item
470 while ( place->next != NULL && item->frame > place->data.frame )
471 place = place->next;
472
473 if ( item->frame < place->data.frame )
474 {
475 if ( place == self->item )
476 self->item = new;
477 if ( place->prev )
478 place->prev->next = new;
479 new->next = place;
480 new->prev = place->prev;
481 place->prev = new;
482 }
483 else if ( item->frame > place->data.frame )
484 {
485 if ( place->next )
486 place->next->prev = new;
487 new->next = place->next;
488 new->prev = place;
489 place->next = new;
490 }
491 else
492 {
493 memcpy( &place->data, &new->data, sizeof( struct mlt_geometry_item_s ) );
494 free( new );
495 }
496 }
497 else
498 {
499 // Set the first item
500 self->item = new;
501
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;
508 }
509
510 // Refresh all geometries
511 mlt_geometry_virtual_refresh( this );
512
513 // TODO: Error checking
514 return 0;
515 }
516
517 // Remove the key at the specified position
518 int mlt_geometry_remove( mlt_geometry this, int position )
519 {
520 int ret = 1;
521
522 // Get the local/private geometry structure
523 geometry self = this->local;
524
525 // Get the first item
526 geometry_item place = self->item;
527
528 while( place != NULL && position != place->data.frame )
529 place = place->next;
530
531 if ( place != NULL && position == place->data.frame )
532 ret = mlt_geometry_drop( this, place );
533
534 // Refresh all geometries
535 mlt_geometry_virtual_refresh( this );
536
537 return ret;
538 }
539
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 )
542 {
543 // Get the local/private geometry structure
544 geometry self = this->local;
545
546 // Get the first item
547 geometry_item place = self->item;
548
549 while( place != NULL && position > place->data.frame )
550 place = place->next;
551
552 if ( place != NULL )
553 memcpy( item, &place->data, sizeof( struct mlt_geometry_item_s ) );
554
555 return place == NULL;
556 }
557
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 )
560 {
561 // Get the local/private geometry structure
562 geometry self = this->local;
563
564 // Get the first item
565 geometry_item place = self->item;
566
567 while( place != NULL && place->next != NULL && position >= place->next->data.frame )
568 place = place->next;
569
570 if ( place != NULL )
571 memcpy( item, &place->data, sizeof( struct mlt_geometry_item_s ) );
572
573 return place == NULL;
574 }
575
576 char *mlt_geometry_serialise_cut( mlt_geometry this, int in, int out )
577 {
578 geometry self = this->local;
579 struct mlt_geometry_item_s item;
580 char *ret = malloc( 1000 );
581 int used = 0;
582 int size = 1000;
583
584 if ( in == -1 )
585 in = 0;
586 if ( out == -1 )
587 out = mlt_geometry_get_length( this );
588
589 if ( ret != NULL )
590 {
591 char temp[ 100 ];
592
593 strcpy( ret, "" );
594
595 item.frame = in;
596
597 while( 1 )
598 {
599 strcpy( temp, "" );
600
601 // If it's the first frame, then it's not necessarily a key
602 if ( item.frame == in )
603 {
604 if ( mlt_geometry_fetch( this, &item, item.frame ) )
605 break;
606
607 // If the first key is larger than the current position
608 // then do nothing here
609 if ( self->item->data.frame > item.frame )
610 {
611 item.frame ++;
612 continue;
613 }
614
615 // To ensure correct seeding, ensure all values are fixed
616 item.f[0] = 1;
617 item.f[1] = 1;
618 item.f[2] = 1;
619 item.f[3] = 1;
620 item.f[4] = 1;
621 }
622 // Typically, we move from key to key
623 else if ( item.frame < out )
624 {
625 if ( mlt_geometry_next_key( this, &item, item.frame ) )
626 break;
627
628 // Special case - crop at the out point
629 if ( item.frame > out )
630 mlt_geometry_fetch( this, &item, out );
631 }
632 // We've handled the last key
633 else
634 {
635 break;
636 }
637
638 if ( item.frame - in != 0 )
639 sprintf( temp, "%d=", item.frame - in );
640
641 if ( item.f[0] )
642 sprintf( temp + strlen( temp ), "%.0f", item.x );
643 strcat( temp, "," );
644 if ( item.f[1] )
645 sprintf( temp + strlen( temp ), "%.0f", item.y );
646 strcat( temp, ":" );
647 if ( item.f[2] )
648 sprintf( temp + strlen( temp ), "%.0f", item.w );
649 strcat( temp, "x" );
650 if ( item.f[3] )
651 sprintf( temp + strlen( temp ), "%.0f", item.h );
652 if ( item.f[4] )
653 sprintf( temp + strlen( temp ), ":%.0f", item.mix );
654
655 if ( used + strlen( temp ) > size )
656 {
657 size += 1000;
658 ret = realloc( ret, size );
659 }
660
661 if ( ret != NULL && used != 0 )
662 {
663 used ++;
664 strcat( ret, ";" );
665 }
666 if ( ret != NULL )
667 {
668 used += strlen( temp );
669 strcat( ret, temp );
670 }
671
672 item.frame ++;
673 }
674 }
675
676 return ret;
677 }
678
679 // Serialise the current geometry
680 char *mlt_geometry_serialise( mlt_geometry this )
681 {
682 geometry self = this->local;
683 char *ret = mlt_geometry_serialise_cut( this, 0, self->length );
684 if ( ret )
685 {
686 free( self->data );
687 self->data = ret;
688 }
689 return ret;
690 }
691
692 // Close the geometry
693 void mlt_geometry_close( mlt_geometry this )
694 {
695 if ( this != NULL )
696 {
697 mlt_geometry_clean( this );
698 free( this->local );
699 free( this );
700 }
701 }
702
703