From: kramm <kramm>
Date: Tue, 7 Aug 2007 17:18:55 +0000 (+0000)
Subject: Huub Schaek's interpolation patch
X-Git-Tag: buttons-working~598
X-Git-Url: http://git.asbjorn.it/?a=commitdiff_plain;h=8f9567dee2a2bbcd105d8a32538e660e1a01870e;p=swftools.git

Huub Schaek's interpolation patch
---

diff --git a/src/swfc-history.c b/src/swfc-history.c
index 40f694c..1a8b609 100644
--- a/src/swfc-history.c
+++ b/src/swfc-history.c
@@ -48,12 +48,65 @@ void change_init(change_t* change)
 
 void change_append(change_t* first, change_t* newChange)
 {
+    change_t* previous = 0;
+    change_t* start = first;
+    float p0, p1, m0, m1;
+
 	while (first->next)
+    {
+    	previous = first;
 		first = first->next;
+    }
 	first->next = newChange;
+    if (first->function == CF_QCHANGE)
+    {
+    	p0 = previous->value;
+    	p1 = first->value;
+    	if (previous->function == CF_QCHANGE)
+    	    m0 = (3 * previous->spline.a + 2 * previous->spline.b + previous->spline.c);
+    	else
+    	    if (previous->function == CF_CHANGE)
+    	    	m0 = (change_value(start, previous->frame) - change_value(start, previous->frame - 1)) * (first->frame - previous->frame);
+    	    else
+    		m0 = (first->value - previous->value);
+    	if (newChange->function == CF_QCHANGE)
+    	    m1 = 0.5 * (newChange->value - previous->value);
+    	else
+    	    if (newChange->function == CF_CHANGE)
+    		m1 = (change_value(previous, first->frame + 1) - change_value(previous, first->frame)) * (first->frame - previous->frame);
+    	    else
+  		m1 = (first->value - previous->value);
+    	first->spline.a = 2 * p0 + m0 - 2 * p1 + m1;
+    	first->spline.b = -3 * p0 - 2 * m0 + 3 * p1 - m1;
+    	first->spline.c = m0;
+    	first->spline.d = p0;
+    }
+    if (newChange->function == CF_QCHANGE)
+    {
+    	p0 = first->value;
+    	p1 = newChange->value;
+    	if (first->function == CF_QCHANGE)
+    	    m0 = m1;
+    	else
+    	    if (first->function == CF_CHANGE)
+    	    	m0 = (change_value(start, first->frame) - change_value(start, first->frame - 1)) * (first->frame - previous->frame);
+    	    else
+	    	m0 = (newChange->value - first->value);
+    	m1 = (newChange->value - first->value);
+    	newChange->spline.a = 2 * p0 + m0 - 2 * p1 + m1;
+    	newChange->spline.b = -3 * p0 - 2 * m0 + 3 * p1 - m1;
+    	newChange->spline.c = m0;
+    	newChange->spline.d = p0;
+    }
 }
 
-float interpolateParameter(float p1, float p2, float fraction, interpolation_t* inter)
+float calculateSpline(change_t* modification, float fraction)
+{
+    spline_t s = modification->spline;
+    return (((s.a * fraction) + s.b) * fraction + s.c) * fraction + s.d;
+}
+
+float interpolateScalar(float p1, float p2, float fraction, interpolation_t* inter)
 {
 	if (!inter)
 		return linear(fraction, p1, p2 - p1);
@@ -97,35 +150,54 @@ float interpolateParameter(float p1, float p2, float fraction, interpolation_t*
 	}
 }
 
-float change_value(change_t* first, U16 frame)
+int change_differs(change_t* modification, U16 frame)
 {
-	change_t* previous = first;
-	while (first && first->frame < frame)
+    change_t* previous = modification;
+    while (modification && modification->frame < frame)
 	{
-		previous = first;
-		first = first->next;
+	previous = modification;
+	modification = modification->next;
+    }
+    if (!modification)
+	return 0;
+    if (modification->frame == frame)
+    	return 1;
+    return (modification->function != CF_JUMP);
 	}
