--- /dev/null
+/*
+ * mlt_geometry.h -- 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 program 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.
+ *
+ * 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.
+ */
+
+#include "mlt_geometry.h"
+#include "mlt_tokeniser.h"
+#include "mlt_factory.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+typedef struct geometry_item_s
+{
+ struct mlt_geometry_item_s data;
+ struct geometry_item_s *next, *prev;
+}
+*geometry_item;
+
+typedef struct
+{
+ char *data;
+ int length;
+ int nw;
+ int nh;
+ geometry_item item;
+}
+geometry_s, *geometry;
+
+// Create a new geometry structure
+mlt_geometry mlt_geometry_init( )
+{
+ mlt_geometry this = calloc( 1, sizeof( struct mlt_geometry_s ) );
+ if ( this != NULL )
+ {
+ this->local = calloc( 1, sizeof( geometry_s ) );
+ 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;
+ }
+ else
+ {
+ free( this );
+ this = NULL;
+ }
+ }
+ return this;
+}
+
+static int mlt_geometry_drop( mlt_geometry this, geometry_item item )
+{
+ geometry self = this->local;
+
+ if ( item == self->item )
+ {
+ self->item = item->next;
+ if ( self->item != NULL )
+ self->item->prev = NULL;
+ }
+ else if ( item->next != NULL )
+ {
+ item->next->prev = item->prev;
+ }
+ else if ( item->prev != NULL )
+ {
+ item->prev->next = item->next;
+ item->next->prev = item->prev;
+ }
+
+ free( item );
+
+ return 0;
+}
+
+static void mlt_geometry_clean( mlt_geometry this )
+{
+ geometry self = this->local;
+ free( self->data );
+ self->data = NULL;
+ while( self->item )
+ mlt_geometry_drop( this, self->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]]*
+// 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 )
+{
+ int i = 0;
+
+ // Create a tokeniser
+ mlt_tokeniser tokens = mlt_tokeniser_init( );
+
+ // Get the local/private structure
+ geometry self = this->local;
+
+ // Clean the existing geometry
+ mlt_geometry_clean( this );
+
+ // Update the info on the data
+ if ( length != -1 )
+ self->length = length;
+ if ( nw != -1 )
+ self->nw = nw;
+ if ( nh != -1 )
+ self->nh = nh;
+ if ( data != NULL )
+ self->data = strdup( data );
+
+ // Tokenise
+ if ( data != NULL )
+ mlt_tokeniser_parse_new( tokens, data, ";" );
+
+ // Iterate through each token
+ for ( i = 0; i < mlt_tokeniser_count( tokens ); i ++ )
+ {
+ struct mlt_geometry_item_s item;
+ char *value = mlt_tokeniser_get_string( tokens, i );
+
+ // Set item to 0
+ memset( &item, 0, sizeof( struct mlt_geometry_item_s ) );
+
+ // Now parse the item
+ mlt_geometry_parse_item( this, &item, value );
+
+ // Now insert into place
+ mlt_geometry_insert( this, &item );
+ }
+
+ // Remove the tokeniser
+ mlt_tokeniser_close( tokens );
+
+ // ???
+ return 0;
+}
+
+// Conditionally refresh in case of a change
+int mlt_geometry_refresh( mlt_geometry this, 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 ) ) );
+ if ( changed )
+ return mlt_geometry_parse( this, data, length, nw, nh );
+ return -1;
+}
+
+int mlt_geometry_get_length( mlt_geometry this )
+{
+ // Get the local/private structure
+ geometry self = this->local;
+
+ // return the length
+ return self->length;
+}
+
+void mlt_geometry_set_length( mlt_geometry this, int length )
+{
+ // Get the local/private structure
+ geometry self = this->local;
+
+ // return the length
+ self->length = length;
+}
+
+int mlt_geometry_parse_item( mlt_geometry this, mlt_geometry_item item, char *value )
+{
+ int ret = 0;
+
+ // Get the local/private structure
+ geometry self = this->local;
+
+ if ( value != NULL && strcmp( value, "" ) )
+ {
+ char *p = strchr( value, '=' );
+ int count = 0;
+ float temp;
+
+ // Determine if a position has been specified
+ if ( p != NULL )
+ {
+ item->frame = atoi( value );
+ value = p + 1;
+ }
+
+ // Special case - frame < 0
+ if ( item->frame < 0 )
+ item->frame += self->length;
+
+ // Obtain the current value at this position - this allows new
+ // frames to be created which don't specify all values
+ mlt_geometry_fetch( this, item, item->frame );
+
+ // Iterate through the remainder of value
+ while( *value )
+ {
+ // Get the value
+ temp = strtod( value, &p );
+
+ // Check if a value was specified
+ if ( p != value )
+ {
+ // Handle the % case
+ if ( *p == '%' )
+ {
+ if ( count == 0 || count == 2 )
+ temp *= self->nw / 100.0;
+ else if ( count == 1 || count == 3 )
+ temp *= self->nh / 100.0;
+ p ++;
+ }
+
+ // Special case - distort token
+ if ( *p == '!' )
+ {
+ p ++;
+ item->distort = 1;
+ }
+
+ // Actually, we don't care about the delimiter at all..
+ if ( *p ) p ++;
+
+ // 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;
+ }
+ }
+ else
+ {
+ p ++;
+ }
+
+ // Update the value pointer
+ value = p;
+ count ++;
+ }
+ }
+ else
+ {
+ ret = 1;
+ }
+
+ 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 )
+{
+ // Get the local geometry
+ geometry self = this->local;
+
+ // Need to find the nearest key to the position specifed
+ geometry_item key = self->item;
+
+ // Iterate through the keys until we reach last or have
+ while( key != NULL && key->next != NULL && position >= key->next->data.frame )
+ key = key->next;
+
+ if ( key != NULL )
+ {
+ // Position is situated before the first key - all zeroes
+ if ( position < key->data.frame )
+ {
+ memset( item, 0, sizeof( struct mlt_geometry_item_s ) );
+ item->mix = 100;
+ }
+ // Position is a key itself - no iterpolation need
+ else if ( position == key->data.frame )
+ {
+ memcpy( item, &key->data, sizeof( struct mlt_geometry_item_s ) );
+ }
+ // Position is after the last key - no interpolation, but not a key frame
+ else if ( key->next == NULL )
+ {
+ memcpy( item, &key->data, sizeof( struct mlt_geometry_item_s ) );
+ item->key = 0;
+ }
+ // Interpolation is needed - position > key and there is a following key
+ else
+ {
+ item->key = 0;
+ item->frame = position;
+ position -= key->data.frame;
+ item->x = linearstep( key->data.x, key->next->data.x, position, key->next->data.frame - key->data.frame );
+ item->y = linearstep( key->data.y, key->next->data.y, position, key->next->data.frame - key->data.frame );
+ item->w = linearstep( key->data.w, key->next->data.w, position, key->next->data.frame - key->data.frame );
+ item->h = linearstep( key->data.h, key->next->data.h, position, key->next->data.frame - key->data.frame );
+ item->mix = linearstep( key->data.mix, key->next->data.mix, position, key->next->data.frame - key->data.frame );
+ item->distort = key->data.distort;
+ position += key->data.frame;
+ }
+
+ item->frame = position;
+ }
+ else
+ {
+ memset( item, 0, sizeof( struct mlt_geometry_item_s ) );
+ item->mix = 100;
+ }
+
+ return key == NULL;
+}
+
+// Specify a geometry item at an absolute position
+int mlt_geometry_insert( mlt_geometry this, mlt_geometry_item item )
+{
+ // Get the local/private geometry structure
+ geometry self = this->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;
+
+ // Determine if we need to insert or append to the list, or if it's a new list
+ if ( self->item != NULL )
+ {
+ // Get the first item
+ geometry_item place = self->item;
+
+ // Locate an existing nearby item
+ while ( place->next != NULL && item->frame > place->data.frame )
+ place = place->next;
+
+ if ( item->frame < place->data.frame )
+ {
+ if ( place == self->item )
+ self->item = new;
+ if ( place->prev )
+ place->prev->next = new;
+ new->next = place;
+ new->prev = place->prev;
+ place->prev = new;
+ }
+ else if ( item->frame > place->data.frame )
+ {
+ new->next = place->next;
+ new->prev = place;
+ place->next = new;
+ }
+ else
+ {
+ memcpy( &place->data, &new->data, sizeof( struct mlt_geometry_item_s ) );
+ free( new );
+ }
+ }
+ else
+ {
+ self->item = new;
+ }
+
+ // TODO: Error checking
+ return 0;
+}
+
+// Remove the key at the specified position
+int mlt_geometry_remove( mlt_geometry this, int position )
+{
+ int ret = 1;
+
+ // Get the local/private geometry structure
+ geometry self = this->local;
+
+ // Get the first item
+ geometry_item place = self->item;
+
+ while( place != NULL && position < place->data.frame )
+ place = place->next;
+
+ if ( place != NULL && position == place->data.frame )
+ ret = mlt_geometry_drop( this, place );
+
+ 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 )
+{
+ // Get the local/private geometry structure
+ geometry self = this->local;
+
+ // Get the first item
+ geometry_item place = self->item;
+
+ while( place != NULL && position > place->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 )
+{
+ struct mlt_geometry_item_s item;
+ char *ret = malloc( 1000 );
+ int used = 0;
+ int size = 1000;
+
+ if ( in == -1 )
+ in = 0;
+ if ( out == -1 )
+ out = mlt_geometry_get_length( this );
+
+ if ( ret != NULL )
+ {
+ char temp[ 100 ];
+
+ strcpy( ret, "" );
+
+ item.frame = in;
+
+ while( 1 )
+ {
+ strcpy( temp, "" );
+
+ // 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 ) )
+ break;
+ }
+ // Typically, we move from key to key
+ else if ( item.frame < out )
+ {
+ if ( mlt_geometry_key( this, &item, item.frame ) )
+ break;
+
+ // Special case - crop at the out point
+ if ( item.frame > out )
+ mlt_geometry_fetch( this, &item, out );
+ }
+ // We've handled the last key
+ else
+ {
+ break;
+ }
+
+ 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 )
+ sprintf( temp + strlen( temp ), ":%.0f", item.mix );
+
+ if ( used + strlen( temp ) > size )
+ {
+ size += 1000;
+ ret = realloc( ret, size );
+ }
+
+ if ( ret != NULL && used != 0 )
+ {
+ used ++;
+ strcat( ret, ";" );
+ }
+ if ( ret != NULL )
+ {
+ used += strlen( temp );
+ strcat( ret, temp );
+ }
+
+ item.frame ++;
+ }
+ }
+
+ return ret;
+}
+
+// Serialise the current geometry
+char *mlt_geometry_serialise( mlt_geometry this )
+{
+ geometry self = this->local;
+ char *ret = mlt_geometry_serialise_cut( this, 0, self->length );
+ if ( ret )
+ {
+ free( self->data );
+ self->data = ret;
+ }
+ return ret;
+}
+
+// Close the geometry
+void mlt_geometry_close( mlt_geometry this )
+{
+ if ( this != NULL )
+ {
+ mlt_geometry_clean( this );
+ free( this->local );
+ free( this );
+ }
+}
+
+
struct geometry_s
{
- int frame;
- float position;
- float mix;
+ struct mlt_geometry_item_s item;
int nw; // normalised width
int nh; // normalised height
int sw; // scaled width, not including consumer scale based upon w/nw
int sh; // scaled height, not including consumer scale based upon h/nh
- float x;
- float y;
- float w;
- float h;
int halign; // horizontal alignment: 0=left, 1=center, 2=right
int valign; // vertical alignment: 0=top, 1=middle, 2=bottom
- int distort;
- struct geometry_s *next;
};
-/** Parse a value from a geometry string.
-*/
-
-static float parse_value( char **ptr, int normalisation, char delim, float defaults )
-{
- float value = defaults;
-
- if ( *ptr != NULL && **ptr != '\0' )
- {
- char *end = NULL;
- value = strtod( *ptr, &end );
- if ( end != NULL )
- {
- if ( *end == '%' )
- value = ( value / 100.0 ) * normalisation;
- while ( *end == delim || *end == '%' )
- end ++;
- }
- *ptr = end;
- }
-
- return value;
-}
-
-/** Parse a geometry property string with the syntax X,Y:WxH:MIX. Any value can be
- expressed as a percentage by appending a % after the value, otherwise values are
- assumed to be relative to the normalised dimensions of the consumer.
+/** Parse the alignment properties into the geometry.
*/
-static void geometry_parse( struct geometry_s *geometry, struct geometry_s *defaults, char *property, int nw, int nh )
+static int alignment_parse( char* align )
{
- // Assign normalised width and height
- geometry->nw = nw;
- geometry->nh = nh;
-
- // Assign from defaults if available
- if ( defaults != NULL )
- {
- geometry->x = defaults->x;
- geometry->y = defaults->y;
- geometry->w = geometry->sw = defaults->w;
- geometry->h = geometry->sh = defaults->h;
- geometry->distort = defaults->distort;
- geometry->mix = defaults->mix;
- defaults->next = geometry;
- }
- else
- {
- geometry->mix = 100;
- }
+ int ret = 0;
+
+ if ( align == NULL );
+ else if ( isdigit( align[ 0 ] ) )
+ ret = atoi( align );
+ else if ( align[ 0 ] == 'c' || align[ 0 ] == 'm' )
+ ret = 1;
+ else if ( align[ 0 ] == 'r' || align[ 0 ] == 'b' )
+ ret = 2;
- // Parse the geomtry string
- if ( property != NULL && strcmp( property, "" ) )
- {
- char *ptr = property;
- geometry->x = parse_value( &ptr, nw, ',', geometry->x );
- geometry->y = parse_value( &ptr, nh, ':', geometry->y );
- geometry->w = geometry->sw = parse_value( &ptr, nw, 'x', geometry->w );
- geometry->h = geometry->sh = parse_value( &ptr, nh, ':', geometry->h );
- if ( *ptr == '!' )
- {
- geometry->distort = 1;
- ptr ++;
- if ( *ptr == ':' )
- ptr ++;
- }
- geometry->mix = parse_value( &ptr, 100, ' ', geometry->mix );
- }
+ return ret;
}
/** Calculate real geometry.
*/
-static void geometry_calculate( struct geometry_s *output, struct geometry_s *in, float position )
+static void geometry_calculate( mlt_transition this, struct geometry_s *output, float position )
{
- // Search in for position
- struct geometry_s *out = in->next;
+ mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+ mlt_geometry geometry = mlt_properties_get_data( properties, "geometries", NULL );
+ int length = mlt_geometry_get_length( geometry );
- if ( position >= 1.0 )
+ // Allow wrapping
+ if ( position >= length && length != 0 )
{
- int section = floor( position );
- position -= section;
+ int section = position / length;
+ position -= section * length;
if ( section % 2 == 1 )
- position = 1.0 - position;
- }
-
- while ( out->next != NULL )
- {
- if ( position >= in->position && position < out->position )
- break;
-
- in = out;
- out = in->next;
+ position = length - position;
}
- position = ( position - in->position ) / ( out->position - in->position );
-
- // Calculate this frames geometry
- if ( in->frame != out->frame - 1 )
- {
- output->nw = in->nw;
- output->nh = in->nh;
- output->x = rint( in->x + ( out->x - in->x ) * position + 0.5 );
- output->y = rint( in->y + ( out->y - in->y ) * position + 0.5 );
- output->w = rint( in->w + ( out->w - in->w ) * position + 0.5 );
- output->h = rint( in->h + ( out->h - in->h ) * position + 0.5 );
- output->mix = in->mix + ( out->mix - in->mix ) * position;
- output->distort = in->distort;
- }
- else
- {
- output->nw = out->nw;
- output->nh = out->nh;
- output->x = out->x;
- output->y = out->y;
- output->w = out->w;
- output->h = out->h;
- output->mix = out->mix;
- output->distort = out->distort;
- }
+ // Fetch the key for the position
+ mlt_geometry_fetch( geometry, &output->item, position );
}
-static void transition_destroy_keys( void *arg )
-{
- struct geometry_s *ptr = arg;
- struct geometry_s *next = NULL;
-
- while ( ptr != NULL )
- {
- next = ptr->next;
- free( ptr );
- ptr = next;
- }
-}
-
-static struct geometry_s *transition_parse_keys( mlt_transition this, int normalised_width, int normalised_height )
+static mlt_geometry transition_parse_keys( mlt_transition this, int normalised_width, int normalised_height )
{
// Loop variable for property interrogation
int i = 0;
// Get the properties of the transition
mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
+ // Create an empty geometries object
+ mlt_geometry geometry = mlt_geometry_init( );
+
// Get the in and out position
mlt_position in = mlt_transition_get_in( this );
mlt_position out = mlt_transition_get_out( this );
- // Create the start
- struct geometry_s *start = calloc( 1, sizeof( struct geometry_s ) );
+ // Get the new style geometry string
+ char *property = mlt_properties_get( properties, "geometry" );
- // Create the end (we always need two entries)
- struct geometry_s *end = calloc( 1, sizeof( struct geometry_s ) );
+ // Parse the geometry if we have one
+ mlt_geometry_parse( geometry, property, out - in + 1, normalised_width, normalised_height );
- // Pointer
- struct geometry_s *ptr = start;
-
- // Check if we're using the new style geometry
- if ( mlt_properties_get( properties, "geometry" ) )
- {
- // Sundry vars
- int i;
- int frame = 0;
-
- // Obtain the geometry data - this is presented in the form:
- // x,y:WxH[!][:mix][;f=x,y:WxH[!][:mix]]*
- char *data = mlt_properties_get( properties, "geometry" );
-
- // Split the data on the ; to seperate the values
- mlt_tokeniser tokens = mlt_tokeniser_init( );
- mlt_tokeniser_parse_new( tokens, data, ";" );
-
- // Parse the first entry
- geometry_parse( start, NULL, mlt_tokeniser_get_string( tokens, 0 ), normalised_width, normalised_height );
-
- // Iterate through the remainder
- for ( i = 1; i < mlt_tokeniser_count( tokens ); i ++ )
- {
- // Get the current value
- char *value = mlt_tokeniser_get_string( tokens, i );
-
- // Used to determine the position
- float position = 0;
-
- // Determine the position of the / delimiter
- char *p = strchr( value, '=' );
-
- // Ensure that it has a frame and extract that value
- if ( p )
- {
- frame = atoi( value );
- value = p + 1;
- }
- else
- {
- fprintf( stderr, "Malformed geometry - no frame in %s (%d)\n", value, i );
- }
-
- // Determine the position
- if ( frame >= 0 && frame < ( out - in ) )
- position = ( float )frame / ( float )( out - in + 1 );
- else if ( frame < 0 && - frame < ( out - in ) )
- position = ( float )( out - in + frame ) / ( float )( out - in + 1 );
-
- // For now, we'll exclude all keys received out of order
- if ( position > ptr->position )
- {
- // Create a new geometry
- struct geometry_s *temp = calloc( 1, sizeof( struct geometry_s ) );
-
- // Parse and add to the list
- geometry_parse( temp, ptr, value, normalised_width, normalised_height );
-
- // Assign the position and frame
- temp->frame = frame;
- temp->position = position;
-
- // Allow the next to be appended after this one
- ptr = temp;
- }
- }
-
- // Close the tokens
- mlt_tokeniser_close( tokens );
-
- // Parse the end
- geometry_parse( end, ptr, NULL, normalised_width, normalised_height );
- if ( out > 0 )
- end->position = ( float )( out - in ) / ( float )( out - in + 1 );
- else
- end->position = 1;
- }
- else
+ // Check if we're using the old style geometry
+ if ( property == NULL )
{
// DEPRECATED: Multiple keys for geometry information is inefficient and too rigid for
- // practical use
+ // practical use - while deprecated, it has been slightly extended too - keys can now
+ // be specified out of order, and can be blanked or NULL to simulate removal
+
+ // Structure to use for parsing and inserting
+ struct mlt_geometry_item_s item;
// Parse the start property
- geometry_parse( start, NULL, mlt_properties_get( properties, "start" ), normalised_width, normalised_height );
+ item.frame = 0;
+ if ( mlt_geometry_parse_item( geometry, &item, mlt_properties_get( properties, "start" ) ) == 0 )
+ mlt_geometry_insert( geometry, &item );
// Parse the keys in between
for ( i = 0; i < mlt_properties_count( properties ); i ++ )
char *value = mlt_properties_get_value( properties, i );
// Determine the frame number
- int frame = atoi( name + 4 );
-
- // Determine the position
- float position = 0;
-
- if ( frame >= 0 && frame < ( out - in ) )
- position = ( float )frame / ( float )( out - in + 1 );
- else if ( frame < 0 && - frame < ( out - in ) )
- position = ( float )( out - in + frame ) / ( float )( out - in + 1 );
+ item.frame = atoi( name + 4 );
- // For now, we'll exclude all keys received out of order
- if ( position > ptr->position )
- {
- // Create a new geometry
- struct geometry_s *temp = calloc( 1, sizeof( struct geometry_s ) );
-
- // Parse and add to the list
- geometry_parse( temp, ptr, value, normalised_width, normalised_height );
-
- // Assign the position and frame
- temp->frame = frame;
- temp->position = position;
-
- // Allow the next to be appended after this one
- ptr = temp;
- }
+ // Parse and add to the list
+ if ( mlt_geometry_parse_item( geometry, &item, value ) == 0 )
+ mlt_geometry_insert( geometry, &item );
else
- {
- fprintf( stderr, "Key out of order - skipping %s\n", name );
- }
+ fprintf( stderr, "Invalid Key - skipping %s = %s\n", name, value );
}
}
// Parse the end
- geometry_parse( end, ptr, mlt_properties_get( properties, "end" ), normalised_width, normalised_height );
- if ( out > 0 )
- end->position = ( float )( out - in ) / ( float )( out - in + 1 );
- else
- end->position = 1;
+ item.frame = -1;
+ if ( mlt_geometry_parse_item( geometry, &item, mlt_properties_get( properties, "end" ) ) == 0 )
+ mlt_geometry_insert( geometry, &item );
}
- return start;
-}
-
-/** Parse the alignment properties into the geometry.
-*/
-
-static int alignment_parse( char* align )
-{
- int ret = 0;
-
- if ( align == NULL );
- else if ( isdigit( align[ 0 ] ) )
- ret = atoi( align );
- else if ( align[ 0 ] == 'c' || align[ 0 ] == 'm' )
- ret = 1;
- else if ( align[ 0 ] == 'r' || align[ 0 ] == 'b' )
- ret = 2;
-
- return ret;
+ return geometry;
}
/** Adjust position according to scaled size and alignment properties.
static void alignment_calculate( struct geometry_s *geometry )
{
- geometry->x += ( geometry->w - geometry->sw ) * geometry->halign / 2;
- geometry->y += ( geometry->h - geometry->sh ) * geometry->valign / 2;
+ geometry->item.x += ( geometry->item.w - geometry->sw ) * geometry->halign / 2;
+ geometry->item.y += ( geometry->item.h - geometry->sh ) * geometry->valign / 2;
}
/** Calculate the position for this frame.
*/
-static float position_calculate( mlt_transition this, mlt_position position )
+static int position_calculate( mlt_transition this, mlt_position position )
{
// Get the in and out position
mlt_position in = mlt_transition_get_in( this );
- mlt_position out = mlt_transition_get_out( this );
// Now do the calcs
- return ( float )( position - in ) / ( float )( out - in + 1 );
+ return position - in;
}
/** Calculate the field delta for this frame - position between two frames.
// Get the in and out position
mlt_position in = mlt_transition_get_in( this );
mlt_position out = mlt_transition_get_out( this );
+ float length = out - in + 1;
// Get the position of the frame
char *name = mlt_properties_get( MLT_TRANSITION_PROPERTIES( this ), "_unique_id" );
mlt_position position = mlt_properties_get_position( MLT_FRAME_PROPERTIES( frame ), name );
// Now do the calcs
- float x = ( float )( position - in ) / ( float )( out - in + 1 );
- float y = ( float )( position + 1 - in ) / ( float )( out - in + 1 );
+ float x = ( float )( position - in ) / length;
+ float y = ( float )( position + 1 - in ) / length;
- return ( y - x ) / 2.0;
+ return length * ( y - x ) / 2.0;
}
static int get_value( mlt_properties properties, char *preferred, char *fallback )
int ret = 0;
int i;
int x_src = 0, y_src = 0;
- int32_t weight = ( 1 << 16 ) * ( geometry.mix / 100 );
+ int32_t weight = ( 1 << 16 ) * ( geometry.item.mix / 100 );
int step = ( field > -1 ) ? 2 : 1;
int bpp = 2;
int stride_src = width_src * bpp;
int stride_dest = width_dest * bpp;
// Adjust to consumer scale
- int x = geometry.x * width_dest / geometry.nw;
- int y = geometry.y * height_dest / geometry.nh;
- int uneven = ( x & 1 );
+ int x = rint( 0.5 + geometry.item.x * width_dest / geometry.nw );
+ int y = rint( 0.5 + geometry.item.y * height_dest / geometry.nh );
+ int x_uneven = x & 1;
// optimization points - no work to do
if ( width_src <= 0 || height_src <= 0 )
stride_dest *= step;
int alpha_stride = stride_src / bpp;
- if ( uneven )
+ // Make sure than x and w are even
+ if ( x_uneven )
{
- p_dest += 2;
+ p_src += 2;
width_src --;
}
mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
- if ( mlt_properties_get( properties, "distort" ) == NULL && mlt_properties_get( b_props, "distort" ) == NULL && geometry->distort == 0 )
+ if ( mlt_properties_get( properties, "distort" ) == NULL && mlt_properties_get( b_props, "distort" ) == NULL && geometry->item.distort == 0 )
{
// Adjust b_frame pixel aspect
- int normalised_width = geometry->w;
- int normalised_height = geometry->h;
+ int normalised_width = geometry->item.w;
+ int normalised_height = geometry->item.h;
int real_width = get_value( b_props, "real_width", "width" );
int real_height = get_value( b_props, "real_height", "height" );
double input_ar = mlt_frame_get_aspect_ratio( b_frame );
// TODO: Should combine fill/distort in one property
if ( mlt_properties_get( properties, "fill" ) != NULL )
{
- scaled_width = ( geometry->w / scaled_width ) * scaled_width;
- scaled_height = ( geometry->h / scaled_height ) * scaled_height;
+ scaled_width = ( geometry->item.w / scaled_width ) * scaled_width;
+ scaled_height = ( geometry->item.h / scaled_height ) * scaled_height;
}
// Save the new scaled dimensions
}
else
{
- geometry->sw = geometry->w;
- geometry->sh = geometry->h;
+ geometry->sw = geometry->item.w;
+ geometry->sh = geometry->item.h;
}
// We want to ensure that we bypass resize now...
alignment_calculate( geometry );
// Adjust to consumer scale
- int x = geometry->x * *width / geometry->nw;
- int y = geometry->y * *height / geometry->nh;
+ int x = geometry->item.x * *width / geometry->nw;
+ int y = geometry->item.y * *height / geometry->nh;
*width = geometry->sw * *width / geometry->nw;
*height = geometry->sh * *height / geometry->nh;
}
-static struct geometry_s *composite_calculate( struct geometry_s *result, mlt_transition this, mlt_frame a_frame, float position )
+static mlt_geometry composite_calculate( mlt_transition this, struct geometry_s *result, mlt_frame a_frame, float position )
{
// Get the properties from the transition
mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
mlt_properties a_props = MLT_FRAME_PROPERTIES( a_frame );
// Structures for geometry
- struct geometry_s *start = mlt_properties_get_data( properties, "geometries", NULL );
+ mlt_geometry start = mlt_properties_get_data( properties, "geometries", NULL );
// Obtain the normalised width and height from the a_frame
int normalised_width = mlt_properties_get_int( a_props, "normalised_width" );
int normalised_height = mlt_properties_get_int( a_props, "normalised_height" );
// Now parse the geometries
- if ( start == NULL || mlt_properties_get_int( properties, "refresh" ) || start->nw != normalised_width || start->nh != normalised_height )
+ if ( start == NULL )
{
// Parse the transitions properties
start = transition_parse_keys( this, normalised_width, normalised_height );
// Assign to properties to ensure we get destroyed
- mlt_properties_set_data( properties, "geometries", start, 0, transition_destroy_keys, NULL );
- mlt_properties_set_int( properties, "refresh", 0 );
+ mlt_properties_set_data( properties, "geometries", start, 0, ( mlt_destructor )mlt_geometry_close, NULL );
+ }
+ else
+ {
+ int length = mlt_transition_get_out( this ) - mlt_transition_get_in( this ) + 1;
+ mlt_geometry_refresh( start, mlt_properties_get( properties, "geometry" ), length, normalised_width, normalised_height );
}
// Do the calculation
- geometry_calculate( result, start, position );
+ geometry_calculate( this, result, position );
+
+ // Assign normalised info
+ result->nw = normalised_width;
+ result->nh = normalised_height;
// Now parse the alignment
result->halign = alignment_parse( mlt_properties_get( properties, "halign" ) );
mlt_properties b_props = MLT_FRAME_PROPERTIES( b_frame );
// Get the position
- float position = position_calculate( this, frame_position );
+ int position = position_calculate( this, frame_position );
// Destination image
uint8_t *dest = NULL;
// Pointers for copy operation
uint8_t *p;
- uint8_t *q;
- uint8_t *r;
- // Corrdinates
+ // Coordinates
int w = 0;
int h = 0;
int x = 0;
int y = 0;
+ int ss = 0;
+ int ds = 0;
+
// Will need to know region to copy
struct geometry_s result;
+ float delta = delta_calculate( this, a_frame );
+
// Calculate the region now
- composite_calculate( &result, this, a_frame, position );
+ composite_calculate( this, &result, a_frame, position + delta / 2 );
// Need to scale down to actual dimensions
- x = result.x * width / result.nw ;
- y = result.y * height / result.nh;
- w = result.w * width / result.nw;
- h = result.h * height / result.nh;
+ x = rint( 0.5 + result.item.x * width / result.nw );
+ y = rint( 0.5 + result.item.y * height / result.nh );
+ w = rint( 0.5 + result.item.w * width / result.nw );
+ h = rint( 0.5 + result.item.h * height / result.nh );
+
+ // Make sure that x and w are even
+ if ( x & 1 )
+ {
+ x --;
+ w += 2;
+ if ( w & 1 )
+ w --;
+ }
+ else if ( w & 1 )
+ {
+ w ++;
+ }
+
+ ds = w * 2;
+ ss = width * 2;
+
+ // Now we need to create a new destination image
+ dest = mlt_pool_alloc( w * h * 2 );
+
+ // Assign to the new frame
+ mlt_properties_set_data( b_props, "image", dest, w * h * 2, mlt_pool_release, NULL );
+ mlt_properties_set_int( b_props, "width", w );
+ mlt_properties_set_int( b_props, "height", h );
if ( y < 0 )
{
- h = h + y;
+ dest += ( ds * -y );
+ h += y;
y = 0;
}
if ( y + h > height )
- h = height - y;
-
- x = ( x | 1 ) ^ 1;
- w = ( w | 1 ) ^ 1;
+ h -= ( y + h - height );
- // Now we need to create a new destination image
- dest = mlt_pool_alloc( w * h * 2 );
+ if ( x < 0 )
+ {
+ dest += -x * 2;
+ w += x;
+ x = 0;
+ }
// Copy the region of the image
- p = image + y * width * 2 + x * 2;
- q = dest;
- r = dest + w * h * 2;
+ p = image + y * ss + x * 2;
- while ( q < r )
+ while ( h -- )
{
- inline_memcpy( q, p, w * 2 );
- q += w * 2;
- p += width * 2;
+ inline_memcpy( dest, p, w * 2 );
+ dest += ds;
+ p += ss;
}
- // Assign to the new frame
- mlt_properties_set_data( b_props, "image", dest, w * h * 2, mlt_pool_release, NULL );
- mlt_properties_set_int( b_props, "width", w );
- mlt_properties_set_int( b_props, "height", h );
-
// Assign this position to the b frame
mlt_frame_set_position( b_frame, frame_position );
mlt_properties_set( b_props, "distort", "true" );
float position = mlt_properties_get_double( b_props, "relative_position" );
float delta = delta_calculate( this, a_frame );
- // Do the calculation
- struct geometry_s *start = composite_calculate( &result, this, a_frame, position );
-
// Get the image from the b frame
uint8_t *image_b = NULL;
int width_b = *width;
int height_b = *height;
+ // Do the calculation
+ composite_calculate( this, &result, a_frame, position );
+
// Optimisation - no compositing required
- if ( result.mix == 0 || ( result.w == 0 && result.h == 0 ) )
+ if ( result.item.mix == 0 || ( result.item.w == 0 && result.item.h == 0 ) )
return 0;
// Need to keep the width/height of the a_frame on the b_frame for titling
float field_position = position + field * delta;
// Do the calculation if we need to
- geometry_calculate( &result, start, field_position );
+ composite_calculate( this, &result, a_frame, field_position );
if ( mlt_properties_get_int( properties, "titles" ) )
{
- result.nw = result.w = *width;
- result.nh = result.h = *height;
+ result.nw = result.item.w = *width;
+ result.nh = result.item.h = *height;
result.sw = width_b;
result.sh = height_b;
}