Added new profiles system: mlt_profile, MLT_PROFILE, and profiles documents.
[melted] / src / framework / mlt_geometry.c
index 956225a..67d98ee 100644 (file)
@@ -1,26 +1,27 @@
 /*
- * mlt_geometry.h -- provides the geometry API
+ * mlt_geometry.c -- provides the geometry API
  * Copyright (C) 2004-2005 Ushodaya Enterprises Limited
  * Author: Charles Yates <charles.yates@pandora.be>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
  *
- * This program is distributed in the hope that it will be useful,
+ * This library is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
 #include "mlt_geometry.h"
 #include "mlt_tokeniser.h"
 #include "mlt_factory.h"
+#include "mlt_profile.h"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -54,12 +55,8 @@ mlt_geometry mlt_geometry_init( )
                if ( this->local != NULL )
                {
                        geometry self = this->local;
-                       char *normalisation = mlt_environment( "MLT_NORMALISATION" );
-                       self->nw = 720;
-                       if ( normalisation == NULL || strcmp( normalisation, "NTSC" ) )
-                               self->nh = 576;
-                       else
-                               self->nh = 480;
+                       self->nw = mlt_profile_get()->width;
+                       self->nh = mlt_profile_get()->height;
                }
                else
                {
@@ -70,6 +67,90 @@ mlt_geometry mlt_geometry_init( )
        return this;
 }
 
+/** A linear step
+*/
+
+static inline double linearstep( double start, double end, double position, int length )
+{
+       double o = ( end - start ) / length;
+       return start + position * o;
+}
+
+static void mlt_geometry_virtual_refresh( mlt_geometry this )
+{
+       geometry self = this->local;
+
+       // Parse of all items to ensure unspecified keys are calculated correctly
+       if ( self->item != NULL )
+       {
+               int i = 0;
+               for ( i = 0; i < 5; i ++ )
+               {
+                       geometry_item current = self->item;
+                       while( current != NULL )
+                       {
+                               int fixed = current->data.f[ i ];
+                               if ( !fixed )
+                               {
+                                       geometry_item prev = current->prev;
+                                       geometry_item next = current->next;
+
+                                       double prev_value = 0;
+                                       double next_value = 0;
+                                       double value = 0;
+
+                                       while( prev != NULL && !prev->data.f[ i ] ) prev = prev->prev;
+                                       while( next != NULL && !next->data.f[ i ] ) next = next->next;
+
+                                       switch( i )
+                                       {
+                                               case 0:
+                                                       if ( prev ) prev_value = prev->data.x;
+                                                       if ( next ) next_value = next->data.x;
+                                                       break;
+                                               case 1:
+                                                       if ( prev ) prev_value = prev->data.y;
+                                                       if ( next ) next_value = next->data.y;
+                                                       break;
+                                               case 2:
+                                                       if ( prev ) prev_value = prev->data.w;
+                                                       if ( next ) next_value = next->data.w;
+                                                       break;
+                                               case 3:
+                                                       if ( prev ) prev_value = prev->data.h;
+                                                       if ( next ) next_value = next->data.h;
+                                                       break;
+                                               case 4:
+                                                       if ( prev ) prev_value = prev->data.mix;
+                                                       if ( next ) next_value = next->data.mix;
+                                                       break;
+                                       }
+
+                                       // This should never happen
+                                       if ( prev == NULL )
+                                               current->data.f[ i ] = 1;
+                                       else if ( next == NULL )
+                                               value = prev_value;
+                                       else 
+                                               value = linearstep( prev_value, next_value, current->data.frame - prev->data.frame, next->data.frame - prev->data.frame );
+
+                                       switch( i )
+                                       {
+                                               case 0: current->data.x = value; break;
+                                               case 1: current->data.y = value; break;
+                                               case 2: current->data.w = value; break;
+                                               case 3: current->data.h = value; break;
+                                               case 4: current->data.mix = value; break;
+                                       }
+                               }
+
+                               // Move to the next item
+                               current = current->next;
+                       }
+               }
+       }
+}
+
 static int mlt_geometry_drop( mlt_geometry this, geometry_item item )
 {
        geometry self = this->local;
@@ -79,6 +160,20 @@ static int mlt_geometry_drop( mlt_geometry this, geometry_item item )
                self->item = item->next;
                if ( self->item != NULL )
                        self->item->prev = NULL;
+               // To ensure correct seeding, ensure all values are fixed
+               if ( self->item != NULL )
+               {
+                       self->item->data.f[0] = 1;
+                       self->item->data.f[1] = 1;
+                       self->item->data.f[2] = 1;
+                       self->item->data.f[3] = 1;
+                       self->item->data.f[4] = 1;
+               }
+       }
+       else if ( item->next != NULL && item->prev != NULL )
+       {
+               item->prev->next = item->next;
+               item->next->prev = item->prev;
        }
        else if ( item->next != NULL )
        {
@@ -87,7 +182,6 @@ static int mlt_geometry_drop( mlt_geometry this, geometry_item item )
        else if ( item->prev != NULL )
        {
                item->prev->next = item->next;
-               item->next->prev = item->prev;
        }
 
        free( item );
@@ -184,7 +278,7 @@ void mlt_geometry_set_length( mlt_geometry this, int length )
        // Get the local/private structure
        geometry self = this->local;
 
-       // return the length
+       // set the length
        self->length = length;
 }
 