-	if (!first)
+
+float change_value(change_t* modification, U16 frame)
+{
+    change_t* previous = modification;
+    while (modification && modification->frame < frame)
+    {
+	previous = modification;
+	modification = modification->next;
+    }
+    if (!modification)
 		return previous->value;
-	if (first->frame == frame)
+    if (modification->frame == frame)
 	{
-		float result;
 		do 
 		{
-			result = first->value;
-			first = first->next;
+	    previous = modification;
+	    modification = modification->next;
 		}
-		while (first && first->frame == frame);
-		return result;
+	while (modification && modification->frame == frame);
+	return previous->value;
 	}
-	switch (first->function)
+    switch (modification->function)
 	{
 		case CF_PUT:
-			return first->value;
+	    return modification->value;
 		case CF_CHANGE:
 		{
-			float fraction = (frame - previous->frame) / (float)(first->frame - previous->frame);
-			return interpolateParameter(previous->value, first->value, fraction, first->interpolation);
+	    float fraction = (frame - previous->frame) / (float)(modification->frame - previous->frame);
+	    return interpolateScalar(previous->value, modification->value, fraction, modification->interpolation);
+	}
+	case CF_QCHANGE:
+	{
+	    float fraction = (frame - previous->frame) / (float)(modification->frame - previous->frame);
+	    return calculateSpline(modification, fraction);
 		}
 		case CF_JUMP:
 			return previous->value;
@@ -167,61 +239,195 @@ void changeFilter_append(changeFilter_t* first, changeFilter_t* newChange)
 RGBA interpolateColor(RGBA c1, RGBA c2, float ratio, interpolation_t* inter)
 {
     RGBA c;
-    c.r = c1.r * (1-ratio) + c2.r * ratio;
-    c.g = c1.g * (1-ratio) + c2.g * ratio;
-    c.b = c1.b * (1-ratio) + c2.b * ratio;
-    c.a = c1.a * (1-ratio) + c2.a * ratio;
+    c.r = interpolateScalar(c1.r, c2.r, ratio, inter);
+    c.g = interpolateScalar(c1.g, c2.g, ratio, inter);
+    c.b = interpolateScalar(c1.b, c2.b, ratio, inter);
+    c.a = interpolateScalar(c1.a, c2.a, ratio, inter);
     return c;
 }
 
-FILTER* interpolateFilter(FILTER* filter1,FILTER* filter2, float ratio, interpolation_t* inter)
+GRADIENT* interpolateNodes(GRADIENT* g1, GRADIENT* g2, float fraction, interpolation_t* inter)
 {
-	if(!filter1 && !filter2)
-		return 0;
-	if(!filter1)
-		return interpolateFilter(filter2,filter1,1-ratio, inter);
+    if (g1->num != g2->num)
+    	syntaxerror("Internal error: gradients are not equal in size");
 
-	if(filter2 && filter2->type != filter1->type)
-		syntaxerror("can't interpolate between %s and %s filters yet", filtername[filter1->type], filtername[filter2->type]);
+    int i;
+    GRADIENT* g = (GRADIENT*) malloc(sizeof(GRADIENT));
+    g->ratios = rfx_calloc(16*sizeof(U8));
+    g->rgba = rfx_calloc(16*sizeof(RGBA));
+    g->num = g1->num;
+    for (i = 0; i < g->num; i++)
+    {
+    	g->ratios[i] = interpolateScalar(g1->ratios[i], g2->ratios[i], fraction, inter);
+    	g->rgba[i] = interpolateColor(g1->rgba[i], g2->rgba[i], fraction, inter);
+    }
+    return g;
+}
+
+void copyGradient(GRADIENT* dest, GRADIENT* source)
+{
+    dest->num = source->num;
+    memcpy(dest->ratios, source->ratios, source->num * sizeof(U8));
+    memcpy(dest->rgba, source->rgba, source->num * sizeof(RGBA));
+}
+
+void insertNode(GRADIENT* g, int pos)
+{
+    memmove(&g->ratios[pos + 1], &g->ratios[pos], (g->num - pos) * sizeof(U8));
+    memmove(&g->rgba[pos + 1], &g->rgba[pos], (g->num - pos) * sizeof(RGBA));
+    if (pos == 0)
+    {
+    	g->ratios[0] = g->ratios[1] / 2;
+    	g->rgba[0] = g->rgba[1];
+    }
+    else
+    	if (pos == g->num)
+    	{
+    	    g->ratios[pos] = (255 + g->ratios[g->num - 1]) / 2;
+    	    g->rgba[pos] = g->rgba[pos - 1];
+    	}
+    	else
+    	{
+	    g->ratios[pos] = (g->ratios[pos - 1] + g->ratios[pos + 1]) / 2;
+	    g->rgba[pos] = interpolateColor(g->rgba[pos - 1], g->rgba[pos + 1], 0.5, 0);
+    	}
+    g->num++;
+}
+
+void insertOptimalNode(GRADIENT* g)
+{
+    int i, next_gap;
+    int pos = 0;
+    int gap = g->ratios[0];
+    for (i = 0; i < g->num - 1; i++)
+    {
+    	next_gap = g->ratios[i + 1] - g->ratios[i];
+    	if (next_gap > gap)
+    	{
+    	    gap = next_gap;
+    	    pos = i + 1;
+    	}
+    }
+    next_gap = 255 - g->ratios[g->num -1];
+    if (next_gap > gap)
+    	pos = g->num;
+    insertNode(g, pos);
+}
    
-	if(filter1->type == FILTERTYPE_BLUR)
+void growGradient(GRADIENT* start, int size)
+{
+    while (start->num < size)
+    	insertOptimalNode(start);
+}
+
+GRADIENT* interpolateGradient(GRADIENT* g1, GRADIENT* g2, float fraction, interpolation_t* inter)
+{
+    int i;
+    GRADIENT g;
+    g.ratios = rfx_calloc(16*sizeof(U8));
+    g.rgba = rfx_calloc(16*sizeof(RGBA));
+
+    if (g1->num > g2->num)
+    {
+    	copyGradient(&g, g2);
+    	growGradient(&g, g1->num);
+    	GRADIENT* result = interpolateNodes(g1, &g, fraction, inter);
+    	rfx_free(g.rgba);
+    	rfx_free(g.ratios);
+    	return result;
+    }
+    else
+    	if (g1->num < g2->num)
+    	{
+    	    copyGradient(&g, g1);
+    	    growGradient(&g, g2->num);
+    	    GRADIENT* result = interpolateNodes(&g, g2, fraction, inter);
+    	    rfx_free(g.rgba);
+    	    rfx_free(g.ratios);
+    	    return result;
+    	}
+    	else
+    	    return interpolateNodes(g1, g2, fraction, inter);
+}
+
+FILTER* interpolateBlur(FILTER* filter1, FILTER* filter2, float ratio, interpolation_t* inter)
 	{
 		FILTER_BLUR*f1 = (FILTER_BLUR*)filter1;
 		FILTER_BLUR*f2 = (FILTER_BLUR*)filter2;
-		if(f2 && f1->blurx == f2->blurx && f1->blury == f2->blury)
+    if (!f1)
+    	f1 = noBlur;
+    if (!f2)
+    	f2 = noBlur;
+    if(f1->blurx == f2->blurx && f1->blury == f2->blury)
 			return 0;
 		FILTER_BLUR*f = (FILTER_BLUR*)swf_NewFilter(FILTERTYPE_BLUR);
-		f->blurx= (f1->blurx)*(1-ratio) + (f2?f2->blurx:0)*ratio;
-		f->blury= (f1->blury)*(1-ratio) + (f2?f2->blury:0)*ratio;
-		f->passes= (f1->passes)*(1-ratio) + (f2?f2->passes:0)*ratio;
+    f->blurx= interpolateScalar(f1->blurx, (f2->blurx), ratio, inter);
+    f->blury= interpolateScalar(f1->blury, (f2->blury), ratio, inter);
+    f->passes= interpolateScalar(f1->passes, (f2->passes), ratio, inter);
 		return (FILTER*)f;
 		}
-	else
-		if (filter1->type == FILTERTYPE_DROPSHADOW)
+
+void matchDropshadowFlags(FILTER_DROPSHADOW* unset, FILTER_DROPSHADOW* target)
+{
+    unset->innershadow = target->innershadow;
+    unset->knockout = target->knockout;
+    unset->composite = target->composite;
+}
+
+FILTER* interpolateDropshadow(FILTER* filter1,FILTER* filter2, float ratio, interpolation_t* inter)
 		{
 			FILTER_DROPSHADOW*f1 = (FILTER_DROPSHADOW*)filter1;
 			FILTER_DROPSHADOW*f2 = (FILTER_DROPSHADOW*)filter2;
-			if(f2 && !memcmp(&f1->color,&f2->color,sizeof(RGBA)) && f1->strength == f2->strength && 
+    if (!f1)
+    	f1 = noDropshadow;
+    if (!f2)
+    	f2 = noDropshadow;
+    if(!memcmp(&f1->color,&f2->color,sizeof(RGBA)) && f1->strength == f2->strength &&
 				f1->blurx == f2->blurx && f1->blury == f2->blury && 
 				f1->angle == f2->angle && f1->distance == f2->distance)
 				return 0;
 			FILTER_DROPSHADOW*f = (FILTER_DROPSHADOW*)swf_NewFilter(FILTERTYPE_DROPSHADOW);
 			memcpy(f, f1, sizeof(FILTER_DROPSHADOW));
 			f->color = interpolateColor(f1->color, f2->color, ratio, inter);
-			f->blurx= (f1->blurx)*(1-ratio) + (f2?f2->blurx:0)*ratio;
-			f->blury= (f1->blury)*(1-ratio) + (f2?f2->blury:0)*ratio;
-			f->passes= (f1->passes)*(1-ratio) + (f2?f2->passes:0)*ratio;
-			f->angle= (f1->angle)*(1-ratio) + (f2?f2->angle:0)*ratio;
-			f->distance= (f1->distance)*(1-ratio) + (f2?f2->distance:0)*ratio;
-			f->strength= (f1->strength)*(1-ratio) + (f2?f2->strength:0)*ratio;
-			return (FILTER*)f;
+    f->blurx= interpolateScalar(f1->blurx, (f2->blurx), ratio, inter);
+    f->blury= interpolateScalar(f1->blury, (f2->blury), ratio, inter);
+    f->passes= interpolateScalar(f1->passes, (f2->passes), ratio, inter);
+    f->angle= interpolateScalar(f1->angle, (f2->angle), ratio, inter);
+    f->distance= interpolateScalar(f1->distance, (f2->distance), ratio, inter);
+    f->strength= interpolateScalar(f1->strength, (f2->strength), ratio, inter);
+    if (f1 == noDropshadow)
+    {
+    	if (f2 != noDropshadow)
+    	    matchDropshadowFlags(f, f2);
 		}
 		else
-			if (filter1->type == FILTERTYPE_BEVEL)
+    	if (f2 == noDropshadow)
+	    matchDropshadowFlags(f, f1);
+	else
+	    if (ratio > 0.5)
+    		matchDropshadowFlags(f, f2);
+	    else
+    		matchDropshadowFlags(f, f1);
+    return (FILTER*)f;
+}
+
+void matchBevelFlags(FILTER_BEVEL* unset, FILTER_BEVEL* target)
+{
+    unset->innershadow = target->innershadow;
+    unset->knockout = target->knockout;
+    unset->composite = target->composite;
+    unset->ontop = target->ontop;
+}
+
+FILTER* interpolateBevel(FILTER* filter1,FILTER* filter2, float ratio, interpolation_t* inter)
 			{
 				FILTER_BEVEL*f1 = (FILTER_BEVEL*)filter1;
 				FILTER_BEVEL*f2 = (FILTER_BEVEL*)filter2;
-				if(f2 && !memcmp(&f1->shadow,&f2->shadow,sizeof(RGBA)) && 
+    if (!f1)
+    	f1 = noBevel;
+    if (!f2)
+    	f2 = noBevel;
+    if(!memcmp(&f1->shadow,&f2->shadow,sizeof(RGBA)) &&
 					!memcmp(&f1->highlight,&f2->highlight,sizeof(RGBA)) && 
 					f1->blurx == f2->blurx && f1->blury == f2->blury && f1->angle == f2->angle && f1->strength == f2->strength && f1->distance == f2->distance)
 					return 0;
@@ -229,20 +435,113 @@ FILTER* interpolateFilter(FILTER* filter1,FILTER* filter2, float ratio, interpol
 				memcpy(f, f1, sizeof(FILTER_BEVEL));
 				f->shadow = interpolateColor(f1->shadow, f2->shadow, ratio, inter);
 				f->highlight = interpolateColor(f1->highlight, f2->highlight, ratio, inter);
-				f->blurx= (f1->blurx)*(1-ratio) + (f2?f2->blurx:0)*ratio;
-				f->blury= (f1->blury)*(1-ratio) + (f2?f2->blury:0)*ratio;
-				f->passes= (f1->passes)*(1-ratio) + (f2?f2->passes:0)*ratio;
-				f->angle= (f1->angle)*(1-ratio) + (f2?f2->angle:0)*ratio;
-				f->distance= (f1->distance)*(1-ratio) + (f2?f2->distance:0)*ratio;
-				f->strength= (f1->strength)*(1-ratio) + (f2?f2->strength:0)*ratio;
+    f->blurx= interpolateScalar(f1->blurx, (f2->blurx), ratio, inter);
+    f->blury= interpolateScalar(f1->blury, (f2->blury), ratio, inter);
+    f->passes= interpolateScalar(f1->passes, (f2->passes), ratio, inter);
+    f->angle= interpolateScalar(f1->angle, (f2->angle), ratio, inter);
+    f->distance= interpolateScalar(f1->distance, (f2->distance), ratio, inter);
+    f->strength= interpolateScalar(f1->strength, (f2->strength), ratio, inter);
+    if (f1 == noBevel)
+    {
+    	if (f2 != noBevel)
+    	    matchBevelFlags(f, f2);
+    }
+    else
+    	if (f2 == noBevel)
+	    matchBevelFlags(f, f1);
+	else
+	    if (ratio > 0.5)
+    		matchBevelFlags(f, f2);
+	    else
+    		matchBevelFlags(f, f1);
 				return (FILTER*)f;
-			} /*else if (filter1->type == FILTERTYPE_GRADIENTGLOW) {
+}
+
+void matchGradientGlowFlags(FILTER_GRADIENTGLOW* unset, FILTER_GRADIENTGLOW* target)
+{
+    unset->innershadow = target->innershadow;
+    unset->knockout = target->knockout;
+    unset->composite = target->composite;
+    unset->ontop = target->ontop;
+}
+
+FILTER* interpolateGradientGlow(FILTER* filter1,FILTER* filter2, float ratio, interpolation_t* inter)
+{
+    FILTER_GRADIENTGLOW*f1 = (FILTER_GRADIENTGLOW*)filter1;
+    FILTER_GRADIENTGLOW*f2 = (FILTER_GRADIENTGLOW*)filter2;
+    if (!f1)
+    	f1 = noGradientGlow;
+    if (!f2)
+    	f2 = noGradientGlow;
+    if(f1->gradient->num == f2->gradient->num &&
+    		!memcmp(&f1->gradient->ratios,&f2->gradient->ratios,f1->gradient->num * sizeof(U8)) &&
+    		!memcmp(&f1->gradient->rgba,&f2->gradient->rgba,f1->gradient->num * sizeof(RGBA)) &&
+		f1->blurx == f2->blurx && f1->blury == f2->blury && f1->angle == f2->angle && f1->strength == f2->strength && f1->distance == f2->distance)
+		return 0;
 	FILTER_GRADIENTGLOW*f = (FILTER_GRADIENTGLOW*)swf_NewFilter(FILTERTYPE_GRADIENTGLOW);
-	// can't interpolate gradients
-	memcpy(f, filter1, sizeof(FILTER_GRADIENTGLOW));
+    memcpy(f, f1, sizeof(FILTER_GRADIENTGLOW));
+    f->blurx= interpolateScalar(f1->blurx, (f2->blurx), ratio, inter);
+    f->blury= interpolateScalar(f1->blury, (f2->blury), ratio, inter);
+    f->passes= interpolateScalar(f1->passes, (f2->passes), ratio, inter);
+    f->angle= interpolateScalar(f1->angle, (f2->angle), ratio, inter);
+    f->distance= interpolateScalar(f1->distance, (f2->distance), ratio, inter);
+    double d = f->distance;
+    double fr;
+    if (d < 0)
+    	fr = ((int)d - d)*65536;
+    else
+    	fr = (d - (int)d)*65536;
+    	printf("%.3f <> %.3f : %.3f = %.3f [%.3f . %.3f] - %.5u.%.5u\n", f1->distance, f2->distance, ratio, f->distance, d, fr/65536, (U16)d, (U16)fr);
+    f->strength= interpolateScalar(f1->strength, (f2->strength), ratio, inter);
+    f->gradient= interpolateGradient(f1->gradient, f2->gradient, ratio, inter);
+    if (f1 == noGradientGlow)
+    {
+    	if (f2 != noGradientGlow)
+    	    matchGradientGlowFlags(f, f2);
+    }
+    else
+    	if (f2 == noGradientGlow)
+	    matchGradientGlowFlags(f, f1);
+	else
+	    if (ratio > 0.5)
+    		matchGradientGlowFlags(f, f2);
+	    else
+    		matchGradientGlowFlags(f, f1);
 	return (FILTER*)f;
-    }*/ else
-				syntaxerror("can't interpolate %s filters yet", filtername[filter1->type]);
+}
+
+FILTER* interpolateFilter(FILTER* filter1,FILTER* filter2, float ratio, interpolation_t* inter)
+{
+    if(!filter1 && !filter2)
+	return 0;
+
+
+    int filter_type;
+    if (!filter1)
+    	filter_type = filter2->type;
+    else
+    	if (!filter2)
+    	    filter_type = filter1->type;
+    	else
+	    if(filter2->type != filter1->type)
+		syntaxerror("can't interpolate between %s and %s filters yet", filtername[filter1->type], filtername[filter2->type]);
+	    else
+	    	filter_type = filter1->type;
+
+
+    switch (filter_type)
+    {
+        case FILTERTYPE_BLUR:
+            return interpolateBlur(filter1, filter2, ratio, inter);
+        case FILTERTYPE_BEVEL:
+            return interpolateBevel(filter1, filter2, ratio, inter);
+        case FILTERTYPE_DROPSHADOW:
+            return interpolateDropshadow(filter1, filter2, ratio, inter);
+        case FILTERTYPE_GRADIENTGLOW:
+            return interpolateGradientGlow(filter1, filter2, ratio, inter);
+        default:
+            syntaxerror("Filtertype %s not supported yet.\n", filtername[filter1->type]);
+    }
 	return 0;
 }
 
@@ -257,7 +556,14 @@ FILTER* copyFilter(FILTER* original)
 			memcpy(copy, original, sizeof(FILTER_BLUR));
 			break;
 		case FILTERTYPE_GRADIENTGLOW:
+	{
 			memcpy(copy, original, sizeof(FILTER_GRADIENTGLOW));
+	    FILTER_GRADIENTGLOW* ggcopy = (FILTER_GRADIENTGLOW*)copy;
+	    ggcopy->gradient = (GRADIENT*)malloc(sizeof(GRADIENT));
+	    ggcopy->gradient->ratios = (U8*)malloc(16 * sizeof(U8));
+	    ggcopy->gradient->rgba = (RGBA*)malloc(16 * sizeof(RGBA));
+	    copyGradient(ggcopy->gradient, ((FILTER_GRADIENTGLOW*)original)->gradient);
+	}
 			break;
 		case FILTERTYPE_DROPSHADOW:
 			memcpy(copy, original, sizeof(FILTER_DROPSHADOW)); 
@@ -265,40 +571,54 @@ FILTER* copyFilter(FILTER* original)
 		case FILTERTYPE_BEVEL:
 			memcpy(copy, original, sizeof(FILTER_BEVEL)); 
 			break;
-		default: printf("unsupported filterype");
+	default: syntaxerror("Internal error: unsupported filterype");
 	}
 	return copy;
 }
 
-FILTER* changeFilter_value(changeFilter_t* first, U16 frame)
+int changeFilter_differs(changeFilter_t* modification, U16 frame)
 {
-	changeFilter_t* previous = first;
-	while (first && first->frame < frame)
+    changeFilter_t* previous = modification;
+    while (modification && modification->frame < frame)
 	{
-		previous = first;
-		first = first->next;
+	previous = modification;
+	modification = modification->next;
 	}
-	if (!first)
+    if (!modification)
+	return 0;
+    if (modification->frame == frame)
+    	return 1;
+    return (modification->function != CF_JUMP);
+}
+
+FILTER* changeFilter_value(changeFilter_t* modification, U16 frame)
+{
+    changeFilter_t* previous = modification;
+    while (modification && modification->frame < frame)
+    {
+	previous = modification;
+	modification = modification->next;
+    }
+    if (!modification)
 		return copyFilter(previous->value);
-	if (first->frame == frame)
+    if (modification->frame == frame)
 	{
-		FILTER* result;
 		do 
 		{
-			result = first->value;
-			first = first->next;
+	    previous = modification;
+	    modification = modification->next;
 		}
-		while (first && first->frame == frame);
-		return copyFilter(result);
+	while (modification && modification->frame == frame);
+	return copyFilter(previous->value);
 	}
-	switch (first->function)
+    switch (modification->function)
 	{
 		case CF_PUT:
-			return copyFilter(first->value);
+	    return copyFilter(modification->value);
 		case CF_CHANGE:
 		{
-			float fraction = (frame - previous->frame) / (float)(first->frame - previous->frame);
-			return interpolateFilter(previous->value, first->value, fraction, first->interpolation);
+	    float fraction = (frame - previous->frame) / (float)(modification->frame - previous->frame);
+	    return interpolateFilter(previous->value, modification->value, fraction, modification->interpolation);
 		}
 		case CF_JUMP:
 			return copyFilter(previous->value);
@@ -382,6 +702,15 @@ void history_rememberFilter(history_t* past, U16 frame, int function, FILTER* va
 	}
 }
 
+int history_change(history_t* past, U16 frame, char* parameter)
+{
+    change_t* first = dictionary_lookup(past->changes, parameter);
+    if (first)	//should always be true.
+	return change_differs(first, frame);
+    printf("no history found for parameter %s\n", parameter);
+    return 0;
+}
+
 float history_value(history_t* past, U16 frame, char* parameter)
 {
 	change_t* first = dictionary_lookup(past->changes, parameter);
@@ -391,6 +720,15 @@ float history_value(history_t* past, U16 frame, char* parameter)
 	return 0;
 }
 
+int history_changeFilter(history_t* past, U16 frame)
+{
+    changeFilter_t* first = dictionary_lookup(past->changes, "filter");
+    if (first)	//should always be true.
+	return changeFilter_differs(first, frame);
+    printf("no history found for parameter filter\n");
+    return 0;
+}
+
 FILTER* history_valueFilter(history_t* past, U16 frame)
 {
 	changeFilter_t* first = dictionary_lookup(past->changes, "filter");
diff --git a/src/swfc-history.h b/src/swfc-history.h
index 00c5be4..b8529df 100644
--- a/src/swfc-history.h
+++ b/src/swfc-history.h
@@ -49,9 +49,19 @@ enum
 #define SF_SHEAR 0x0200
 #define SF_PIVOT 0x0400
 #define SF_PIN 0x0800
-#define SF_BLEND 0x01000
-#define SF_FILTER 0x02000
+#define SF_BLEND 0x1000
+#define SF_FILTER 0x2000
+#define SF_ALL 0x3fff
 
+FILTER_BLUR* noBlur;
+FILTER_BEVEL* noBevel;
+FILTER_DROPSHADOW* noDropshadow;
+FILTER_GRADIENTGLOW* noGradientGlow;
+
+typedef struct _spline
+{
+    float a, b, c, d;
+} spline_t;
 
 typedef struct _change
 {
@@ -59,6 +69,7 @@ typedef struct _change
 	float value;
 	int function;
 	interpolation_t* interpolation;
+    spline_t spline;
 	struct _change* next;
 } change_t;
 
@@ -75,6 +86,7 @@ typedef struct _changeFilter
 	int function;
 	interpolation_t* interpolation;
 	struct _changeFilter* next;
+    spline_t spline;
 } changeFilter_t;
 
 changeFilter_t* changeFilter_new(U16 frame, int function, FILTER* value, interpolation_t* inter);
@@ -97,7 +109,9 @@ void history_begin(history_t* past, char* parameter, U16 frame, TAG* tag, float
 void history_beginFilter(history_t* past, U16 frame, TAG* tag, FILTER* value);
 void history_remember(history_t* past, char* parameter, U16 frame, int function, float value, interpolation_t* inter);
 void history_rememberFilter(history_t* past, U16 frame, int function, FILTER* value, interpolation_t* inter);
+int history_change(history_t* past, U16 frame, char* parameter);
 float history_value(history_t* past, U16 frame, char* parameter);
+int history_changeFilter(history_t* past, U16 frame);
 FILTER* history_valueFilter(history_t* past, U16 frame);
 
 #endif
diff --git a/src/swfc.c b/src/swfc.c
index 41e5b93..a93fea5 100644
--- a/src/swfc.c
+++ b/src/swfc.c
@@ -46,6 +46,7 @@ static int verbose = 2;
 static int optimize = 0;
 static int override_outputname = 0;
 static int do_cgi = 0;
+static int change_sets_all = 0;
 
 static struct options_t options[] = {
 {"h", "help"},
@@ -446,7 +447,7 @@ static MATRIX s_instancepos(SRECT rect, parameters_t*p)
     return m;
 }
 
-void builtInInterpolations()
+void initBuiltIns()
 {
     interpolation_t* new;
     new = (interpolation_t*)malloc(sizeof(interpolation_t));
@@ -522,6 +523,34 @@ void builtInInterpolations()
     new = (interpolation_t*)malloc(sizeof(interpolation_t));
     new->function = IF_SINE_IN_OUT;
     dictionary_put2(&interpolations, "sineInOut", new);
+
+    RGBA c;
+    memset(&c, 0, sizeof(RGBA));
+    gradient_t* noGradient = (gradient_t*)malloc(sizeof(gradient_t));
+    noGradient->gradient.ratios = (U8*)malloc(16 * sizeof(U8));
+    noGradient->gradient.rgba = (RGBA*)malloc(16 * sizeof(RGBA));
+    noGradient->gradient.num = 2;
+    noGradient->gradient.rgba[0] = c;
+    noGradient->gradient.ratios[0] = 0;
+    noGradient->gradient.rgba[1] = c;
+    noGradient->gradient.ratios[1] = 255;
+    noGradient->radial = 0;
+    noGradient->rotate = 0;
+    dictionary_put2(&gradients, "no_gradient", noGradient);
+
+    noBlur = (FILTER_BLUR*) swf_NewFilter(FILTERTYPE_BLUR);
+    noBlur->passes = 1;
+    dictionary_put2(&filters, "no_blur", noBlur);
+    noBevel = (FILTER_BEVEL*) swf_NewFilter(FILTERTYPE_BEVEL);
+    noBevel->passes = 1;
+    dictionary_put2(&filters, "no_bevel", noBevel);
+    noDropshadow = (FILTER_DROPSHADOW*) swf_NewFilter(FILTERTYPE_DROPSHADOW);
+    noDropshadow->passes = 1;
+    dictionary_put2(&filters, "no_dropshadow", noDropshadow);
+    noGradientGlow = (FILTER_GRADIENTGLOW*) swf_NewFilter(FILTERTYPE_GRADIENTGLOW);
+    noGradientGlow->passes = 1;
+    noGradientGlow->gradient = &noGradient->gradient;
+    dictionary_put2(&filters, "no_gradientglow", noGradientGlow);
 }
 
 void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA background)
@@ -550,7 +579,7 @@ void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA backgrou
     dictionary_init(&instances);
     dictionary_init(&sounds);
     dictionary_init(&interpolations);
-    builtInInterpolations();
+    initBuiltIns();
     cleanUp = &freeDictionaries;
 
     memset(&stack[stackpos], 0, sizeof(stack[0]));
@@ -751,6 +780,34 @@ TAG* removeFromTo(TAG*from, TAG*to)
     return save;
 }
 
+static int parametersChange(history_t* history, int frame)
+{
+    int willChange = 0;
+
+    willChange = willChange || history_change(history, frame, "x");
+    willChange = willChange || history_change(history, frame, "y");
+    willChange = willChange || history_change(history, frame, "scalex");
+    willChange = willChange || history_change(history, frame, "scaley");
+    willChange = willChange || history_change(history, frame, "cxform.r0");
+    willChange = willChange || history_change(history, frame, "cxform.g0");
+    willChange = willChange || history_change(history, frame, "cxform.b0");
+    willChange = willChange || history_change(history, frame, "cxform.a0");
+    willChange = willChange || history_change(history, frame, "cxform.r1");
+    willChange = willChange || history_change(history, frame, "cxform.g1");
+    willChange = willChange || history_change(history, frame, "cxform.b1");
+    willChange = willChange || history_change(history, frame, "cxform.a1");
+    willChange = willChange || history_change(history, frame, "rotate");
+    willChange = willChange || history_change(history, frame, "shear");
+    willChange = willChange || history_change(history, frame, "pivot.x");
+    willChange = willChange || history_change(history, frame, "pivot.y");
+    willChange = willChange || history_change(history, frame, "pin.x");
+    willChange = willChange || history_change(history, frame, "pin.y");
+    willChange = willChange || history_change(history, frame, "blendmode");
+    willChange = willChange || history_changeFilter(history, frame);
+
+    return willChange;
+}
+
 static void readParameters(history_t* history, parameters_t* p, int frame)
 {
     p->x = history_value(history, frame, "x");
@@ -808,9 +865,11 @@ static void writeInstance(instance_t* i)
     while (frame < currentframe)
     {
         frame++;
-        readParameters(i->history, &p, frame);
         while (tag->id != ST_SHOWFRAME)
             tag = tag->next;
+        if (parametersChange(i->history, frame))
+        {
+            readParameters(i->history, &p, frame);
         m = s_instancepos(i->character->size, &p);
 
         if(p.blendmode || p.filter)
@@ -819,9 +878,16 @@ static void writeInstance(instance_t* i)
             tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
         setPlacement(tag, 0, i->depth, m, 0, &p, 1);
         if (p.filter)
+            {
+            	if (p.filter->type == FILTERTYPE_GRADIENTGLOW)
+            	    gradient_free(((FILTER_GRADIENTGLOW*)p.filter)->gradient);
             free(p.filter);
     }
 }
+        else
+            tag = tag->next;
+    }
+}
 
 void dumpSWF(SWF*swf)
 {
@@ -853,6 +919,8 @@ static void s_endSprite()
         num++;
         name = stringarray_at(index, num);
     }
+    while (tag->next)
+	tag = tag->next;
 
     tag = swf_InsertTag(tag, ST_SHOWFRAME);
     tag = swf_InsertTag(tag, ST_END);
@@ -1615,11 +1683,11 @@ void s_gradientglow(char*name, char*gradient, float blurx, float blury,
         syntaxerror("filter %s defined twice", name);
 
     gradient_t* g = dictionary_lookup(&gradients, gradient);
+    if(!g)
+	syntaxerror("unknown gradient %s", gradient);
 
     composite = 1;
 
-    if(!g)
-	syntaxerror("unknown gradient %s", gradient);
     FILTER_GRADIENTGLOW* filter = rfx_calloc(sizeof(FILTER_GRADIENTGLOW));
     filter->type = FILTERTYPE_GRADIENTGLOW;
     filter->gradient = &g->gradient;
@@ -1893,11 +1961,15 @@ SRECT s_getInstanceBBox(char*name)
     if(!c) syntaxerror("internal error(5)");
     return c->size;
 }
-parameters_t s_getParameters(char*name)
+void s_getParameters(char*name, parameters_t* p)
 {
     instance_t * i = dictionary_lookup(&instances, name);
-    if(!i) syntaxerror("instance '%s' unknown(10)", name);
-    return i->parameters;
+    if(!i)
+    	syntaxerror("instance '%s' unknown(10)", name);
+    if (change_sets_all)
+        readParameters(i->history, p, currentframe);
+    else
+    	*p = i->parameters;
 }
 void s_startclip(char*instance, char*character, parameters_t p)
 {
@@ -2055,6 +2127,7 @@ void s_change(char*instance, parameters_t p2, interpolation_t* inter)
     instance_t* i = dictionary_lookup(&instances, instance);
     if(!i)
         syntaxerror("instance %s not known", instance);
+
     recordChanges(i->history, p2, CF_CHANGE, inter);
 }
 
@@ -2391,11 +2464,12 @@ static int c_flash(map_t*args)
 {
     char* filename = map_lookup(args, "filename");
     char* compressstr = lu(args, "compress");
+    char* change_modestr = lu(args, "change-sets-all");
     SRECT bbox = parseBox(lu(args, "bbox"));
     int version = parseInt(lu(args, "version"));
     int fps = (int)(parseFloat(lu(args, "fps"))*256);
-    int compress = 0;
     RGBA color = parseColor(lu(args, "background"));
+    int compress = 0;
 
     if(!filename || !*filename) {
 	/* for compatibility */
@@ -2419,6 +2493,12 @@ static int c_flash(map_t*args)
 	compress = 0;
     else syntaxerror("value \"%s\" not supported for the compress argument", compressstr);
 
+    if(!strcmp(change_modestr, "yes"))
+	change_sets_all = 1;
+    else
+	if(strcmp(change_modestr, "no"))
+	    syntaxerror("value \"%s\" not supported for the change-sets-all argument", change_modestr);
+
     s_swf(filename, bbox, version, fps, compress, color);
     return 0;
 }
@@ -2801,7 +2881,7 @@ static int c_placement(map_t*args, int type)
     SRECT oldbbox;
     MULADD luminance;
     parameters_t p;
-    U32 set = 0x00000000;
+    U16 set = 0x0000;
 
     if(type==9)
     { // (?) .rotate  or .arcchange
@@ -2838,7 +2918,7 @@ static int c_placement(map_t*args, int type)
 	parameters_clear(&p);
 	// button's show
     } else {
-	p = s_getParameters(instance);
+	s_getParameters(instance, &p);
     }
 
     /* x,y position */
@@ -3019,6 +3099,8 @@ static int c_placement(map_t*args, int type)
         set = set | SF_FILTER;
     }
 
+    if (change_sets_all)
+	set = SF_ALL;
     p.set = set;
 
     switch (type)
@@ -3500,7 +3582,7 @@ static struct {
     command_func_t* func;
     char*arguments;
 } arguments[] =
-{{"flash", c_flash, "bbox=autocrop background=black version=6 fps=50 name= filename= @compress=default"},
+{{"flash", c_flash, "bbox=autocrop background=black version=6 fps=50 name= filename= @compress=default @change-sets-all=no"},
  {"frame", c_frame, "n=<plus>1 name= @cut=no @anchor=no"},
  // "import" type stuff
  {"swf", c_swf, "name filename"},