/*
- * 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>
#include <string.h>
-#include <math.h>
typedef struct geometry_item_s
{
// Create a new geometry structure
mlt_geometry mlt_geometry_init( )
{
- mlt_geometry this = calloc( 1, sizeof( struct mlt_geometry_s ) );
- if ( this != NULL )
+ mlt_geometry self = calloc( 1, sizeof( struct mlt_geometry_s ) );
+ if ( self != NULL )
{
- this->local = calloc( 1, sizeof( geometry_s ) );
- if ( this->local != NULL )
+ self->local = calloc( 1, sizeof( geometry_s ) );
+ if ( self->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;
+ geometry g = self->local;
+ g->nw = 720;
+ g->nh = 576;
}
else
{
- free( this );
- this = NULL;
+ free( self );
+ self = NULL;
}
}
- return this;
+ return self;
}
/** A linear step
return start + position * o;
}
-static void mlt_geometry_virtual_refresh( mlt_geometry this )
+void mlt_geometry_interpolate( mlt_geometry self )
{
- geometry self = this->local;
+ geometry g = self->local;
// Parse of all items to ensure unspecified keys are calculated correctly
- if ( self->item != NULL )
+ if ( g->item != NULL )
{
int i = 0;
for ( i = 0; i < 5; i ++ )
{
- geometry_item current = self->item;
+ geometry_item current = g->item;
while( current != NULL )
{
int fixed = current->data.f[ i ];
geometry_item prev = current->prev;
geometry_item next = current->next;
- float prev_value = 0;
- float next_value = 0;
- float value = 0;
+ 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;
}
}
-static int mlt_geometry_drop( mlt_geometry this, geometry_item item )
+static int mlt_geometry_drop( mlt_geometry self, geometry_item item )
{
- geometry self = this->local;
+ geometry g = self->local;
- if ( item == self->item )
+ if ( item == g->item )
{
- self->item = item->next;
- if ( self->item != NULL )
- self->item->prev = NULL;
+ g->item = item->next;
+ if ( g->item != NULL )
+ g->item->prev = NULL;
// To ensure correct seeding, ensure all values are fixed
- if ( self->item != NULL )
+ if ( g->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;
+ g->item->data.f[0] = 1;
+ g->item->data.f[1] = 1;
+ g->item->data.f[2] = 1;
+ g->item->data.f[3] = 1;
+ g->item->data.f[4] = 1;
}
}
else if ( item->next != NULL && item->prev != NULL )
return 0;
}
-static void mlt_geometry_clean( mlt_geometry this )
+static void mlt_geometry_clean( mlt_geometry self )
{
- geometry self = this->local;
- free( self->data );
- self->data = NULL;
- while( self->item )
- mlt_geometry_drop( this, self->item );
+ geometry g = self->local;
+ if ( g->data )
+ free( g->data );
+ g->data = NULL;
+ while( g->item )
+ mlt_geometry_drop( self, g->item );
}
// Parse the geometry specification for a given length and normalised width/height (-1 for default)
-// data is constructed as: [frame=]X,Y:WxH[:mix][;[frame=]X,Y:WxH[:mix]]*
+// data is constructed as: [frame=]X/Y:WxH[:mix][!][;[frame=]X/Y:WxH[:mix][!]]*
// and X, Y, W and H can have trailing % chars to indicate percentage of normalised size
-int mlt_geometry_parse( mlt_geometry this, char *data, int length, int nw, int nh )
+// Append a pair's value with ! to enable distort.
+int mlt_geometry_parse( mlt_geometry self, char *data, int length, int nw, int nh )
{
int i = 0;
mlt_tokeniser tokens = mlt_tokeniser_init( );
// Get the local/private structure
- geometry self = this->local;
+ geometry g = self->local;
// Clean the existing geometry
- mlt_geometry_clean( this );
+ mlt_geometry_clean( self );
// Update the info on the data
if ( length != -1 )
- self->length = length;
+ g->length = length;
if ( nw != -1 )
- self->nw = nw;
+ g->nw = nw;
if ( nh != -1 )
- self->nh = nh;
+ g->nh = nh;
if ( data != NULL )
- self->data = strdup( data );
+ g->data = strdup( data );
// Tokenise
if ( data != NULL )
memset( &item, 0, sizeof( struct mlt_geometry_item_s ) );
// Now parse the item
- mlt_geometry_parse_item( this, &item, value );
+ mlt_geometry_parse_item( self, &item, value );
// Now insert into place
- mlt_geometry_insert( this, &item );
+ mlt_geometry_insert( self, &item );
}
+ mlt_geometry_interpolate( self );
// Remove the tokeniser
mlt_tokeniser_close( tokens );
}
// Conditionally refresh in case of a change
-int mlt_geometry_refresh( mlt_geometry this, char *data, int length, int nw, int nh )
+int mlt_geometry_refresh( mlt_geometry self, char *data, int length, int nw, int nh )
{
- geometry self = this->local;
- int changed = ( length != -1 && length != self->length );
- changed = changed || ( nw != -1 && nw != self->nw );
- changed = changed || ( nh != -1 && nh != self->nh );
- changed = changed || ( data != NULL && ( self->data == NULL || strcmp( data, self->data ) ) );
+ geometry g = self->local;
+ int changed = ( length != -1 && length != g->length );
+ changed = changed || ( nw != -1 && nw != g->nw );
+ changed = changed || ( nh != -1 && nh != g->nh );
+ changed = changed || ( data != NULL && ( g->data == NULL || strcmp( data, g->data ) ) );
if ( changed )
- return mlt_geometry_parse( this, data, length, nw, nh );
+ return mlt_geometry_parse( self, data, length, nw, nh );
return -1;
}
-int mlt_geometry_get_length( mlt_geometry this )
+int mlt_geometry_get_length( mlt_geometry self )
{
// Get the local/private structure
- geometry self = this->local;
+ geometry g = self->local;
// return the length
- return self->length;
+ return g->length;
}
-void mlt_geometry_set_length( mlt_geometry this, int length )
+void mlt_geometry_set_length( mlt_geometry self, int length )
{
// Get the local/private structure
- geometry self = this->local;
+ geometry g = self->local;
- // return the length
- self->length = length;
+ // set the length
+ g->length = length;
}
-int mlt_geometry_parse_item( mlt_geometry this, mlt_geometry_item item, char *value )
+int mlt_geometry_parse_item( mlt_geometry self, mlt_geometry_item item, char *value )
{
int ret = 0;
// Get the local/private structure
- geometry self = this->local;
+ geometry g = self->local;
if ( value != NULL && strcmp( value, "" ) )
{
{
temp = atof( value );
if ( temp > -1 && temp < 1 )
- item->frame = temp * self->length;
+ item->frame = temp * g->length;
else
item->frame = temp;
value = p + 1;
// Special case - frame < 0
if ( item->frame < 0 )
- item->frame += self->length;
+ item->frame += g->length;
- // Obtain the current value at this position - this allows new
+ // Obtain the current value at this position - self allows new
// frames to be created which don't specify all values
- mlt_geometry_fetch( this, item, item->frame );
+ mlt_geometry_fetch( self, 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 ( *p == '%' )
{
if ( count == 0 || count == 2 )
- temp *= self->nw / 100.0;
+ temp *= g->nw / 100.0;
else if ( count == 1 || count == 3 )
- temp *= self->nh / 100.0;
+ temp *= g->nh / 100.0;
p ++;
}
}
// Fetch a geometry item for an absolute position
-int mlt_geometry_fetch( mlt_geometry this, mlt_geometry_item item, float position )
+int mlt_geometry_fetch( mlt_geometry self, mlt_geometry_item item, float position )
{
// Get the local geometry
- geometry self = this->local;
+ geometry g = self->local;
// Need to find the nearest key to the position specifed
- geometry_item key = self->item;
+ geometry_item key = g->item;
// Iterate through the keys until we reach last or have
while( key != NULL && key->next != NULL && position >= key->next->data.frame )
else
{
memset( item, 0, sizeof( struct mlt_geometry_item_s ) );
+ item->frame = position;
item->mix = 100;
}
}
// Specify a geometry item at an absolute position
-int mlt_geometry_insert( mlt_geometry this, mlt_geometry_item item )
+int mlt_geometry_insert( mlt_geometry self, mlt_geometry_item item )
{
// Get the local/private geometry structure
- geometry self = this->local;
+ geometry g = self->local;
- // Create a new local item (this may be removed if a key already exists at this position)
- geometry_item new = calloc( 1, sizeof( struct geometry_item_s ) );
- memcpy( &new->data, item, sizeof( struct mlt_geometry_item_s ) );
- new->data.key = 1;
+ // Create a new local item (this may be removed if a key already exists at self position)
+ geometry_item gi = calloc( 1, sizeof( struct geometry_item_s ) );
+ memcpy( &gi->data, item, sizeof( struct mlt_geometry_item_s ) );
+ gi->data.key = 1;
// Determine if we need to insert or append to the list, or if it's a new list
- if ( self->item != NULL )
+ if ( g->item != NULL )
{
// Get the first item
- geometry_item place = self->item;
+ geometry_item place = g->item;
// Locate an existing nearby item
while ( place->next != NULL && item->frame > place->data.frame )
if ( item->frame < place->data.frame )
{
- if ( place == self->item )
- self->item = new;
+ if ( place == g->item )
+ g->item = gi;
if ( place->prev )
- place->prev->next = new;
- new->next = place;
- new->prev = place->prev;
- place->prev = new;
+ place->prev->next = gi;
+ gi->next = place;
+ gi->prev = place->prev;
+ place->prev = gi;
}
else if ( item->frame > place->data.frame )
{
if ( place->next )
- place->next->prev = new;
- new->next = place->next;
- new->prev = place;
- place->next = new;
+ place->next->prev = gi;
+ gi->next = place->next;
+ gi->prev = place;
+ place->next = gi;
}
else
{
- memcpy( &place->data, &new->data, sizeof( struct mlt_geometry_item_s ) );
- free( new );
+ memcpy( &place->data, &gi->data, sizeof( struct mlt_geometry_item_s ) );
+ free( gi );
}
}
else
{
// Set the first item
- self->item = new;
+ g->item = gi;
// 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;
+ g->item->data.f[0] = 1;
+ g->item->data.f[1] = 1;
+ g->item->data.f[2] = 1;
+ g->item->data.f[3] = 1;
+ g->item->data.f[4] = 1;
}
- // Refresh all geometries
- mlt_geometry_virtual_refresh( this );
-
// TODO: Error checking
return 0;
}
// Remove the key at the specified position
-int mlt_geometry_remove( mlt_geometry this, int position )
+int mlt_geometry_remove( mlt_geometry self, int position )
{
int ret = 1;
// Get the local/private geometry structure
- geometry self = this->local;
+ geometry g = self->local;
// Get the first item
- geometry_item place = self->item;
+ geometry_item place = g->item;
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 );
+ ret = mlt_geometry_drop( self, place );
return ret;
}
// Get the key at the position or the next following
-int mlt_geometry_next_key( mlt_geometry this, mlt_geometry_item item, int position )
+int mlt_geometry_next_key( mlt_geometry self, mlt_geometry_item item, int position )
{
// Get the local/private geometry structure
- geometry self = this->local;
+ geometry g = self->local;
// Get the first item
- geometry_item place = self->item;
+ geometry_item place = g->item;
while( place != NULL && position > place->data.frame )
place = place->next;
}
// Get the key at the position or the previous key
-int mlt_geometry_prev_key( mlt_geometry this, mlt_geometry_item item, int position )
+int mlt_geometry_prev_key( mlt_geometry self, mlt_geometry_item item, int position )
{
// Get the local/private geometry structure
- geometry self = this->local;
+ geometry g = self->local;
// Get the first item
- geometry_item place = self->item;
+ geometry_item place = g->item;
while( place != NULL && place->next != NULL && position >= place->next->data.frame )
place = place->next;
return place == NULL;
}
-char *mlt_geometry_serialise_cut( mlt_geometry this, int in, int out )
+#define ISINT(x) ( (x) == (int64_t) (x) )
+#define PICKFMT(x) ( ISINT(x) ? "%.0f" : "%f" )
+
+char *mlt_geometry_serialise_cut( mlt_geometry self, int in, int out )
{
+ geometry g = self->local;
struct mlt_geometry_item_s item;
char *ret = malloc( 1000 );
int used = 0;
if ( in == -1 )
in = 0;
if ( out == -1 )
- out = mlt_geometry_get_length( this );
+ out = mlt_geometry_get_length( self );
if ( ret != NULL )
{
// If it's the first frame, then it's not necessarily a key
if ( item.frame == in )
{
- if ( mlt_geometry_fetch( this, &item, item.frame ) )
+ if ( mlt_geometry_fetch( self, &item, item.frame ) )
break;
+ // If the first key is larger than the current position
+ // then do nothing here
+ if ( g->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;
// Typically, we move from key to key
else if ( item.frame < out )
{
- if ( mlt_geometry_next_key( this, &item, item.frame ) )
+ if ( mlt_geometry_next_key( self, &item, item.frame ) )
break;
// Special case - crop at the out point
if ( item.frame > out )
- mlt_geometry_fetch( this, &item, out );
+ mlt_geometry_fetch( self, &item, out );
}
// We've handled the last key
else
if ( item.frame - in != 0 )
sprintf( temp, "%d=", item.frame - in );
- 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 )
+ if ( item.f[0] )
+ sprintf( temp + strlen( temp ), PICKFMT( item.x ), item.x );
+ if ( item.f[1] ) {
+ strcat( temp, "/" );
+ sprintf( temp + strlen( temp ), PICKFMT( item.y ), item.y );
+ }
+ if ( item.f[2] ) {
+ strcat( temp, ":" );
+ sprintf( temp + strlen( temp ), PICKFMT( item.w ), item.w );
+ }
+ if ( item.f[3] ) {
+ strcat( temp, "x" );
+ sprintf( temp + strlen( temp ), PICKFMT( item.h ), item.h );
+ }
+ if ( item.f[4] ) {
+ strcat( temp, ":" );
+ sprintf( temp + strlen( temp ), PICKFMT( item.mix ), item.mix );
+ }
+
+ if ( used + strlen( temp ) + 2 > size ) // +2 for ';' and NULL
{
size += 1000;
ret = realloc( ret, size );
}
// Serialise the current geometry
-char *mlt_geometry_serialise( mlt_geometry this )
+char *mlt_geometry_serialise( mlt_geometry self )
{
- geometry self = this->local;
- char *ret = mlt_geometry_serialise_cut( this, 0, self->length );
+ geometry g = self->local;
+ char *ret = mlt_geometry_serialise_cut( self, 0, g->length );
if ( ret )
{
- free( self->data );
- self->data = ret;
+ if ( g->data )
+ free( g->data );
+ g->data = ret;
}
return ret;
}
// Close the geometry
-void mlt_geometry_close( mlt_geometry this )
+void mlt_geometry_close( mlt_geometry self )
{
- if ( this != NULL )
+ if ( self != NULL )
{
- mlt_geometry_clean( this );
- free( this->local );
- free( this );
+ mlt_geometry_clean( self );
+ free( self->local );
+ free( self );
}
}