@@ -199,12 +293,16 @@ int mlt_geometry_parse_item( mlt_geometry this, mlt_geometry_item item, char *va
        {
                char *p = strchr( value, '=' );
                int count = 0;
-               float temp;
+               double temp;
 
                // Determine if a position has been specified
                if ( p != NULL )
                {
-                       item->frame = atoi( value );
+                       temp = atof( value );
+                       if ( temp > -1 && temp < 1 )
+                               item->frame = temp * self->length;
+                       else
+                               item->frame = temp;
                        value = p + 1;
                }
 
@@ -216,6 +314,17 @@ int mlt_geometry_parse_item( mlt_geometry this, mlt_geometry_item item, char *va
                // frames to be created which don't specify all values
                mlt_geometry_fetch( this, item, item->frame );
 
+               // Special case - when an empty string is specified, all values are fixed
+               // TODO: Check if this is logical - it's convenient, but it's also odd...
+               if ( !*value )
+               {
+                       item->f[0] = 1;
+                       item->f[1] = 1;
+                       item->f[2] = 1;
+                       item->f[3] = 1;
+                       item->f[4] = 1;
+               }
+
                // Iterate through the remainder of value
                while( *value )
                {
@@ -236,7 +345,7 @@ int mlt_geometry_parse_item( mlt_geometry this, mlt_geometry_item item, char *va
                                }
 
                                // Special case - distort token
-                               if ( *p == '!' )
+                               if ( *p == '!' || *p == '*' )
                                {
                                        p ++;
                                        item->distort = 1;
@@ -248,11 +357,11 @@ int mlt_geometry_parse_item( mlt_geometry this, mlt_geometry_item item, char *va
                                // Assign to the item
                                switch( count )
                                {
-                                       case 0: item->x = temp; break;
-                                       case 1: item->y = temp; break;
-                                       case 2: item->w = temp; break;
-                                       case 3: item->h = temp; break;
-                                       case 4: item->mix = temp; break;
+                                       case 0: item->x = temp; item->f[0] = 1; break;
+                                       case 1: item->y = temp; item->f[1] = 1; break;
+                                       case 2: item->w = temp; item->f[2] = 1; break;
+                                       case 3: item->h = temp; item->f[3] = 1; break;
+                                       case 4: item->mix = temp; item->f[4] = 1; break;
                                }
                        }
                        else
@@ -273,15 +382,6 @@ int mlt_geometry_parse_item( mlt_geometry this, mlt_geometry_item item, char *va
        return ret;
 }
 
-/** A linear step
-*/
-
-static inline float linearstep( float start, float end, float position, int length )
-{
-       float o = ( end - start ) / length;
-       return start + position * o + 0.5;
-}
-
 // Fetch a geometry item for an absolute position
 int mlt_geometry_fetch( mlt_geometry this, mlt_geometry_item item, float position )
 {
@@ -313,6 +413,11 @@ int mlt_geometry_fetch( mlt_geometry this, mlt_geometry_item item, float positio
                {
                        memcpy( item, &key->data, sizeof( struct mlt_geometry_item_s ) );
                        item->key = 0;
+                       item->f[ 0 ] = 0;
+                       item->f[ 1 ] = 0;
+                       item->f[ 2 ] = 0;
+                       item->f[ 3 ] = 0;
+                       item->f[ 4 ] = 0;
                }
                // Interpolation is needed - position > key and there is a following key
                else
@@ -334,6 +439,7 @@ int mlt_geometry_fetch( mlt_geometry this, mlt_geometry_item item, float positio
        else
        {
                memset( item, 0, sizeof( struct mlt_geometry_item_s ) );
+               item->frame = position;
                item->mix = 100;
        }
 
@@ -373,6 +479,8 @@ int mlt_geometry_insert( mlt_geometry this, mlt_geometry_item item )
                }
                else if ( item->frame > place->data.frame )
                {
+                       if ( place->next )
+                               place->next->prev = new;
                        new->next = place->next;
                        new->prev = place;
                        place->next = new;
@@ -385,9 +493,20 @@ int mlt_geometry_insert( mlt_geometry this, mlt_geometry_item item )
        }
        else
        {
+               // Set the first item
                self->item = new;
+
+               // To ensure correct seeding, ensure all values are fixed
+               self->item->data.f[0] = 1;
+               self->item->data.f[1] = 1;
+               self->item->data.f[2] = 1;
+               self->item->data.f[3] = 1;
+               self->item->data.f[4] = 1;
        }
 
+       // Refresh all geometries
+       mlt_geometry_virtual_refresh( this );
+
        // TODO: Error checking
        return 0;
 }
@@ -403,17 +522,20 @@ int mlt_geometry_remove( mlt_geometry this, int position )
        // Get the first item
        geometry_item place = self->item;
 
-       while( place != NULL && position < place->data.frame )
+       while( place != NULL && position != place->data.frame )
                place = place->next;
 
        if ( place != NULL && position == place->data.frame )
                ret = mlt_geometry_drop( this, place );
 
+       // Refresh all geometries
+       mlt_geometry_virtual_refresh( this );
+
        return ret;
 }
 
 // Get the key at the position or the next following
-int mlt_geometry_key( mlt_geometry this, mlt_geometry_item item, int position )
+int mlt_geometry_next_key( mlt_geometry this, mlt_geometry_item item, int position )
 {
        // Get the local/private geometry structure
        geometry self = this->local;
@@ -430,8 +552,27 @@ int mlt_geometry_key( mlt_geometry this, mlt_geometry_item item, int position )
        return place == NULL;
 }
 
+// Get the key at the position or the previous key
+int mlt_geometry_prev_key( mlt_geometry this, mlt_geometry_item item, int position )
+{
+       // Get the local/private geometry structure
+       geometry self = this->local;
+
+       // Get the first item
+       geometry_item place = self->item;
+
+       while( place != NULL && place->next != NULL && position >= place->next->data.frame )
+               place = place->next;
+
+       if ( place != NULL )
+               memcpy( item, &place->data, sizeof( struct mlt_geometry_item_s ) );
+
+       return place == NULL;
+}
+
 char *mlt_geometry_serialise_cut( mlt_geometry this, int in, int out )
 {
+       geometry self = this->local;
        struct mlt_geometry_item_s item;
        char *ret = malloc( 1000 );
        int used = 0;
@@ -459,11 +600,26 @@ char *mlt_geometry_serialise_cut( mlt_geometry this, int in, int out )
                        {
                                if ( mlt_geometry_fetch( this, &item, item.frame ) )
                                        break;
+
+                               // If the first key is larger than the current position
+                               // then do nothing here
+                               if ( self->item->data.frame > item.frame )
+                               {
+                                       item.frame ++;
+                                       continue;
+                               }
+
+                               // To ensure correct seeding, ensure all values are fixed
+                               item.f[0] = 1;
+                               item.f[1] = 1;
+                               item.f[2] = 1;
+                               item.f[3] = 1;
+                               item.f[4] = 1;
                        }
                        // Typically, we move from key to key
                        else if ( item.frame < out )
                        {
-                               if ( mlt_geometry_key( this, &item, item.frame ) )
+                               if ( mlt_geometry_next_key( this, &item, item.frame ) )
                                        break;
 
                                // Special case - crop at the out point
@@ -479,9 +635,18 @@ char *mlt_geometry_serialise_cut( mlt_geometry this, int in, int out )
                        if ( item.frame - in != 0 )
                                sprintf( temp, "%d=", item.frame - in );
 
-                       sprintf( temp + strlen( temp ), "%.0f,%.0f:%.0fx%.0f%s", item.x, item.y, item.w, item.h, item.distort ? "!" : "" );
-
-                       if ( item.mix )
+                       if ( item.f[0] ) 
+                               sprintf( temp + strlen( temp ), "%.0f", item.x );
+                       strcat( temp, "," );
+                       if ( item.f[1] ) 
+                               sprintf( temp + strlen( temp ), "%.0f", item.y );
+                       strcat( temp, ":" );
+                       if ( item.f[2] ) 
+                               sprintf( temp + strlen( temp ), "%.0f", item.w );
+                       strcat( temp, "x" );
+                       if ( item.f[3] ) 
+                               sprintf( temp + strlen( temp ), "%.0f", item.h );
+                       if ( item.f[4] ) 
                                sprintf( temp + strlen( temp ), ":%.0f", item.mix );
 
                        if ( used + strlen( temp ) > size )