From fce425d1301bdc2876d5cd96e4960aeeb5952147 Mon Sep 17 00:00:00 2001 From: Dan Dennedy Date: Thu, 16 May 2013 22:38:16 -0700 Subject: [PATCH] Add support for discrete animation including strings. --- src/framework/mlt_animation.c | 58 ++++++-- src/framework/mlt_animation.h | 7 +- src/framework/mlt_property.c | 28 +++- src/tests/test_properties/test_properties.cpp | 129 ++++++++++++++++++ 4 files changed, 207 insertions(+), 15 deletions(-) diff --git a/src/framework/mlt_animation.c b/src/framework/mlt_animation.c index 195b1af2..449ee2fe 100644 --- a/src/framework/mlt_animation.c +++ b/src/framework/mlt_animation.c @@ -68,11 +68,18 @@ void mlt_animation_interpolate( mlt_animation self ) if ( !prev ) current->item.is_key = 1; - mlt_property_interpolate( current->item.property, - prev->item.property, next->item.property, - current->item.frame - prev->item.frame, - next->item.frame - prev->item.frame, - self->fps, self->locale ); + if ( current->item.keyframe_type == mlt_keyframe_discrete ) + { + mlt_property_pass( current->item.property, prev->item.property ); + } + else + { + mlt_property_interpolate( current->item.property, + prev->item.property, next->item.property, + current->item.frame - prev->item.frame, + next->item.frame - prev->item.frame, + self->fps, self->locale ); + } } // Move to the next item @@ -219,9 +226,14 @@ int mlt_animation_parse_item( mlt_animation self, mlt_animation_item item, const mlt_property_set_string( item->property, value ); item->frame = mlt_property_get_int( item->property, self->fps, self->locale ); } - value = strchr( value, '=' ) + 1; - // TODO the character preceeding the equal sign indicates method of interpolation. + // The character preceeding the equal sign indicates interpolation method. + p = strchr( value, '=' ) - 1; + if ( p[0] == '|' ) + item->keyframe_type = mlt_keyframe_discrete; + else + item->keyframe_type = mlt_keyframe_linear; + value = &p[2]; } // Special case - frame < 0 @@ -257,6 +269,8 @@ int mlt_animation_get_item( mlt_animation self, mlt_animation_item item, int pos if ( node ) { + item->keyframe_type = node->item.keyframe_type; + // Position is before the first keyframe. if ( position < node->item.frame ) { @@ -279,9 +293,16 @@ int mlt_animation_get_item( mlt_animation self, mlt_animation_item item, int pos else { item->is_key = 0; - mlt_property_interpolate( item->property, node->item.property, node->next->item.property, - position - node->item.frame, node->next->item.frame - node->item.frame, - self->fps, self->locale ); + if ( node->item.keyframe_type == mlt_keyframe_discrete ) + { + mlt_property_pass( item->property, node->item.property ); + } + else + { + mlt_property_interpolate( item->property, node->item.property, node->next->item.property, + position - node->item.frame, node->next->item.frame - node->item.frame, + self->fps, self->locale ); + } } } else @@ -301,6 +322,7 @@ int mlt_animation_insert( mlt_animation self, mlt_animation_item item ) animation_node node = calloc( 1, sizeof( *node ) ); node->item.frame = item->frame; node->item.is_key = 1; + node->item.keyframe_type = item->keyframe_type; node->item.property = mlt_property_init(); mlt_property_pass( node->item.property, item->property ); @@ -337,6 +359,7 @@ int mlt_animation_insert( mlt_animation self, mlt_animation_item item ) // Update matching node. current->item.frame = item->frame; current->item.is_key = 1; + current->item.keyframe_type = item->keyframe_type; mlt_property_close( current->item.property ); current->item.property = node->item.property; free( node ); @@ -378,6 +401,7 @@ int mlt_animation_next_key( mlt_animation self, mlt_animation_item item, int pos { item->frame = node->item.frame; item->is_key = node->item.is_key; + item->keyframe_type = node->item.keyframe_type; mlt_property_pass( item->property, node->item.property ); } @@ -396,6 +420,7 @@ int mlt_animation_prev_key( mlt_animation self, mlt_animation_item item, int pos { item->frame = node->item.frame; item->is_key = node->item.is_key; + item->keyframe_type = node->item.keyframe_type; mlt_property_pass( item->property, node->item.property ); } @@ -481,7 +506,18 @@ char *mlt_animation_serialize_cut( mlt_animation self, int in, int out ) { // Append keyframe time and keyframe/value delimiter (=). if ( item.frame - in != 0 ) - sprintf( ret + used, "%d=", item.frame - in ); + { + const char *s; + switch (item.keyframe_type) { + case mlt_keyframe_discrete: + s = "|"; + break; + default: + s = ""; + break; + } + sprintf( ret + used, "%d%s=", item.frame - in, s ); + } // Append item value. if ( item.is_key ) strcat( ret, mlt_property_get_string_l( item.property, self->locale ) ); diff --git a/src/framework/mlt_animation.h b/src/framework/mlt_animation.h index 313e193b..a0bb0629 100644 --- a/src/framework/mlt_animation.h +++ b/src/framework/mlt_animation.h @@ -25,12 +25,17 @@ #include "mlt_types.h" #include "mlt_property.h" +typedef enum { + mlt_keyframe_discrete, + mlt_keyframe_linear +} mlt_keyframe_type; + struct mlt_animation_item_s { - int is_key; /**< = whether this is a key frame or an interpolated item */ int frame; /**< The actual frame this corresponds to */ mlt_property property; + mlt_keyframe_type keyframe_type; }; typedef struct mlt_animation_item_s *mlt_animation_item; diff --git a/src/framework/mlt_property.c b/src/framework/mlt_property.c index da41726e..21c6e08c 100644 --- a/src/framework/mlt_property.c +++ b/src/framework/mlt_property.c @@ -925,6 +925,29 @@ char *mlt_property_get_time( mlt_property self, mlt_time_format format, double f return self->prop_string; } +static int is_property_numeric( mlt_property self, locale_t locale ) +{ + int result = ( self->types & mlt_prop_int ) || + ( self->types & mlt_prop_int64 ) || + ( self->types & mlt_prop_double ) || + ( self->types & mlt_prop_position ); + + // If not already numeric but string is numeric. + if ( ( !result && self->types & mlt_prop_string ) && self->prop_string ) + { + double temp; + char *p = NULL; +#if defined(__GLIBC__) || defined(__DARWIN__) + if ( locale ) + temp = strtod_l( self->prop_string, &p, locale ); + else +#endif + temp = strtod( self->prop_string, &p ); + result = ( p != self->prop_string ); + } + return result; +} + static inline double linearstep( double start, double end, double position, int length ) { double o = ( end - start ) / length; @@ -935,8 +958,7 @@ int mlt_property_interpolate(mlt_property self, mlt_property previous, mlt_prope double position, int length, double fps, locale_t locale ) { int error = 0; - - if ( fps > 0 ) + if ( fps > 0 && is_property_numeric( previous, locale ) && is_property_numeric( next, locale ) ) { double start = previous? mlt_property_get_double( previous, fps, locale ) : 0; double end = next? mlt_property_get_double( next, fps, locale ) : 0; @@ -945,7 +967,7 @@ int mlt_property_interpolate(mlt_property self, mlt_property previous, mlt_prope } else { - error = 1; + mlt_property_pass( self, previous ); } return error; } diff --git a/src/tests/test_properties/test_properties.cpp b/src/tests/test_properties/test_properties.cpp index 35a01cdb..b8eb494c 100644 --- a/src/tests/test_properties/test_properties.cpp +++ b/src/tests/test_properties/test_properties.cpp @@ -352,6 +352,47 @@ private Q_SLOTS: mlt_animation_close(a); } + void IntAnimation() + { + locale_t locale; +#if defined(__linux__) || defined(__DARWIN__) + locale = newlocale( LC_NUMERIC_MASK, "POSIX", NULL ); +#endif + double fps = 25.0; + mlt_animation a = mlt_animation_new(); + struct mlt_animation_item_s item; + + mlt_animation_parse(a, "50=100; 60=60; 100=0", 100, fps, locale); + mlt_animation_remove(a, 60); + char *a_serialized = mlt_animation_serialize(a); + QCOMPARE(a_serialized, "50=100;100=0"); + if (a_serialized) free(a_serialized); + item.property = mlt_property_init(); + + mlt_animation_get_item(a, &item, 10); + QCOMPARE(mlt_property_get_int(item.property, fps, locale), 100); + QCOMPARE(item.is_key, 0); + + mlt_animation_get_item(a, &item, 50); + QCOMPARE(mlt_property_get_int(item.property, fps, locale), 100); + QCOMPARE(item.is_key, 1); + + mlt_animation_get_item(a, &item, 75); + QCOMPARE(mlt_property_get_int(item.property, fps, locale), 50); + QCOMPARE(item.is_key, 0); + + mlt_animation_get_item(a, &item, 100); + QCOMPARE(mlt_property_get_int(item.property, fps, locale), 0); + QCOMPARE(item.is_key, 1); + + mlt_animation_get_item(a, &item, 110); + QCOMPARE(mlt_property_get_int(item.property, fps, locale), 0); + QCOMPARE(item.is_key, 0); + + mlt_property_close(item.property); + mlt_animation_close(a); + } + void AnimationWithTimeValueKeyframes() { locale_t locale; @@ -392,6 +433,94 @@ private Q_SLOTS: mlt_property_close(item.property); mlt_animation_close(a); } + + void DiscreteIntAnimation() + { + locale_t locale; +#if defined(__linux__) || defined(__DARWIN__) + locale = newlocale( LC_NUMERIC_MASK, "POSIX", NULL ); +#endif + double fps = 25.0; + mlt_animation a = mlt_animation_new(); + struct mlt_animation_item_s item; + + mlt_animation_parse(a, "50|=100; 60|=60; 100|=0", 100, fps, locale); + char *a_serialized = mlt_animation_serialize(a); + QCOMPARE(a_serialized, "50|=100;60|=60;100|=0"); + if (a_serialized) free(a_serialized); + item.property = mlt_property_init(); + + mlt_animation_get_item(a, &item, 10); + QCOMPARE(mlt_property_get_int(item.property, fps, locale), 100); + QCOMPARE(item.is_key, 0); + + mlt_animation_get_item(a, &item, 50); + QCOMPARE(mlt_property_get_int(item.property, fps, locale), 100); + QCOMPARE(item.is_key, 1); + + mlt_animation_get_item(a, &item, 55); + QCOMPARE(mlt_property_get_int(item.property, fps, locale), 100); + QCOMPARE(item.is_key, 0); + + mlt_animation_get_item(a, &item, 60); + QCOMPARE(mlt_property_get_int(item.property, fps, locale), 60); + QCOMPARE(item.is_key, 1); + + mlt_animation_get_item(a, &item, 75); + QCOMPARE(mlt_property_get_int(item.property, fps, locale), 60); + QCOMPARE(item.is_key, 0); + + mlt_animation_get_item(a, &item, 100); + QCOMPARE(mlt_property_get_int(item.property, fps, locale), 0); + QCOMPARE(item.is_key, 1); + + mlt_animation_get_item(a, &item, 110); + QCOMPARE(mlt_property_get_int(item.property, fps, locale), 0); + QCOMPARE(item.is_key, 0); + + mlt_property_close(item.property); + mlt_animation_close(a); + } + + void StringAnimation() + { + locale_t locale; +#if defined(__linux__) || defined(__DARWIN__) + locale = newlocale( LC_NUMERIC_MASK, "POSIX", NULL ); +#endif + double fps = 25.0; + mlt_animation a = mlt_animation_new(); + struct mlt_animation_item_s item; + + mlt_animation_parse(a, "50=hello world; 60=\"good night\"; 100=bar", 100, fps, locale); + char *a_serialized = mlt_animation_serialize(a); + QCOMPARE(a_serialized, "50=hello world;60=\"good night\";100=bar"); + if (a_serialized) free(a_serialized); + item.property = mlt_property_init(); + + mlt_animation_get_item(a, &item, 10); + QCOMPARE(mlt_property_get_string(item.property), "hello world"); + QCOMPARE(item.is_key, 0); + + mlt_animation_get_item(a, &item, 50); + QCOMPARE(mlt_property_get_string(item.property), "hello world"); + QCOMPARE(item.is_key, 1); + + mlt_animation_get_item(a, &item, 75); + QCOMPARE(mlt_property_get_string(item.property), "\"good night\""); + QCOMPARE(item.is_key, 0); + + mlt_animation_get_item(a, &item, 100); + QCOMPARE(mlt_property_get_string(item.property), "bar"); + QCOMPARE(item.is_key, 1); + + mlt_animation_get_item(a, &item, 110); + QCOMPARE(mlt_property_get_string(item.property), "bar"); + QCOMPARE(item.is_key, 0); + + mlt_property_close(item.property); + mlt_animation_close(a); + } }; QTEST_APPLESS_MAIN(TestProperties) -- 2.39.2