]> git.sesse.net Git - mlt/blob - src/framework/mlt_geometry.c
Luma and composite fixes
[mlt] / src / framework / mlt_geometry.c
1 /*
2  * mlt_geometry.h -- provides the geometry API
3  * Copyright (C) 2004-2005 Ushodaya Enterprises Limited
4  * Author: Charles Yates <charles.yates@pandora.be>
5  *
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.
10  *
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.
15  *
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.
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 static int mlt_geometry_drop( mlt_geometry this, geometry_item item )
74 {
75         geometry self = this->local;
76
77         if ( item == self->item )
78         {
79                 self->item = item->next;
80                 if ( self->item != NULL )
81                         self->item->prev = NULL;
82         }
83         else if ( item->next != NULL )
84         {
85                 item->next->prev = item->prev;
86         }
87         else if ( item->prev != NULL )
88         {
89                 item->prev->next = item->next;
90                 item->next->prev = item->prev;
91         }
92
93         free( item );
94
95         return 0;
96 }
97
98 static void mlt_geometry_clean( mlt_geometry this )
99 {
100         geometry self = this->local;
101         free( self->data );
102         self->data = NULL;
103         while( self->item )
104                 mlt_geometry_drop( this, self->item );
105 }
106
107 // Parse the geometry specification for a given length and normalised width/height (-1 for default)
108 // data is constructed as: [frame=]X,Y:WxH[:mix][;[frame=]X,Y:WxH[:mix]]*
109 // and X, Y, W and H can have trailing % chars to indicate percentage of normalised size
110 int mlt_geometry_parse( mlt_geometry this, char *data, int length, int nw, int nh )
111 {
112         int i = 0;
113
114         // Create a tokeniser
115         mlt_tokeniser tokens = mlt_tokeniser_init( );
116
117         // Get the local/private structure
118         geometry self = this->local;
119
120         // Clean the existing geometry
121         mlt_geometry_clean( this );
122
123         // Update the info on the data
124         if ( length != -1 )
125                 self->length = length;
126         if ( nw != -1 )
127                 self->nw = nw;
128         if ( nh != -1 )
129                 self->nh = nh;
130         if ( data != NULL )
131                 self->data = strdup( data );
132
133         // Tokenise
134         if ( data != NULL )
135                 mlt_tokeniser_parse_new( tokens, data, ";" );
136
137         // Iterate through each token
138         for ( i = 0; i < mlt_tokeniser_count( tokens ); i ++ )
139         {
140                 struct mlt_geometry_item_s item;
141                 char *value = mlt_tokeniser_get_string( tokens, i );
142
143                 // Set item to 0
144                 memset( &item, 0, sizeof( struct mlt_geometry_item_s ) );
145
146                 // Now parse the item
147                 mlt_geometry_parse_item( this, &item, value );
148
149                 // Now insert into place
150                 mlt_geometry_insert( this, &item );
151         }
152
153         // Remove the tokeniser
154         mlt_tokeniser_close( tokens );
155
156         // ???
157         return 0;
158 }
159
160 // Conditionally refresh in case of a change
161 int mlt_geometry_refresh( mlt_geometry this, char *data, int length, int nw, int nh )
162 {
163         geometry self = this->local;
164         int changed = ( length != -1 && length != self->length );
165         changed = changed || ( nw != -1 && nw != self->nw );
166         changed = changed || ( nh != -1 && nh != self->nh );
167         changed = changed || ( data != NULL && ( self->data == NULL || strcmp( data, self->data ) ) );
168         if ( changed )
169                 return mlt_geometry_parse( this, data, length, nw, nh );
170         return -1;
171 }
172
173 int mlt_geometry_get_length( mlt_geometry this )
174 {
175         // Get the local/private structure
176         geometry self = this->local;
177
178         // return the length
179         return self->length;
180 }
181
182 void mlt_geometry_set_length( mlt_geometry this, int length )
183 {
184         // Get the local/private structure
185         geometry self = this->local;
186
187         // return the length
188         self->length = length;
189 }
190
191 int mlt_geometry_parse_item( mlt_geometry this, mlt_geometry_item item, char *value )
192 {
193         int ret = 0;
194
195         // Get the local/private structure
196         geometry self = this->local;
197
198         if ( value != NULL && strcmp( value, "" ) )
199         {
200                 char *p = strchr( value, '=' );
201                 int count = 0;
202                 float temp;
203
204                 // Determine if a position has been specified
205                 if ( p != NULL )
206                 {
207                         item->frame = atoi( value );
208                         value = p + 1;
209                 }
210
211                 // Special case - frame < 0
212                 if ( item->frame < 0 )
213                         item->frame += self->length;
214
215                 // Obtain the current value at this position - this allows new
216                 // frames to be created which don't specify all values
217                 mlt_geometry_fetch( this, item, item->frame );
218
219                 // Iterate through the remainder of value
220                 while( *value )
221                 {
222                         // Get the value
223                         temp = strtod( value, &p );
224
225                         // Check if a value was specified
226                         if ( p != value )
227                         {
228                                 // Handle the % case
229                                 if ( *p == '%' )
230                                 {
231                                         if ( count == 0 || count == 2 )
232                                                 temp *= self->nw / 100.0;
233                                         else if ( count == 1 || count == 3 )
234                                                 temp *= self->nh / 100.0;
235                                         p ++;
236                                 }
237
238                                 // Special case - distort token
239                                 if ( *p == '!' )
240                                 {
241                                         p ++;
242                                         item->distort = 1;
243                                 }
244
245                                 // Actually, we don't care about the delimiter at all..
246                                 if ( *p ) p ++;
247
248                                 // Assign to the item
249                                 switch( count )
250                                 {
251                                         case 0: item->x = temp; break;
252                                         case 1: item->y = temp; break;
253                                         case 2: item->w = temp; break;
254                                         case 3: item->h = temp; break;
255                                         case 4: item->mix = temp; break;
256                                 }
257                         }
258                         else
259                         {
260                                 p ++;
261                         }
262
263                         // Update the value pointer
264                         value = p;
265                         count ++;
266                 }
267         }
268         else
269         {
270                 ret = 1;
271         }
272
273         return ret;
274 }
275
276 /** A linear step
277 */
278
279 static inline float linearstep( float start, float end, float position, int length )
280 {
281         float o = ( end - start ) / length;
282         return start + position * o;
283 }
284
285 // Fetch a geometry item for an absolute position
286 int mlt_geometry_fetch( mlt_geometry this, mlt_geometry_item item, float position )
287 {
288         // Get the local geometry
289         geometry self = this->local;
290
291         // Need to find the nearest key to the position specifed
292         geometry_item key = self->item;
293
294         // Iterate through the keys until we reach last or have 
295         while( key != NULL && key->next != NULL && position >= key->next->data.frame )
296                 key = key->next;
297
298         if ( key != NULL )
299         {
300                 // Position is situated before the first key - all zeroes
301                 if ( position < key->data.frame )
302                 {
303                         memset( item, 0, sizeof( struct mlt_geometry_item_s ) );
304                         item->mix = 100;
305                 }
306                 // Position is a key itself - no iterpolation need
307                 else if ( position == key->data.frame )
308                 {
309                         memcpy( item, &key->data, sizeof( struct mlt_geometry_item_s ) );
310                 }
311                 // Position is after the last key - no interpolation, but not a key frame
312                 else if ( key->next == NULL )
313                 {
314                         memcpy( item, &key->data, sizeof( struct mlt_geometry_item_s ) );
315                         item->key = 0;
316                 }
317                 // Interpolation is needed - position > key and there is a following key
318                 else
319                 {
320                         item->key = 0;
321                         item->frame = position;
322                         position -= key->data.frame;
323                         item->x = linearstep( key->data.x, key->next->data.x, position, key->next->data.frame - key->data.frame );
324                         item->y = linearstep( key->data.y, key->next->data.y, position, key->next->data.frame - key->data.frame );
325                         item->w = linearstep( key->data.w, key->next->data.w, position, key->next->data.frame - key->data.frame );
326                         item->h = linearstep( key->data.h, key->next->data.h, position, key->next->data.frame - key->data.frame );
327                         item->mix = linearstep( key->data.mix, key->next->data.mix, position, key->next->data.frame - key->data.frame );
328                         item->distort = key->data.distort;
329                         position += key->data.frame;
330                 }
331
332                 item->frame = position;
333         }
334         else
335         {
336                 memset( item, 0, sizeof( struct mlt_geometry_item_s ) );
337                 item->mix = 100;
338         }
339
340         return key == NULL;
341 }
342
343 // Specify a geometry item at an absolute position
344 int mlt_geometry_insert( mlt_geometry this, mlt_geometry_item item )
345 {
346         // Get the local/private geometry structure
347         geometry self = this->local;
348
349         // Create a new local item (this may be removed if a key already exists at this position)
350         geometry_item new = calloc( 1, sizeof( struct geometry_item_s ) );
351         memcpy( &new->data, item, sizeof( struct mlt_geometry_item_s ) );
352         new->data.key = 1;
353
354         // Determine if we need to insert or append to the list, or if it's a new list
355         if ( self->item != NULL )
356         {
357                 // Get the first item
358                 geometry_item place = self->item;
359
360                 // Locate an existing nearby item
361                 while ( place->next != NULL && item->frame > place->data.frame )
362                         place = place->next;
363
364                 if ( item->frame < place->data.frame )
365                 {
366                         if ( place == self->item )
367                                 self->item = new;
368                         if ( place->prev )
369                                 place->prev->next = new;
370                         new->next = place;
371                         new->prev = place->prev;
372                         place->prev = new;
373                 }
374                 else if ( item->frame > place->data.frame )
375                 {
376                         new->next = place->next;
377                         new->prev = place;
378                         place->next = new;
379                 }
380                 else
381                 {
382                         memcpy( &place->data, &new->data, sizeof( struct mlt_geometry_item_s ) );
383                         free( new );
384                 }
385         }
386         else
387         {
388                 self->item = new;
389         }
390
391         // TODO: Error checking
392         return 0;
393 }
394
395 // Remove the key at the specified position
396 int mlt_geometry_remove( mlt_geometry this, int position )
397 {
398         int ret = 1;
399
400         // Get the local/private geometry structure
401         geometry self = this->local;
402
403         // Get the first item
404         geometry_item place = self->item;
405
406         while( place != NULL && position < place->data.frame )
407                 place = place->next;
408
409         if ( place != NULL && position == place->data.frame )
410                 ret = mlt_geometry_drop( this, place );
411
412         return ret;
413 }
414
415 // Get the key at the position or the next following
416 int mlt_geometry_key( mlt_geometry this, mlt_geometry_item item, int position )
417 {
418         // Get the local/private geometry structure
419         geometry self = this->local;
420
421         // Get the first item
422         geometry_item place = self->item;
423
424         while( place != NULL && position > place->data.frame )
425                 place = place->next;
426
427         if ( place != NULL )
428                 memcpy( item, &place->data, sizeof( struct mlt_geometry_item_s ) );
429
430         return place == NULL;
431 }
432
433 char *mlt_geometry_serialise_cut( mlt_geometry this, int in, int out )
434 {
435         struct mlt_geometry_item_s item;
436         char *ret = malloc( 1000 );
437         int used = 0;
438         int size = 1000;
439
440         if ( in == -1 )
441                 in = 0;
442         if ( out == -1 )
443                 out = mlt_geometry_get_length( this );
444
445         if ( ret != NULL )
446         {
447                 char temp[ 100 ];
448
449                 strcpy( ret, "" );
450
451                 item.frame = in;
452
453                 while( 1 )
454                 {
455                         strcpy( temp, "" );
456
457                         // If it's the first frame, then it's not necessarily a key
458                         if ( item.frame == in )
459                         {
460                                 if ( mlt_geometry_fetch( this, &item, item.frame ) )
461                                         break;
462                         }
463                         // Typically, we move from key to key
464                         else if ( item.frame < out )
465                         {
466                                 if ( mlt_geometry_key( this, &item, item.frame ) )
467                                         break;
468
469                                 // Special case - crop at the out point
470                                 if ( item.frame > out )
471                                         mlt_geometry_fetch( this, &item, out );
472                         }
473                         // We've handled the last key
474                         else
475                         {
476                                 break;
477                         }
478
479                         if ( item.frame - in != 0 )
480                                 sprintf( temp, "%d=", item.frame - in );
481
482                         sprintf( temp + strlen( temp ), "%.0f,%.0f:%.0fx%.0f%s", item.x, item.y, item.w, item.h, item.distort ? "!" : "" );
483
484                         if ( item.mix )
485                                 sprintf( temp + strlen( temp ), ":%.0f", item.mix );
486
487                         if ( used + strlen( temp ) > size )
488                         {
489                                 size += 1000;
490                                 ret = realloc( ret, size );
491                         }
492
493                         if ( ret != NULL && used != 0 )
494                         {
495                                 used ++;
496                                 strcat( ret, ";" );
497                         }
498                         if ( ret != NULL )
499                         {
500                                 used += strlen( temp );
501                                 strcat( ret, temp );
502                         }
503
504                         item.frame ++;
505                 }
506         }
507
508         return ret;
509 }
510
511 // Serialise the current geometry
512 char *mlt_geometry_serialise( mlt_geometry this )
513 {
514         geometry self = this->local;
515         char *ret = mlt_geometry_serialise_cut( this, 0, self->length );
516         if ( ret )
517         {
518                 free( self->data );
519                 self->data = ret;
520         }
521         return ret;
522 }
523
524 // Close the geometry
525 void mlt_geometry_close( mlt_geometry this )
526 {
527         if ( this != NULL )
528         {
529                 mlt_geometry_clean( this );
530                 free( this->local );
531                 free( this );
532         }
533 }
534
535