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