applied patches from Huub Schaeks
authorkramm <kramm>
Fri, 27 Jul 2007 19:57:02 +0000 (19:57 +0000)
committerkramm <kramm>
Fri, 27 Jul 2007 19:57:02 +0000 (19:57 +0000)
15 files changed:
lib/drawer.c
lib/modules/swfbits.c
lib/modules/swfshape.c
lib/modules/swftext.c
lib/mp3.c
lib/q.c
lib/q.h
lib/wav.c
src/swfc-feedback.c [new file with mode: 0644]
src/swfc-feedback.h [new file with mode: 0644]
src/swfc-history.c [new file with mode: 0644]
src/swfc-history.h [new file with mode: 0644]
src/swfc-interpolation.c [new file with mode: 0644]
src/swfc-interpolation.h [new file with mode: 0644]
src/swfc.c

index 801ff38..f5196ca 100644 (file)
@@ -94,52 +94,64 @@ static void draw_lineTo2(drawer_t*draw, double x, double y)
     draw->lineTo(draw, &c);
 }
 
+static float getFloat(const char** p)
+{
+    char* token = getToken(p);
+    float result = atof(token);
+    free(token);
+    return result;
+}
 
 void draw_string(drawer_t*draw, const char*string)
 {
     const char*p = string;
     while(*p) {
        char*token = getToken(&p);
-       if(!token || !*token) 
+    if(!token)
+        break;
+    if (!*token) 
+    {
+        free(token);
            break;
+    }
        if(!strncmp(token, "moveTo", 6) ||
           !strncmp(token, "M", 1) //svg
           ) {
            FPOINT to;
-           to.x = atof(getToken(&p));
-           to.y = atof(getToken(&p));
+        to.x = getFloat(&p);
+        to.y = getFloat(&p);
            draw->moveTo(draw, &to);
        }
        else if(!strncmp(token, "lineTo", 6) ||
                !strncmp(token, "L", 1) //svg
             ) {
            FPOINT to;
-           to.x = atof(getToken(&p));
-           to.y = atof(getToken(&p));
+        to.x = getFloat(&p);
+        to.y = getFloat(&p);
            draw->lineTo(draw, &to);
        }
        else if(!strncmp(token, "curveTo", 7) || !strncmp(token, "splineTo", 8)) {
            FPOINT mid,to;
-           mid.x = atof(getToken(&p));
-           mid.y = atof(getToken(&p));
-           to.x = atof(getToken(&p));
-           to.y = atof(getToken(&p));
+        mid.x = getFloat(&p);
+        mid.y = getFloat(&p);
+        to.x = getFloat(&p);
+        to.y = getFloat(&p);
            draw->splineTo(draw, &mid, &to);
        }
        else if(!strncmp(token, "conicTo", 5)) {
            FPOINT mid,to;
-           mid.x = atof(getToken(&p));
-           mid.y = atof(getToken(&p));
-           to.x = atof(getToken(&p));
-           to.y = atof(getToken(&p));
+        mid.x = getFloat(&p);
+        mid.y = getFloat(&p);
+        to.x = getFloat(&p);
+        to.y = getFloat(&p);
            draw_conicTo(draw, &mid, &to);
        }
        else if(!strncmp(token, "circle", 6)) {
            int mx,my,r;
            double r2;
-           mx = atof(getToken(&p));
-           my = atof(getToken(&p));
-           r = atof(getToken(&p));
+        mx = getFloat(&p);
+        my = getFloat(&p);
+        r = getFloat(&p);
            r2 = 0.70710678118654757*r;
            draw_moveTo2(draw, mx, my-r);
            draw_conicTo2(draw, mx+r2, my-r2, mx+r, my);
@@ -149,10 +161,10 @@ void draw_string(drawer_t*draw, const char*string)
        }
        else if(!strncmp(token, "box", 3)) {
            int x1,y1,x2,y2;
-           x1 = atof(getToken(&p));
-           y1 = atof(getToken(&p));
-           x2 = atof(getToken(&p));
-           y2 = atof(getToken(&p));
+        x1 = getFloat(&p);
+        y1 = getFloat(&p);
+        x2 = getFloat(&p);
+        y2 = getFloat(&p);
            draw_moveTo2(draw, x1, y1);
            draw_lineTo2(draw, x1, y2);
            draw_lineTo2(draw, x2, y2);
@@ -163,12 +175,12 @@ void draw_string(drawer_t*draw, const char*string)
                !strncmp(token, "C", 1) //svg
                ) {
            FPOINT mid1,mid2,to;
-           mid1.x = atof(getToken(&p));
-           mid1.y = atof(getToken(&p));
-           mid2.x = atof(getToken(&p));
-           mid2.y = atof(getToken(&p));
-           to.x = atof(getToken(&p));
-           to.y = atof(getToken(&p));
+        mid1.x = getFloat(&p);
+        mid1.y = getFloat(&p);
+        mid2.x = getFloat(&p);
+        mid2.y = getFloat(&p);
+        to.x = getFloat(&p);
+        to.y = getFloat(&p);
            draw_cubicTo(draw, &mid1, &mid2, &to);
        }
        else if(!strncmp(token, "z", 1) //svg
index 80b151b..204dcc4 100644 (file)
@@ -393,6 +393,7 @@ int swf_SetJPEGBits(TAG * t, char *fname, int quality)
        }
     }
 
+    free(scanline);
     swf_SetJPEGBitsFinish(out);
     jpeg_finish_decompress(&cinfo);
     fclose(f);
index 2be4ee3..7bb6424 100644 (file)
@@ -926,6 +926,7 @@ void swf_Shape2ToShape(SHAPE2*shape2, SHAPE*shape)
     swf_ShapeSetEnd(tag);
     shape->data = tag->data;
     shape->bitlen = tag->len*8;
+    free(tag);
 }
 
 void swf_SetShape2(TAG*tag, SHAPE2*shape2)
@@ -986,6 +987,13 @@ void swf_ParseDefineShape(TAG*tag, SHAPE2*shape)
     l = shape->lines;
 }
 
+static void free_lines(SHAPELINE* lines)
+{
+    if (lines->next)
+        free_lines(lines->next);
+    free(lines);
+}
+
 void swf_RecodeShapeData(U8*data, int bitlen, int in_bits_fill, int in_bits_line, 
                          U8**destdata, U32*destbitlen, int out_bits_fill, int out_bits_line)
 {
@@ -1009,6 +1017,7 @@ void swf_RecodeShapeData(U8*data, int bitlen, int in_bits_fill, int in_bits_line
 
     swf_Shape2ToShape(&s2,&s);
 
+    free_lines(s2.lines);
     free(s2.fillstyles);
     free(s2.linestyles);
     free(s.fillstyle.data);
index 1612e63..363b55d 100644 (file)
@@ -22,7 +22,7 @@
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
-static U32 readUTF8char(U8 ** text)
+U32 readUTF8char(U8 ** text)
 {
     U32 c = 0;
     if (!(*(*text) & 0x80))
@@ -572,18 +572,20 @@ void swf_LayoutFree(SWFLAYOUT * l)
 
 static void font_freeglyphnames(SWFFONT*f)
 {
-    if (f->glyphnames) {
-       int t;
-       for (t = 0; t < f->numchars; t++) {
-           if (f->glyphnames[t]) {
-               rfx_free(f->glyphnames[t]);
-               f->glyphnames[t] = 0;
-           }
+    if (f->glyphnames)
+    {
+        int t;
+        for (t = 0; t < f->numchars; t++)
+        {
+            if (f->glyphnames[t])
+            {
+                rfx_free(f->glyphnames[t]);
+                f->glyphnames[t] = 0;
+            }
+        }
+        rfx_free(f->glyphnames);
+        f->glyphnames = 0;
        }
-       rfx_free(f->glyphnames);
-       f->glyphnames = 0;
-    }
-
 }
 static void font_freeusage(SWFFONT*f)
 {
@@ -1036,24 +1038,28 @@ void swf_FontFree(SWFFONT * f)
 {
     int i;
     if (!f)
-       return;
+        return;
 
-    if (f->glyph) {
-       for (i = 0; i < f->numchars; i++)
-           if (f->glyph[i].shape) {
-               swf_ShapeFree(f->glyph[i].shape);
-               f->glyph[i].shape = NULL;
-           }
-       rfx_free(f->glyph);
-       f->glyph = NULL;
+    if (f->glyph)
+    {
+        for (i = 0; i < f->numchars; i++)
+            if (f->glyph[i].shape)
+            {
+                swf_ShapeFree(f->glyph[i].shape);
+                f->glyph[i].shape = NULL;
+            }
+            rfx_free(f->glyph);
+            f->glyph = NULL;
     }
-    if (f->ascii2glyph) {
-       rfx_free(f->ascii2glyph);
-       f->ascii2glyph = NULL;
+    if (f->ascii2glyph)
+    {
+        rfx_free(f->ascii2glyph);
+        f->ascii2glyph = NULL;
     }
-    if (f->glyph2ascii) {
-       rfx_free(f->glyph2ascii);
-       f->glyph2ascii = NULL;
+    if (f->glyph2ascii)
+    {
+        rfx_free(f->glyph2ascii);
+        f->glyph2ascii = NULL;
     }
     font_freename(f);
     font_freelayout(f);
index 8bb7ee7..408ef37 100644 (file)
--- a/lib/mp3.c
+++ b/lib/mp3.c
@@ -157,6 +157,7 @@ int mp3_read(struct MP3*mp3, char* filename)
     if(!root)
     {
         fprintf(stderr, "readMP3: not a MP3 file\n");
+        fclose(fi);
         return 0;
     }
     
@@ -192,6 +193,7 @@ int mp3_read(struct MP3*mp3, char* filename)
         root = next;
     }
     
+    fclose(fi);
     return mp3->data != NULL;
 }
 
diff --git a/lib/q.c b/lib/q.c
index 7d257f7..0083dcd 100644 (file)
--- a/lib/q.c
+++ b/lib/q.c
@@ -384,6 +384,12 @@ void dictionary_put2(dictionary_t*dict, const char*t1, void* t2)
     string_set(&s, (char*)t1);
     dictionary_put(dict, s, t2);
 }
+stringarray_t* dictionary_index(dictionary_t*dict)
+{
+    dictionary_internal_t*d = (dictionary_internal_t*)dict->internal;
+    return &d->keys;
+}
+
 void* dictionary_lookup(dictionary_t*dict, const char*name)
 {
     int s;
@@ -430,6 +436,20 @@ void dictionary_destroy(dictionary_t*dict)
     free(dict);
 }
 
+void dictionary_free_all(dictionary_t* dict, void (*freeFunction)(void*))
+{
+    dictionary_internal_t*d = (dictionary_internal_t*)dict->internal;
+    int num = 0;
+    char* name = stringarray_at(&d->keys, num)    ;
+    while (name)
+    {
+        freeFunction(dictionary_lookup(dict, name));
+        num++;
+        name = stringarray_at(&d->keys, num);
+    }
+    dictionary_clear(dict);
+}
+
 // ------------------------------- heap_t -------------------------------
 
 void heap_init(heap_t*h,int n,int elem_size, int(*compare)(const void *, const void *))
diff --git a/lib/q.h b/lib/q.h
index 0e9d602..35629d8 100644 (file)
--- a/lib/q.h
+++ b/lib/q.h
@@ -111,11 +111,13 @@ void map_destroy(map_t*map);
 void dictionary_init(dictionary_t*dict);
 void dictionary_put(dictionary_t*dict, string_t t1, void* t2);
 void dictionary_put2(dictionary_t*dict, const char* t1, void* t2);
+stringarray_t* dictionary_index(dictionary_t*dict);
 void* dictionary_lookup(dictionary_t*dict, const char*name);
 void dictionary_dump(dictionary_t*dict, FILE*fi, const char*prefix);
 void dictionary_del(dictionary_t*dict, const char* name);
 void dictionary_clear(dictionary_t*dict);
 void dictionary_destroy(dictionary_t*dict);
+void dictionary_free_all(dictionary_t* dict, void (*freeFunction)(void*));
 
 void heap_init(heap_t*h,int n,int elem_size, int(*compare)(const void *, const void *));
 void heap_clear(heap_t*h);
index 17ca2a2..72d9502 100644 (file)
--- a/lib/wav.c
+++ b/lib/wav.c
@@ -53,8 +53,9 @@ int wav_read(struct WAV*wav, char* filename)
     long int filesize;
     struct WAVBlock block;
     long int pos;
+    
     if(!fi)
-       return 0;
+        return 0;
     fseek(fi, 0, SEEK_END);
     filesize = ftell(fi);
     fseek(fi, 0, SEEK_SET);
@@ -62,65 +63,87 @@ int wav_read(struct WAV*wav, char* filename)
     //printf("Filesize: %d\n", filesize);
 
     if(!getWAVBlock (fi, &block))
-       return 0;
-    if(strncmp(block.id,"RIFF",4)) {
-       fprintf(stderr, "wav_read: not a WAV file\n");
-       return 0;
+    {
+        fclose(fi);
+        return 0;
+    }
+    if(strncmp(block.id,"RIFF",4))
+    {
+        fprintf(stderr, "wav_read: not a WAV file\n");
+        fclose(fi);
+        return 0;
     }
     if(block.size + 8 < filesize)
-       fprintf(stderr, "wav_read: warning - more tags (%d extra bytes)\n",
-               filesize - block.size - 8);
+        fprintf(stderr, "wav_read: warning - more tags (%d extra bytes)\n",
+    filesize - block.size - 8);
 
-    if(block.size == filesize) {
+    if(block.size == filesize)
        /* some buggy software doesn't generate the right tag length */
-       block.size = filesize - 8;
-    }
+        block.size = filesize - 8;
 
     if(block.size + 8 > filesize)
-       fprintf(stderr, "wav_read: warning - short file (%d bytes missing)\n",
-               block.size + 8 -  filesize);
-    if(fread(b, 1, 4, fi) < 4) {
-       return 0;
-    }
-    if(strncmp(b, "WAVE", 4)) {
-       fprintf(stderr, "wav_read: not a WAV file (2)\n");
-       return 0;
-    }
-    do
+        fprintf(stderr, "wav_read: warning - short file (%d bytes missing)\n",
+    block.size + 8 -  filesize);
+    if(fread(b, 1, 4, fi) < 4)
     {
-       getWAVBlock(fi, &block);
-       pos = ftell(fi);
-       if(!strncmp(block.id, "fmt ", 4)) {
-           if(fread(&b, 1, 16, fi)<16)
-               return 0;
-           wav->tag = b[0]|b[1]<<8;
-           wav->channels = b[2]|b[3]<<8;
-           wav->sampsPerSec = b[4]|b[5]<<8|b[6]<<16|b[7]<<24;
-           wav->bytesPerSec = b[8]|b[9]<<8|b[10]<<16|b[11]<<24;
-           wav->align = b[12]|b[13]<<8;
-           wav->bps = b[14]|b[15]<<8;
-       } else if (!strncmp(block.id, "LIST", 4)) {
-           // subchunk ICMT (comment) may exist
-       } else if (!strncmp(block.id, "data", 4)) {
-           int l;
-           wav->data = malloc(block.size);
-           if(!wav->data) {
-               fprintf(stderr, "Out of memory (%d bytes needed)", block.size);
+        fclose(fi);
                return 0;
-           }
-           l = fread(wav->data, 1, block.size, fi);
-           if(l < block.size) {
-               fprintf(stderr, "Error while reading data block of size %d (%d bytes missing)", block.size, block.size-l);
+    }
+    if(strncmp(b, "WAVE", 4))
+    {
+        fprintf(stderr, "wav_read: not a WAV file (2)\n");
+        fclose(fi);
                return 0;
-           }
-           wav->size = block.size;
        }
-       pos+=block.size;
-       fseek(fi, pos, SEEK_SET);
+    do
+    {
+        getWAVBlock(fi, &block);
+        pos = ftell(fi);
+        if(!strncmp(block.id, "fmt ", 4))
+        {
+            if(fread(&b, 1, 16, fi)<16)
+            {
+                fclose(fi);
+                return 0;
+            }
+            wav->tag = b[0]|b[1]<<8;
+            wav->channels = b[2]|b[3]<<8;
+            wav->sampsPerSec = b[4]|b[5]<<8|b[6]<<16|b[7]<<24;
+            wav->bytesPerSec = b[8]|b[9]<<8|b[10]<<16|b[11]<<24;
+            wav->align = b[12]|b[13]<<8;
+            wav->bps = b[14]|b[15]<<8;
+        }
+        else
+            if (!strncmp(block.id, "LIST", 4))
+            {
+        // subchunk ICMT (comment) may exist
+            }
+            else
+                if (!strncmp(block.id, "data", 4))
+                {
+                    int l;
+                    wav->data = malloc(block.size);
+                    if(!wav->data)
+                    {
+                        fprintf(stderr, "Out of memory (%d bytes needed)", block.size);
+                        fclose(fi);
+                        return 0;
+                    }
+                    l = fread(wav->data, 1, block.size, fi);
+                    if(l < block.size)
+                    {
+                        fprintf(stderr, "Error while reading data block of size %d (%d bytes missing)", block.size, block.size-l);
+                        fclose(fi);
+                        return 0;
+                    }
+                    wav->size = block.size;
+                }
+                pos+=block.size;
+                fseek(fi, pos, SEEK_SET);
     }
     while (pos < filesize);
-
+    fclose(fi);
     return 1;
 }
 
diff --git a/src/swfc-feedback.c b/src/swfc-feedback.c
new file mode 100644 (file)
index 0000000..51e0d91
--- /dev/null
@@ -0,0 +1,48 @@
+/* swfc- Compiles swf code (.sc) files into .swf files.
+
+   Part of the swftools package.
+
+   Copyright (c) 2007 Huub Schaeks <huub@h-schaeks.speedlinq.nl>
+   Copyright (c) 2007 Matthias Kramm <kramm@quiss.org>
+   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 <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include "feedback.h"
+
+void syntaxerror(char*format, ...)
+{
+    char buf[1024];
+    va_list arglist;
+    va_start(arglist, format);
+    vsprintf(buf, format, arglist);
+    va_end(arglist);
+    fprintf(stderr, "\"%s\", line %d column %d: error- %s\n", filename, line, column, buf);
+    if (cleanUp)
+           cleanUp();
+    exit(1);
+}
+
+void warning(char*format, ...)
+{
+    char buf[1024];
+    va_list arglist;
+    va_start(arglist, format);
+    vsprintf(buf, format, arglist);
+    va_end(arglist);
+    fprintf(stderr, "\"%s\", line %d column %d: warning- %s\n", filename, line, column, buf);
+}
diff --git a/src/swfc-feedback.h b/src/swfc-feedback.h
new file mode 100644 (file)
index 0000000..6196ea1
--- /dev/null
@@ -0,0 +1,33 @@
+/* swfc- Compiles swf code (.sc) files into .swf files.
+
+   Part of the swftools package.
+
+   Copyright (c) 2007 Huub Schaeks <huub@h-schaeks.speedlinq.nl>
+   Copyright (c) 2007 Matthias Kramm <kramm@quiss.org>
+   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 */
+
+#ifndef __FEEDBACK_H
+#define __FEEDBACK_H
+
+char* filename;
+int line;
+int column;
+void (*cleanUp)();
+
+void syntaxerror(char*format, ...);
+void warning(char*format, ...);
+
+#endif
diff --git a/src/swfc-history.c b/src/swfc-history.c
new file mode 100644 (file)
index 0000000..b7ce399
--- /dev/null
@@ -0,0 +1,401 @@
+/* swfc- Compiles swf code (.sc) files into .swf files.
+
+   Part of the swftools package.
+
+   Copyright (c) 2007 Huub Schaeks <huub@h-schaeks.speedlinq.nl>
+   Copyright (c) 2007 Matthias Kramm <kramm@quiss.org>
+   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 <stdlib.h>
+#include <memory.h>
+#include "history.h"
+
+change_t* change_new(U16 frame, int function, float value, interpolation_t* inter)
+{
+       change_t* newChange = (change_t*)malloc(sizeof(change_t));
+       change_init(newChange);
+       newChange->frame = frame;
+       newChange->function = function;
+       newChange->value = value;
+       newChange->interpolation = inter;
+       return newChange;
+}
+
+void change_free(change_t *change)
+{
+       if (change->next)
+               change_free(change->next);
+       free(change);
+}
+
+void change_init(change_t* change)
+{
+       memset(change, 0, sizeof(change_t));
+}
+
+void change_append(change_t* first, change_t* newChange)
+{
+       while (first->next)
+               first = first->next;
+       first->next = newChange;
+}
+
+float interpolateParameter(float p1, float p2, float fraction, interpolation_t* inter)
+{
+       if (!inter)
+               return linear(fraction, p1, p2 - p1);
+       switch (inter->function)
+       {
+               case IF_LINEAR: return linear(fraction, p1, p2 - p1);
+               case IF_QUAD_IN: return quadIn(fraction, p1, p2 - p1);
+               case IF_QUAD_OUT: return quadOut(fraction, p1, p2 - p1);
+               case IF_QUAD_IN_OUT: return quadInOut(fraction, p1, p2 - p1);
+               case IF_CUBIC_IN: return cubicIn(fraction, p1, p2 - p1);
+               case IF_CUBIC_OUT: return cubicOut(fraction, p1, p2 - p1);
+               case IF_CUBIC_IN_OUT: return cubicInOut(fraction, p1, p2 - p1);
+               case IF_QUART_IN: return quartIn(fraction, p1, p2 - p1);
+               case IF_QUART_OUT: return quartOut(fraction, p1, p2 - p1);
+               case IF_QUART_IN_OUT: return quartInOut(fraction, p1, p2 - p1);
+               case IF_QUINT_IN: return quintIn(fraction, p1, p2 - p1);
+               case IF_QUINT_OUT: return quintOut(fraction, p1, p2 - p1);
+               case IF_QUINT_IN_OUT: return quintInOut(fraction, p1, p2 - p1);
+               case IF_CIRCLE_IN: return circleIn(fraction, p1, p2 - p1);
+               case IF_CIRCLE_OUT: return circleOut(fraction, p1, p2 - p1);
+               case IF_CIRCLE_IN_OUT: return circleInOut(fraction, p1, p2 - p1);
+               case IF_EXPONENTIAL_IN: return exponentialIn(fraction, p1, p2 - p1);
+               case IF_EXPONENTIAL_OUT: return exponentialOut(fraction, p1, p2 - p1);
+               case IF_EXPONENTIAL_IN_OUT: return exponentialInOut(fraction, p1, p2 - p1);
+               case IF_SINE_IN: return sineIn(fraction, p1, p2 - p1);
+               case IF_SINE_OUT: return sineOut(fraction, p1, p2 - p1);
+               case IF_SINE_IN_OUT: return sineInOut(fraction, p1, p2 - p1);
+               case IF_ELASTIC_IN: return elasticIn(fraction, p1, p2 - p1, inter->amplitude, inter->bounces, inter->damping);
+               case IF_ELASTIC_OUT: return elasticOut(fraction, p1, p2 - p1, inter->amplitude, inter->bounces, inter->damping);
+               case IF_ELASTIC_IN_OUT: return elasticInOut(fraction, p1, p2 - p1, inter->amplitude, inter->bounces, inter->damping);
+               case IF_BACK_IN: return backIn(fraction, p1, p2 - p1, inter->speed);
+               case IF_BACK_OUT: return backOut(fraction, p1, p2 - p1, inter->speed);
+               case IF_BACK_IN_OUT: return backInOut(fraction, p1, p2 - p1, inter->speed);
+               case IF_BOUNCE_IN: return bounceIn(fraction, p1, p2 - p1, inter->bounces, inter->growth, inter->damping);
+               case IF_BOUNCE_OUT: return bounceOut(fraction, p1, p2 - p1, inter->bounces, inter->growth, inter->damping);
+               case IF_BOUNCE_IN_OUT: return bounceInOut(fraction, p1, p2 - p1, inter->bounces, inter->growth, inter->damping);
+               case IF_FAST_BOUNCE_IN: return fastBounceIn(fraction, p1, p2 - p1, inter->bounces, inter->growth, inter->damping);
+               case IF_FAST_BOUNCE_OUT: return fastBounceOut(fraction, p1, p2 - p1, inter->bounces, inter->growth, inter->damping);
+               case IF_FAST_BOUNCE_IN_OUT: return fastBounceInOut(fraction, p1, p2 - p1, inter->bounces, inter->growth, inter->damping);
+               default: return linear(fraction, p1, p2 - p1);
+       }
+}
+
+float change_value(change_t* first, U16 frame)
+{
+       change_t* previous = first;
+       while (first && first->frame < frame)
+       {
+               previous = first;
+               first = first->next;
+       }
+       if (!first)
+               return previous->value;
+       if (first->frame == frame)
+       {
+               float result;
+               do 
+               {
+                       result = first->value;
+                       first = first->next;
+               }
+               while (first && first->frame == frame);
+               return result;
+       }
+       switch (first->function)
+       {
+               case CF_PUT:
+                       return first->value;
+               case CF_CHANGE:
+               {
+                       float fraction = (frame - previous->frame) / (float)(first->frame - previous->frame);
+                       return interpolateParameter(previous->value, first->value, fraction, first->interpolation);
+               }
+               case CF_JUMP:
+                       return previous->value;
+               default:
+                       return 0;
+       }
+}
+
+changeFilter_t* changeFilter_new(U16 frame, int function, FILTER* value, interpolation_t* inter)
+{
+       changeFilter_t* newChange = (changeFilter_t*)malloc(sizeof(changeFilter_t));
+       changeFilter_init(newChange);
+       newChange->frame = frame;
+       newChange->function = function;
+       newChange->value = value;
+       newChange->interpolation = inter;
+       return newChange;
+}
+
+void changeFilter_free(changeFilter_t *change)
+{
+       if (change->next)
+               changeFilter_free(change->next);
+       free(change);
+}
+
+void changeFilter_init(changeFilter_t* change)
+{
+       memset(change, 0, sizeof(changeFilter_t));
+}
+
+void changeFilter_append(changeFilter_t* first, changeFilter_t* newChange)
+{
+       while (first->next)
+               first = first->next;
+       first->next = 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;
+    return c;
+}
+
+FILTER* interpolateFilter(FILTER* filter1,FILTER* filter2, float ratio, interpolation_t* inter)
+{
+       if(!filter1 && !filter2)
+               return 0;
+       if(!filter1)
+               return interpolateFilter(filter2,filter1,1-ratio, inter);
+
+       if(filter2 && filter2->type != filter1->type)
+               syntaxerror("can't interpolate between %s and %s filters yet", filtername[filter1->type], filtername[filter2->type]);
+   
+       if(filter1->type == FILTERTYPE_BLUR)
+       {
+               FILTER_BLUR*f1 = (FILTER_BLUR*)filter1;
+               FILTER_BLUR*f2 = (FILTER_BLUR*)filter2;
+               if(f2 && 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;
+               return (FILTER*)f;
+               }
+       else
+               if (filter1->type == FILTERTYPE_DROPSHADOW)
+               {
+                       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 && 
+                               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;
+               }
+               else
+                       if (filter1->type == FILTERTYPE_BEVEL)
+                       {
+                               FILTER_BEVEL*f1 = (FILTER_BEVEL*)filter1;
+                               FILTER_BEVEL*f2 = (FILTER_BEVEL*)filter2;
+                               if(f2 && !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;
+                               FILTER_BEVEL*f = (FILTER_BEVEL*)swf_NewFilter(FILTERTYPE_BEVEL);
+                               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;
+                               return (FILTER*)f;
+                       } /*else if (filter1->type == FILTERTYPE_GRADIENTGLOW) {
+       FILTER_GRADIENTGLOW*f = (FILTER_GRADIENTGLOW*)swf_NewFilter(FILTERTYPE_GRADIENTGLOW);
+       // can't interpolate gradients
+       memcpy(f, filter1, sizeof(FILTER_GRADIENTGLOW));
+       return (FILTER*)f;
+    }*/ else
+                               syntaxerror("can't interpolate %s filters yet", filtername[filter1->type]);
+       return 0;
+}
+
+FILTER* copyFilter(FILTER* original)
+{
+       if (!original)
+               return original;
+       FILTER* copy = swf_NewFilter(original->type);
+       switch (original->type)
+       {
+               case FILTERTYPE_BLUR:
+                       memcpy(copy, original, sizeof(FILTER_BLUR));
+                       break;
+               case FILTERTYPE_GRADIENTGLOW:
+                       memcpy(copy, original, sizeof(FILTER_GRADIENTGLOW));
+                       break;
+               case FILTERTYPE_DROPSHADOW:
+                       memcpy(copy, original, sizeof(FILTER_DROPSHADOW)); 
+                       break;
+               case FILTERTYPE_BEVEL:
+                       memcpy(copy, original, sizeof(FILTER_BEVEL)); 
+                       break;
+               default: printf("unsupported filterype");
+       }
+       return copy;
+}
+
+FILTER* changeFilter_value(changeFilter_t* first, U16 frame)
+{
+       changeFilter_t* previous = first;
+       while (first && first->frame < frame)
+       {
+               previous = first;
+               first = first->next;
+       }
+       if (!first)
+               return copyFilter(previous->value);
+       if (first->frame == frame)
+       {
+               FILTER* result;
+               do 
+               {
+                       result = first->value;
+                       first = first->next;
+               }
+               while (first && first->frame == frame);
+               return copyFilter(result);
+       }
+       switch (first->function)
+       {
+               case CF_PUT:
+                       return copyFilter(first->value);
+               case CF_CHANGE:
+               {
+                       float fraction = (frame - previous->frame) / (float)(first->frame - previous->frame);
+                       return interpolateFilter(previous->value, first->value, fraction, first->interpolation);
+               }
+               case CF_JUMP:
+                       return copyFilter(previous->value);
+               default:
+                       return 0;
+       }
+}
+
+history_t* history_new()
+{
+       history_t* newHistory = (history_t*)malloc(sizeof(history_t));
+       history_init(newHistory);
+       return newHistory;
+}
+
+void history_free(history_t* past)
+{
+       change_free(dictionary_lookup(past->changes, "x"));
+       change_free(dictionary_lookup(past->changes, "y"));
+       change_free(dictionary_lookup(past->changes, "scalex"));
+       change_free(dictionary_lookup(past->changes, "scaley"));
+       change_free(dictionary_lookup(past->changes, "cxform.r0"));
+       change_free(dictionary_lookup(past->changes, "cxform.g0"));
+       change_free(dictionary_lookup(past->changes, "cxform.b0"));
+       change_free(dictionary_lookup(past->changes, "cxform.a0"));
+       change_free(dictionary_lookup(past->changes, "cxform.r1"));
+       change_free(dictionary_lookup(past->changes, "cxform.g1"));
+       change_free(dictionary_lookup(past->changes, "cxform.b1"));
+       change_free(dictionary_lookup(past->changes, "cxform.a1"));
+       change_free(dictionary_lookup(past->changes, "rotate"));
+       change_free(dictionary_lookup(past->changes, "shear"));
+       change_free(dictionary_lookup(past->changes, "pivot.x"));
+       change_free(dictionary_lookup(past->changes, "pivot.y"));
+       change_free(dictionary_lookup(past->changes, "pin.x"));
+       change_free(dictionary_lookup(past->changes, "pin.y"));
+       change_free(dictionary_lookup(past->changes, "blendmode"));
+       changeFilter_free(dictionary_lookup(past->changes, "filter"));
+       dictionary_destroy(past->changes);
+       free(past);
+}
+
+void history_init(history_t* past)
+{
+       past->changes = (dictionary_t*)malloc(sizeof(dictionary_t));
+       dictionary_init(past->changes);
+}
+
+void history_begin(history_t* past, char* parameter, U16 frame, TAG* tag, float value)
+{
+       change_t* first = change_new(frame, CF_PUT, value, 0);
+       past->firstTag = tag;
+       past->firstFrame = frame;
+       dictionary_put2(past->changes, parameter, first);
+}
+
+void history_beginFilter(history_t* past, U16 frame, TAG* tag, FILTER* value)
+{
+       changeFilter_t* first = changeFilter_new(frame, CF_PUT, value, 0);
+       past->firstTag = tag;
+       past->firstFrame = frame;
+       dictionary_put2(past->changes, "filter", first);
+}
+
+void history_remember(history_t* past, char* parameter, U16 frame, int function, float value, interpolation_t* inter)
+{
+       change_t* first = dictionary_lookup(past->changes, parameter);
+       if (first) //should always be true
+       {
+               change_t* next = change_new(frame, function, value, inter);
+               change_append(first, next);
+       }
+}
+
+void history_rememberFilter(history_t* past, U16 frame, int function, FILTER* value, interpolation_t* inter)
+{
+       changeFilter_t* first = dictionary_lookup(past->changes, "filter");
+       if (first) //should always be true
+       {
+               changeFilter_t* next = changeFilter_new(frame, function, value, inter);
+               changeFilter_append(first, next);
+       }
+}
+
+float history_value(history_t* past, U16 frame, char* parameter)
+{
+       change_t* first = dictionary_lookup(past->changes, parameter);
+       if (first)      //should always be true.
+               return change_value(first, frame);
+       printf("no history found for parameter %s\n", parameter);
+       return 0;
+}
+
+FILTER* history_valueFilter(history_t* past, U16 frame)
+{
+       changeFilter_t* first = dictionary_lookup(past->changes, "filter");
+       if (first)      //should always be true.
+               return changeFilter_value(first, frame);
+       printf("no history found for parameter filter\n");
+       return 0;
+}
diff --git a/src/swfc-history.h b/src/swfc-history.h
new file mode 100644 (file)
index 0000000..5ced477
--- /dev/null
@@ -0,0 +1,103 @@
+/* swfc- Compiles swf code (.sc) files into .swf files.
+
+   Part of the swftools package.
+
+   Copyright (c) 2007 Huub Schaeks <huub@h-schaeks.speedlinq.nl>
+   Copyright (c) 2007 Matthias Kramm <kramm@quiss.org>
+   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 */
+
+#ifndef __HISTORY_H
+#define __HISTORY_Y
+
+#include "types.h"
+#include "rfxswf.h"
+#include "q.h"
+#include "feedback.h"
+#include "interpolation.h"
+
+enum
+{
+       CF_PUT = 1,
+       CF_CHANGE = 2,
+       CF_QCHANGE = 3,
+       CF_ARCCHANGE = 4,
+       CF_JUMP = 5     
+};
+
+#define SF_X 0x0001
+#define SF_Y 0x0002
+#define SF_SCALEX 0x0004
+#define SF_SCALEY 0x0008
+#define SF_CX_R 0x0010
+#define SF_CX_G 0x0020
+#define SF_CX_B 0x0040
+#define SF_CX_A 0x0080
+#define SF_ROTATE 0x0100
+#define SF_SHEAR 0x0200
+#define SF_PIVOT 0x0400
+#define SF_PIN 0x0800
+#define SF_BLEND 0x01000
+#define SF_FILTER 0x02000
+
+
+typedef struct _change
+{
+       U16 frame;
+       float value;
+       int function;
+       interpolation_t* interpolation;
+       struct _change* next;
+} change_t;
+
+change_t* change_new(U16 frame, int function, float value, interpolation_t* inter);
+void change_free(change_t* change);
+void change_init(change_t* change);
+void change_append(change_t* first, change_t* newChange);
+float change_value(change_t* first, U16 frame);
+
+typedef struct _changeFilter
+{
+       U16 frame;
+       FILTER* value;
+       int function;
+       interpolation_t* interpolation;
+       struct _changeFilter* next;
+} changeFilter_t;
+
+changeFilter_t* changeFilter_new(U16 frame, int function, FILTER* value, interpolation_t* inter);
+void changeFilter_free(changeFilter_t* change);
+void changeFilter_init(changeFilter_t* change);
+void changeFilter_append(changeFilter_t* first, changeFilter_t* newChange);
+FILTER* changeFilter_value(changeFilter_t* first, U16 frame);
+
+typedef struct _history
+{
+       U16 firstFrame;
+       TAG* firstTag;
+       dictionary_t* changes;
+} history_t;
+
+history_t* history_new();
+void history_free(history_t* past);
+void history_init(history_t* past);
+void history_begin(history_t* past, char* parameter, U16 frame, TAG* tag, float value);
+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);
+float history_value(history_t* past, U16 frame, char* parameter);
+FILTER* history_valueFilter(history_t* past, U16 frame);
+
+#endif
diff --git a/src/swfc-interpolation.c b/src/swfc-interpolation.c
new file mode 100644 (file)
index 0000000..17759e7
--- /dev/null
@@ -0,0 +1,301 @@
+/* swfc- Compiles swf code (.sc) files into .swf files.
+
+   Part of the swftools package.
+
+   Copyright (c) 2007 Huub Schaeks <huub@h-schaeks.speedlinq.nl>
+   Copyright (c) 2007 Matthias Kramm <kramm@quiss.org>
+   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 <stdlib.h>
+#include <math.h>
+#include <memory.h>
+#include "interpolation.h"
+
+static inline float poly(float fraction, float start, float delta, int degree)
+{
+       return delta * pow(fraction, degree) + start;
+}
+
+float linear(float fraction, float start, float delta)
+{
+       return poly(fraction, start, delta, 1);
+}
+
+float quadIn(float fraction, float start, float delta)
+{
+       return poly(fraction, start, delta, 2);
+}
+
+float quadOut(float fraction, float start, float delta)
+{
+       return quadIn(1 - fraction, start + delta, -delta);
+}
+
+float quadInOut(float fraction, float start, float delta)
+{
+       if (fraction < 0.5)
+               return quadIn(2 * fraction, start, delta / 2);
+       return quadOut(2 * fraction - 1, start + delta / 2, delta / 2);
+}
+
+float cubicIn(float fraction, float start, float delta)
+{
+       return poly(fraction, start, delta, 3);
+}
+
+float cubicOut(float fraction, float start, float delta)
+{
+       return cubicIn(1 - fraction, start + delta, -delta);
+}
+
+float cubicInOut(float fraction, float start, float delta)
+{
+       if (fraction < 0.5)
+               return cubicIn(2 * fraction, start, delta / 2);
+       return cubicOut(2 * fraction - 1, start + delta / 2, delta / 2);
+}
+
+float quartIn(float fraction, float start, float delta)
+{
+       return poly(fraction, start, delta, 4);
+}
+
+float quartOut(float fraction, float start, float delta)
+{
+       return quartIn(1 - fraction, start + delta, -delta);
+}
+
+float quartInOut(float fraction, float start, float delta)
+{
+       if (fraction < 0.5)
+               return quartIn(2 * fraction, start, delta / 2);
+       return quartOut(2 * fraction - 1, start + delta / 2, delta / 2);
+}
+
+float quintIn(float fraction, float start, float delta)
+{
+       return poly(fraction, start, delta, 5);
+}
+
+float quintOut(float fraction, float start, float delta)
+{
+       return quintIn(1 - fraction, start + delta, -delta);
+}
+
+float quintInOut(float fraction, float start, float delta)
+{
+       if (fraction < 0.5)
+               return quintIn(2 * fraction, start, delta / 2);
+       return quintOut(2 * fraction - 1, start + delta / 2, delta / 2);
+}
+
+float circleIn(float fraction, float start, float delta)
+{
+       return delta * (1 - sqrt(1 - fraction * fraction)) + start;
+}
+
+float circleOut(float fraction, float start, float delta)
+{
+       return circleIn(1 - fraction, start + delta, -delta);
+}
+
+float circleInOut(float fraction, float start, float delta)
+{
+       if (fraction < 0.5)
+               return circleIn(2 * fraction, start, delta / 2);
+       return circleOut(2 * fraction - 1, start + delta / 2, delta / 2);
+}
+
+float exponentialIn(float fraction, float start, float delta)
+{
+       if (fraction == 0)
+               return start;
+       return delta * pow(2, 10 * (fraction - 1)) + start;
+}
+
+float exponentialOut(float fraction, float start, float delta)
+{
+       return exponentialIn(1 - fraction, start + delta, -delta);
+}
+
+float exponentialInOut(float fraction, float start, float delta)
+{
+       if (fraction < 0.5)
+               return exponentialIn(2 * fraction, start, delta / 2);
+       return exponentialOut(2 * fraction - 1, start + delta / 2, delta / 2);
+}
+
+float sineIn(float fraction, float start, float delta)
+{
+       return delta * (1 - cos(fraction * PI/2)) + start;
+}
+
+float sineOut(float fraction, float start, float delta)
+{
+       return sineIn(1 - fraction, start + delta, -delta);
+}
+
+float sineInOut(float fraction, float start, float delta)
+{
+       if (fraction < 0.5)
+               return sineIn(2 * fraction, start, delta / 2);
+       return sineOut(2 * fraction - 1, start + delta / 2, delta / 2);
+}
+
+float elasticIn(float fraction, float start, float delta, float amplitude, int bounces, float damping)
+{
+       if (fraction == 0 || delta == 0)
+               return start;
+       if (fraction == 1)
+               return start + delta;
+       if (amplitude < fabs(delta))
+               amplitude = delta;
+       float period = 1 / (bounces + 0.25);
+//     float s = asin(delta / amplitude) - 2 * PI / period;
+       return amplitude * pow(2, damping * (fraction - 1)) * sin(fraction * (2 * PI) / period /*+ fraction * s*/) + start;
+}
+
+float elasticOut(float fraction, float start, float delta, float amplitude, int bounces, float damping)
+{
+       return elasticIn(1 - fraction, start + delta, -delta, amplitude, bounces, damping);
+}
+       
+float elasticInOut(float fraction, float start, float delta, float amplitude, int bounces, float damping)
+{
+       if (fraction < 0.5)
+               return elasticIn(2 * fraction, start, delta / 2, amplitude, bounces, damping);
+       return elasticOut(2 * fraction - 1, start + delta / 2, delta / 2, amplitude, bounces, damping);
+}
+
+float backIn(float fraction, float start, float delta, float speed)
+{
+       return delta * fraction * fraction * ((speed + 1) * fraction - speed) + start;
+}
+
+float backOut(float fraction, float start, float delta, float speed)
+{
+       return backIn(1 - fraction, start + delta, -delta, speed);
+}
+
+float backInOut(float fraction, float start, float delta, float speed)
+{
+       if (fraction < 0.5)
+               return backIn(2 * fraction, start, delta / 2, speed);
+       return backOut(2 * fraction - 1, start + delta / 2, delta / 2, speed);
+}
+
+/* when applied to movement bounceIn the object 'hits the floor' bounces times 
+ * (after leaving the floor first) before gently reaching the final position at the top of the final bounce
+ * Each bounce takes growth times a long as the previous, except for the last one which lasts only half 
+ * that time. The heights of the intermediate bounces are determined by the damping parameter.
+ * Set damping to 0 for an undamped movement.*/
+
+float bounceIn(float fraction, float start, float delta, int bounces, float growth, float damping)
+{
+       if (fraction == 0 || delta == 0)
+               return start;
+       if (fraction == 1)
+               return start + delta;
+       float w0;
+       if (growth == 1.0)
+               w0 = 1 / (bounces + 0.5);
+       else
+       {
+               float gN = pow(growth, bounces);
+               w0 = 1 / ((gN - 1) / (growth - 1) + gN / 2 );
+       }
+       float bounceStart = 0;
+       int i;
+       float w = w0;
+       for (i = 0; i <= bounces; i++)
+       {
+               float bounceEnd = bounceStart + w;
+               if (fraction >= bounceStart && fraction < bounceEnd)
+               {
+                       float half = (bounceEnd + bounceStart) / 2;
+                       float top = delta / pow(2, damping * ((bounces - i)));
+                       fraction -= half;
+                       fraction /= (w / 2);
+                       return (1 - fraction * fraction) * top + start;
+               }
+               bounceStart = bounceEnd;
+               w = w * growth;
+       }
+}
+
+/* bounceOut is a time-reversed bounceIn; therefore each bounce takes 1/growth times as long as
+ * the previous, which I think fits the idea when applied to movement */
+
+float bounceOut(float fraction, float start, float delta, int bounces, float growth, float damping)
+{
+       return bounceIn(1 - fraction, start + delta, -delta, bounces, growth, damping);
+}
+
+/* since bounceIn and bounceOut are combined, if growth > 1 then the bounce-times will increase in
+ * the first half and decrease in the second half */
+
+float bounceInOut(float fraction, float start, float delta, int bounces, float growth, float damping)
+{
+       if (fraction < 0.5)
+               return bounceIn(2 * fraction, start, delta / 2, bounces, growth, damping);
+       return bounceOut(2 * fraction - 1, start + delta / 2, delta / 2, bounces, growth, damping);
+}
+/* fastBounce(In/Out) doesn't end or start in a horizontal slope (= gentle end or start) as
+ * bounce(In/Out) do which means fastBounceInOut doesn't have the 'delay' in the middle */
+float fastBounceIn(float fraction, float start, float delta, int bounces, float growth, float damping)
+{
+       if (fraction == 0 || delta == 0)
+               return start;
+       if (fraction == 1)
+               return start + delta;
+       float w0;
+       if (growth == 1.0)
+               w0 = 1 / (bounces + 0.25); /* in general (bounces + 1 / (2 * f)) */
+       else
+       {
+               float gN = pow(growth, bounces);
+               w0 = 1 / ((gN - 1) / (growth - 1) + gN / 4 /* in general: gN / (2 * f) */ );
+       }
+       float bounceStart = 0;
+       int i;
+       float w = w0;
+       for (i = 0; i <= bounces; i++)
+       {
+               float bounceEnd = bounceStart + w;
+               if (fraction >= bounceStart && fraction < bounceEnd)
+               {
+                       float half = (bounceEnd + bounceStart) / 2;
+                       float top = delta / 0.75/* in general: (1 - (1 / f) * (1 / f)) */ / pow(2, damping * ((bounces - i)));
+                       fraction -= half;
+                       fraction /= (w / 2);
+                       return (1 - fraction * fraction) * top + start;
+               }
+               bounceStart = bounceEnd;
+               w = w * growth;
+       }
+}
+
+float fastBounceOut(float fraction, float start, float delta, int bounces, float growth, float damping)
+{
+       return fastBounceIn(1 - fraction, start + delta, -delta, bounces, growth, damping);
+}
+
+float fastBounceInOut(float fraction, float start, float delta, int bounces, float growth, float damping)
+{
+       if (fraction < 0.5)
+               return fastBounceIn(2 * fraction, start, delta / 2, bounces, growth, damping);
+       return fastBounceOut(2 * fraction - 1, start + delta / 2, delta / 2, bounces, growth, damping);
+}
diff --git a/src/swfc-interpolation.h b/src/swfc-interpolation.h
new file mode 100644 (file)
index 0000000..173ae9b
--- /dev/null
@@ -0,0 +1,116 @@
+/* swfc- Compiles swf code (.sc) files into .swf files.
+
+   Part of the swftools package.
+
+   Copyright (c) 2007 Huub Schaeks <huub@h-schaeks.speedlinq.nl>
+   Copyright (c) 2007 Matthias Kramm <kramm@quiss.org>
+   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 */
+
+#ifndef __EASE_H
+#define __EASE_H
+
+typedef struct _interpolation {
+       int function;
+       float speed, amplitude, growth, damping;
+       int bounces;
+} interpolation_t;
+
+enum {
+       IF_LINEAR = 1,
+       IF_QUAD_IN = 2,
+       IF_QUAD_OUT = 3,
+       IF_QUAD_IN_OUT = 4,
+       IF_CUBIC_IN = 5,
+       IF_CUBIC_OUT = 6,
+       IF_CUBIC_IN_OUT = 7,
+       IF_QUART_IN = 8,
+       IF_QUART_OUT = 9,
+       IF_QUART_IN_OUT = 10,
+       IF_QUINT_IN = 11,
+       IF_QUINT_OUT = 12,
+       IF_QUINT_IN_OUT = 13,
+       IF_CIRCLE_IN = 14,
+       IF_CIRCLE_OUT = 15,
+       IF_CIRCLE_IN_OUT = 16,
+       IF_EXPONENTIAL_IN = 17,
+       IF_EXPONENTIAL_OUT = 18,
+       IF_EXPONENTIAL_IN_OUT = 19,
+       IF_SINE_IN = 20,
+       IF_SINE_OUT = 21,
+       IF_SINE_IN_OUT = 22,
+       IF_ELASTIC_IN = 23,
+       IF_ELASTIC_OUT = 24,
+       IF_ELASTIC_IN_OUT = 25,
+       IF_BACK_IN = 26,
+       IF_BACK_OUT = 27,
+       IF_BACK_IN_OUT = 28,
+       IF_BOUNCE_IN = 29,
+       IF_BOUNCE_OUT = 30,
+       IF_BOUNCE_IN_OUT = 31,
+       IF_FAST_BOUNCE_IN = 32,
+       IF_FAST_BOUNCE_OUT = 33,
+       IF_FAST_BOUNCE_IN_OUT = 34
+};
+
+#define PI 3.14159265358979
+
+float linear(float fraction, float start, float delta);
+
+float quadIn(float fraction, float start, float delta);
+float quadOut(float fraction, float start, float delta);
+float quadInOut(float fraction, float start, float delta);
+
+float cubicIn(float fraction, float start, float delta);
+float cubicOut(float fraction, float start, float delta);
+float cubicInOut(float fraction, float start, float delta);
+
+float quartIn(float fraction, float start, float delta);
+float quartOut(float fraction, float start, float delta);
+float quartInOut(float fraction, float start, float delta);
+
+float quintIn(float fraction, float start, float delta);
+float quintOut(float fraction, float start, float delta);
+float quintInOut(float fraction, float start, float delta);
+
+float circleIn(float fraction, float start, float delta);
+float circleOut(float fraction, float start, float delta);
+float circleInOut(float fraction, float start, float delta);
+
+float exponentialIn(float fraction, float start, float delta);
+float exponentialOut(float fraction, float start, float delta);
+float exponentialInOut(float fraction, float start, float delta);
+
+float sineIn(float fraction, float start, float delta);
+float sineOut(float fraction, float start, float delta);
+float sineInOut(float fraction, float start, float delta);
+
+float elasticIn(float fraction, float start, float delta, float amplitude, int bounces, float damping);
+float elasticOut(float fraction, float start, float delta, float amplitude, int bounces, float damping);
+float elasticInOut(float fraction, float start, float delta, float amplitude, int bounces, float damping);
+
+float backIn(float fraction, float start, float delta, float speed);
+float backOut(float fraction, float start, float delta, float speed);
+float backInOut(float fraction, float start, float delta, float speed);
+
+float bounceIn(float fraction, float start, float delta, int bounces, float growth, float damping);
+float bounceOut(float fraction, float start, float delta, int bounces, float growth, float damping);
+float bounceInOut(float fraction, float start, float delta, int bounces, float growth, float damping);
+
+float fastBounceIn(float fraction, float start, float delta, int bounces, float growth, float damping);
+float fastBounceOut(float fraction, float start, float delta, int bounces, float growth, float damping);
+float fastBounceInOut(float fraction, float start, float delta, int bounces, float growth, float damping);
+
+#endif
index 34e731e..f4d55da 100644 (file)
 #include "../lib/wav.h"
 #include "parser.h"
 #include "../lib/png.h"
+#include "../lib/interpolation.h"
+#include "../lib/history.h"
 
 //#define DEBUG
-
-static char * filename = 0;
 static char * outputname = "output.swf";
 static int verbose = 2;
 static int optimize = 0;
@@ -116,29 +116,6 @@ static int pos;
 static char*text;
 static int textlen;
 static int type;
-static int line;
-static int column;
-
-static void syntaxerror(char*format, ...)
-{
-    char buf[1024];
-    va_list arglist;
-    va_start(arglist, format);
-    vsprintf(buf, format, arglist);
-    va_end(arglist);
-    fprintf(stderr, "\"%s\", line %d column %d: error- %s\n", filename, line, column, buf);
-    exit(1);
-}
-
-static void warning(char*format, ...)
-{
-    char buf[1024];
-    va_list arglist;
-    va_start(arglist, format);
-    vsprintf(buf, format, arglist);
-    va_end(arglist);
-    fprintf(stderr, "\"%s\", line %d column %d: warning- %s\n", filename, line, column, buf);
-}
 
 static void readToken()
 {
@@ -204,6 +181,7 @@ static dictionary_t textures;
 static dictionary_t outlines;
 static dictionary_t gradients;
 static dictionary_t filters;
+static dictionary_t interpolations;
 static char idmap[65536];
 static TAG*tag = 0; //current tag
 
@@ -214,6 +192,7 @@ static U16 currentdepth;
 static dictionary_t instances;
 static dictionary_t fonts;
 static dictionary_t sounds;
+static dictionary_t fontUsage;
 
 typedef struct _parameters {
     int x,y; 
@@ -225,6 +204,7 @@ typedef struct _parameters {
     SPOINT pin;
     U8 blendmode; //not interpolated
     FILTER*filter;
+    U16 set; // bits indicating wether a parameter was set in the c_placement function
 } parameters_t;
 
 typedef struct _character {
@@ -239,6 +219,7 @@ typedef struct _instance {
     parameters_t parameters;
     TAG* lastTag; //last tag which set the object
     U16 lastFrame; //frame lastTag is in
+    history_t* history;
 } instance_t;
 
 typedef struct _outline {
@@ -260,10 +241,49 @@ typedef struct _texture {
     FILLSTYLE fs;
 } texture_t;
 
+char* interpolationFunctions[] = {"linear", \
+        "quadIn", "quadOut", "quadInOut", \
+        "cubicIn", "cubicOut", "cubicInOut", \
+        "quartIn", "quartOut", "quartInOut", \
+        "quintIn", "quintOut", "quintInOut", \
+        "circleIn", "circleOut", "circleInOut", \
+        "exponentialIn", "exponentialOut", "exponentialInOut", \
+        "sineIn", "sineOut", "sineInOut", \
+        "elasticIn", "elasticOut", "elasticInOut", \
+        "backIn", "backOut", "backInOut", \
+        "bounceIn", "bounceOut", "bounceInOut", \
+        "fastBounceIn", "fastBounceOut", "fastBounceInOut"};
+
+typedef struct _fontData {
+    char *glyphs;
+    int notUsed, needsAll;
+} fontData;
+
+void addFontData(char *name)
+{
+    fontData* newFont;
+    newFont = (fontData *)malloc(sizeof(fontData));
+    memset(newFont, 0, sizeof(fontData));
+    newFont->notUsed = 1;
+    dictionary_put2(&fontUsage, name, newFont);
+}
+
+void freeFontData(fontData* font)
+{
+    free(font->glyphs);
+    free(font);
+}
+
+fontData *getFontData(char *name)
+{
+    return (fontData *)dictionary_lookup(&fontUsage, name);
+}
+
 static void character_init(character_t*c)
 {
     memset(c, 0, sizeof(character_t));
 }
+
 static character_t* character_new()
 {
     character_t*c;
@@ -271,10 +291,19 @@ static character_t* character_new()
     character_init(c);
     return c;
 }
+
 static void instance_init(instance_t*i)
 {
     memset(i, 0, sizeof(instance_t));
+    i->history = history_new();
 }
+
+static void instance_free(instance_t* i)
+{
+    history_free(i->history);
+    free(i);
+}
+
 static instance_t* instance_new()
 {
     instance_t*c;
@@ -283,6 +312,60 @@ static instance_t* instance_new()
     return c;
 }
 
+static void free_instance(void* i)
+{
+    instance_free((instance_t*)i);
+}
+
+static void free_font(void* f)
+{
+    swf_FontFree((SWFFONT*)f);
+}
+
+static void free_fontData(void* fd)
+{
+    freeFontData((fontData*)fd);
+}
+
+static void gradient_free(GRADIENT* grad)
+{
+    free(grad->ratios);
+    free(grad->rgba);
+    free(grad);
+}
+
+static void free_gradient(void* grad)
+{
+    gradient_free((GRADIENT*) grad);
+}
+
+static void outline_free(outline_t* o)
+{
+    free(o->shape->data);
+    free(o->shape);
+    free(o);
+}
+
+static void free_outline(void* o)
+{
+    outline_free((outline_t*)o);
+}
+
+static void freeDictionaries()
+{
+   dictionary_free_all(&instances, free_instance);
+   dictionary_free_all(&characters, free);
+    dictionary_free_all(&images, free);
+    dictionary_free_all(&textures, free);
+    dictionary_free_all(&outlines, free_outline);
+    dictionary_free_all(&gradients, free_gradient);
+    dictionary_free_all(&filters, free);
+    dictionary_free_all(&fonts, free_font);
+    dictionary_free_all(&sounds, free);
+    dictionary_free_all(&fontUsage, free_fontData);
+    dictionary_free_all(&interpolations, free);
+}
+
 static void incrementid()
 {
     while(idmap[++id]) {
@@ -294,13 +377,13 @@ static void incrementid()
 
 static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
 {
+    if(dictionary_lookup(&characters, name))
+        syntaxerror("character %s defined twice", name);
     character_t* c = character_new();
     
     c->definingTag = ctag;
     c->id = id;
     c->size = r;
-    if(dictionary_lookup(&characters, name))
-       syntaxerror("character %s defined twice", name);
     dictionary_put2(&characters, name, c);
 
     tag = swf_InsertTag(tag, ST_NAMECHARACTER);
@@ -313,23 +396,23 @@ static void s_addcharacter(char*name, U16 id, TAG*ctag, SRECT r)
 }
 static void s_addimage(char*name, U16 id, TAG*ctag, SRECT r)
 {
+    if(dictionary_lookup(&images, name))
+        syntaxerror("image %s defined twice", name);
+        
     character_t* c = character_new();
     c->definingTag = ctag;
     c->id = id;
     c->size = r;
-
-    if(dictionary_lookup(&images, name))
-       syntaxerror("image %s defined twice", name);
     dictionary_put2(&images, name, c);
 }
 static instance_t* s_addinstance(char*name, character_t*c, U16 depth)
 {
+    if(dictionary_lookup(&instances, name))
+        syntaxerror("object %s defined twice", name);
     instance_t* i = instance_new();
     i->character = c;
     i->depth = depth;
     //swf_GetMatrix(0, &i->matrix);
-    if(dictionary_lookup(&instances, name))
-       syntaxerror("object %s defined twice", name);
     dictionary_put2(&instances, name, i);
     return i;
 }
@@ -357,10 +440,10 @@ static void makeMatrix(MATRIX*m, parameters_t*p)
      *       \r0 sy/ \y/
      */
 
-    sx =  p->scalex*cos(p->rotate/360*2*3.14159265358979);
-    r1 = -p->scalex*sin(p->rotate/360*2*3.14159265358979)+sx*p->shear;
-    r0 =  p->scaley*sin(p->rotate/360*2*3.14159265358979);
-    sy =  p->scaley*cos(p->rotate/360*2*3.14159265358979)+r0*p->shear;
+    sx =  p->scalex*cos(p->rotate/360*2*PI);
+    r1 = -p->scalex*sin(p->rotate/360*2*PI)+sx*p->shear;
+    r0 =  p->scaley*sin(p->rotate/360*2*PI);
+    sy =  p->scaley*cos(p->rotate/360*2*PI)+r0*p->shear;
 
     m->sx = (int)(sx*65536+0.5);
     m->r1 = (int)(r1*65536+0.5);
@@ -388,12 +471,92 @@ static MATRIX s_instancepos(SRECT rect, parameters_t*p)
     return m;
 }
 
+void builtInInterpolations()
+{
+    interpolation_t* new;
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_LINEAR;
+    dictionary_put2(&interpolations, "linear", new);
+
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUAD_IN;
+    dictionary_put2(&interpolations, "quadIn", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUAD_OUT;
+    dictionary_put2(&interpolations, "quadOut", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUAD_IN_OUT;
+    dictionary_put2(&interpolations, "quadInOut", new);
+
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_CUBIC_IN;
+    dictionary_put2(&interpolations, "cubicIn", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_CUBIC_OUT;
+    dictionary_put2(&interpolations, "cubicOut", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_CUBIC_IN_OUT;
+    dictionary_put2(&interpolations, "cubicInOut", new);
+
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUART_IN;
+    dictionary_put2(&interpolations, "quartIn", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUART_OUT;
+    dictionary_put2(&interpolations, "quartOut", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUART_IN_OUT;
+    dictionary_put2(&interpolations, "quartInOut", new);
+
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUINT_IN;
+    dictionary_put2(&interpolations, "quintIn", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUINT_OUT;
+    dictionary_put2(&interpolations, "quintOut", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_QUINT_IN_OUT;
+    dictionary_put2(&interpolations, "quintInOut", new);
+
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_CIRCLE_IN;
+    dictionary_put2(&interpolations, "circleIn", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_CIRCLE_OUT;
+    dictionary_put2(&interpolations, "circleOut", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_CIRCLE_IN_OUT;
+    dictionary_put2(&interpolations, "circleInOut", new);
+
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_EXPONENTIAL_IN;
+    dictionary_put2(&interpolations, "exponentialIn", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_EXPONENTIAL_OUT;
+    dictionary_put2(&interpolations, "exponentialOut", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_EXPONENTIAL_IN_OUT;
+    dictionary_put2(&interpolations, "exponentialInOut", new);
+
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_SINE_IN;
+    dictionary_put2(&interpolations, "sineIn", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_SINE_OUT;
+    dictionary_put2(&interpolations, "sineOut", new);
+    new = (interpolation_t*)malloc(sizeof(interpolation_t));
+    new->function = IF_SINE_IN_OUT;
+    dictionary_put2(&interpolations, "sineInOut", new);
+}
+
 void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA background)
 {
-    SWF*swf = (SWF*)malloc(sizeof(SWF));
-
     if(stackpos)
-       syntaxerror(".swf blocks can't be nested");
+        syntaxerror(".swf blocks can't be nested");
+    if(stackpos==sizeof(stack)/sizeof(stack[0]))
+        syntaxerror("too many levels of recursion");
+        
+    SWF*swf = (SWF*)malloc(sizeof(SWF));
 
     memset(swf, 0, sizeof(swf));
     swf->fileVersion = version;
@@ -402,9 +565,6 @@ void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA backgrou
     swf->firstTag = tag = swf_InsertTag(0, ST_SETBACKGROUNDCOLOR);
     swf->compressed = compress;
     swf_SetRGB(tag,&background);
-
-    if(stackpos==sizeof(stack)/sizeof(stack[0]))
-       syntaxerror("too many levels of recursion");
     
     dictionary_init(&characters);
     dictionary_init(&images);
@@ -415,6 +575,9 @@ void s_swf(char*name, SRECT r, int version, int fps, int compress, RGBA backgrou
     dictionary_init(&instances);
     dictionary_init(&fonts);
     dictionary_init(&sounds);
+    dictionary_init(&interpolations);
+    builtInInterpolations();
+    cleanUp = &freeDictionaries;
 
     memset(&stack[stackpos], 0, sizeof(stack[0]));
     stack[stackpos].type = 0;
@@ -615,6 +778,89 @@ TAG* removeFromTo(TAG*from, TAG*to)
     return save;
 }
 
+static void readParameters(history_t* history, parameters_t* p, int frame)
+{
+    p->x = history_value(history, frame, "x");
+    p->y = history_value(history, frame, "y");
+    p->scalex = history_value(history, frame, "scalex");
+    p->scaley = history_value(history, frame, "scaley");
+    p->cxform.r0 = history_value(history, frame, "cxform.r0");
+    p->cxform.g0 = history_value(history, frame, "cxform.g0");
+    p->cxform.b0 = history_value(history, frame, "cxform.b0");
+    p->cxform.a0 = history_value(history, frame, "cxform.a0");
+    p->cxform.r1 = history_value(history, frame, "cxform.r1");
+    p->cxform.g1 = history_value(history, frame, "cxform.g1");
+    p->cxform.b1 = history_value(history, frame, "cxform.b1");
+    p->cxform.a1 = history_value(history, frame, "cxform.a1");
+    p->rotate = history_value(history, frame, "rotate");
+    p->shear = history_value(history, frame, "shear");
+    p->pivot.x = history_value(history, frame, "pivot.x");
+    p->pivot.y = history_value(history, frame, "pivot.y");
+    p->pin.x = history_value(history, frame, "pin.x");
+    p->pin.y = history_value(history, frame, "pin.y");
+    p->blendmode = history_value(history, frame, "blendmode");
+    p->filter = history_valueFilter(history, frame);
+}
+
+void setPlacement(TAG*tag, U16 id, U16 depth, MATRIX m, char*name, parameters_t*p, char move)
+{
+    SWFPLACEOBJECT po;
+    FILTERLIST flist;
+    swf_GetPlaceObject(NULL, &po);
+    po.id = id;
+    po.depth = depth;
+    po.matrix = m;
+    po.cxform = p->cxform;
+    po.name = name;
+    po.move = move;
+    if(move)
+    po.id = 0;
+    if(p->blendmode) {
+    po.blendmode = p->blendmode;
+    }
+    if(p->filter) {
+    flist.num = 1;
+    flist.filter[0] = p->filter;
+    po.filters = &flist;
+    }
+    swf_SetPlaceObject(tag, &po);
+}
+
+static void writeInstance(instance_t* i)
+{
+    parameters_t p;
+    MATRIX m;
+    int frame = i->history->firstFrame;
+    TAG* tag = i->history->firstTag;
+    while (frame < currentframe)
+    {
+        frame++;
+        readParameters(i->history, &p, frame);
+        while (tag->id != ST_SHOWFRAME)
+            tag = tag->next;
+        m = s_instancepos(i->character->size, &p);
+
+        if(p.blendmode || p.filter)
+            tag = swf_InsertTag(tag, ST_PLACEOBJECT3);
+        else
+            tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
+        setPlacement(tag, 0, i->depth, m, 0, &p, 1);
+        if (p.filter)
+            free(p.filter);        
+    }
+}
+
+void dumpSWF(SWF*swf)
+{
+    TAG* tag = swf->firstTag;
+    printf("vvvvvvvvvvvvvvvvvvvvv\n");
+    while(tag) {
+    printf("%8d %s\n", tag->len, swf_TagGetName(tag));
+    tag = tag->next;
+    }
+    printf("^^^^^^^^^^^^^^^^^^^^^\n");
+}
+
 static void s_endSprite()
 {
     SRECT r = currentrect;
@@ -623,24 +869,34 @@ static void s_endSprite()
        tag = removeFromTo(stack[stackpos].cut, tag);
 
     stackpos--;
-   
+    instance_t *i;
+    stringarray_t* index =dictionary_index(&instances);
+    int num = 0;
+    char* name = stringarray_at(index, num);
+    while (name)
+    {
+        i = dictionary_lookup(&instances, name);
+        writeInstance(i);
+        num++;
+        name = stringarray_at(index, num);
+    }
+    
+    tag = swf_InsertTag(tag, ST_SHOWFRAME);
+    tag = swf_InsertTag(tag, ST_END);
+
+    tag = stack[stackpos].tag;
+    swf_FoldSprite(tag);
+    if(tag->next != 0)
+        syntaxerror("internal error(7)");
     /* TODO: before clearing, prepend "<spritename>." to names and
              copy into old instances dict */
-    dictionary_clear(&instances);
+   dictionary_free_all(&instances, free_instance);
 
     currentframe = stack[stackpos].oldframe;
     currentrect = stack[stackpos].oldrect;
     currentdepth = stack[stackpos].olddepth;
     instances = stack[stackpos].oldinstances;
 
-    tag = swf_InsertTag(tag, ST_SHOWFRAME);
-    tag = swf_InsertTag(tag, ST_END);
-
-    tag = stack[stackpos].tag;
-    swf_FoldSprite(tag);
-    if(tag->next != 0)
-       syntaxerror("internal error(7)");
-
     s_addcharacter(stack[stackpos].name, stack[stackpos].id, stack[stackpos].tag, r);
     free(stack[stackpos].name);
 }
@@ -651,6 +907,18 @@ static void s_endSWF()
     SWF* swf;
     char*filename;
     
+    instance_t *i;
+    stringarray_t* index =dictionary_index(&instances);
+    int num = 0;
+    char* name = stringarray_at(index, num);
+    while (name)
+    {
+        i = dictionary_lookup(&instances, name);
+        writeInstance(i);
+        num++;
+        name = stringarray_at(index, num);
+    }
+
     if(stack[stackpos].cut)
        tag = removeFromTo(stack[stackpos].cut, tag);
 
@@ -696,16 +964,8 @@ static void s_endSWF()
        {if(swf_WriteSWF(fi, swf)<0) syntaxerror("WriteSWF() failed.\n");}
 
     close(fi);
-    
-    dictionary_clear(&instances);
-    dictionary_clear(&characters);
-    dictionary_clear(&images);
-    dictionary_clear(&textures);
-    dictionary_clear(&outlines);
-    dictionary_clear(&gradients); // mem leak
-    dictionary_clear(&filters);
-    dictionary_clear(&fonts);
-    dictionary_clear(&sounds);
+
+    freeDictionaries();
 
     swf_FreeTags(swf);
     free(swf);
@@ -790,8 +1050,8 @@ int addFillStyle(SHAPE*s, SRECT*r, char*name)
        MATRIX rot,m;
        double ccos,csin;
        swf_GetMatrix(0, &rot);
-       ccos = cos(-gradient->rotate*2*3.14159265358979/360);
-       csin = sin(-gradient->rotate*2*3.14159265358979/360);
+    ccos = cos(-gradient->rotate*2*PI/360);
+    csin = sin(-gradient->rotate*2*PI/360);
        rot.sx =  ccos*65536;
        rot.r1 = -csin*65536;
        rot.r0 =  csin*65536;
@@ -1086,6 +1346,7 @@ void s_image(char*name, char*type, char*filename, int quality)
        tag = swf_InsertTag(tag, ST_DEFINEBITSLOSSLESS);
        swf_SetU16(tag, imageID);
        swf_SetLosslessImage(tag, data, width, height);
+    free(data);
 
        r.xmin = 0;
        r.ymin = 0;
@@ -1133,6 +1394,8 @@ void s_getBitmapSize(char*name, int*width, int*height)
 
 void s_texture(char*name, char*object, int x, int y, float scalex, float scaley, float rotate, float shear)
 {
+    if(dictionary_lookup(&textures, name))
+        syntaxerror("texture %s defined twice", name);
     gradient_t* gradient = dictionary_lookup(&gradients, object);
     character_t* bitmap = dictionary_lookup(&images, object);
     texture_t* texture = (texture_t*)rfx_calloc(sizeof(texture_t));
@@ -1166,27 +1429,16 @@ void s_texture(char*name, char*object, int x, int y, float scalex, float scaley,
        fs->m.sy *= 20;
     }
 
-
-    if(dictionary_lookup(&textures, name))
-       syntaxerror("texture %s defined twice", name);
     dictionary_put2(&textures, name, texture);
 }
 
-void dumpSWF(SWF*swf)
-{
-    TAG* tag = swf->firstTag;
-    printf("vvvvvvvvvvvvvvvvvvvvv\n");
-    while(tag) {
-       printf("%8d %s\n", tag->len, swf_TagGetName(tag));
-       tag = tag->next;
-    }
-    printf("^^^^^^^^^^^^^^^^^^^^^\n");
-}
-    
-void s_font(char*name, char*filename)
+void s_font(char*name, char*filename, char *glyphs)
 {
+    if(dictionary_lookup(&fonts, name))
+        syntaxerror("font %s defined twice", name);
+        
     SWFFONT* font;
-    font = swf_LoadFont(filename);
+    font = swf_LoadFont(filename, glyphs);
    
     if(font == 0) {
        warning("Couldn't open font file \"%s\"", filename);
@@ -1218,8 +1470,6 @@ void s_font(char*name, char*filename)
     swf_SetString(tag, name);
     incrementid();
 
-    if(dictionary_lookup(&fonts, name))
-       syntaxerror("font %s defined twice", name);
     dictionary_put2(&fonts, name, font);
 }
 
@@ -1237,46 +1487,53 @@ void s_sound(char*name, char*filename)
     struct MP3 mp3;
     sound_t* sound;
     U16*samples = NULL;
-    unsigned numsamples;
+    unsigned numsamples = 1;
     unsigned blocksize = 1152;
     int is_mp3 = 0;
 
-    if(wav_read(&wav, filename)) {
+    if(dictionary_lookup(&sounds, name))
+        syntaxerror("sound %s defined twice", name);
+        
+    if(wav_read(&wav, filename))
+    {
         int t;
-       wav_convert2mono(&wav, &wav2, 44100);
-       samples = (U16*)wav2.data;
-       numsamples = wav2.size/2;
-       free(wav.data);
+        wav_convert2mono(&wav, &wav2, 44100);
+        samples = (U16*)wav2.data;
+        numsamples = wav2.size/2;
+        free(wav.data);
 #ifdef WORDS_BIGENDIAN
        /* swap bytes */
-       for(t=0;t<numsamples;t++) {
-           samples[t] = (samples[t]>>8)&0xff | (samples[t]<<8)&0xff00;
-       }
+        for(t=0;t<numsamples;t++)
+            samples[t] = (samples[t]>>8)&0xff | (samples[t]<<8)&0xff00;
 #endif
-    } else if(mp3_read(&mp3, filename)) {
-        fprintf(stderr, "\"%s\" seems to work as a MP3 file...\n", filename);
-        blocksize = 1;
-        is_mp3 = 1;
     }
     else
-    {
-       warning("Couldn't read WAV/MP3 file \"%s\"", filename);
-       samples = 0;
-       numsamples = 0;
-    }
+        if(mp3_read(&mp3, filename))
+        {
+            fprintf(stderr, "\"%s\" seems to work as a MP3 file...\n", filename);
+            blocksize = 1;
+            is_mp3 = 1;
+        }
+        else
+        {
+            warning("Couldn't read WAV/MP3 file \"%s\"", filename);
+            samples = 0;
+            numsamples = 0;
+        }
     
     if(numsamples%blocksize != 0)
     {
        // apply padding, so that block is a multiple of blocksize
-       int numblocks = (numsamples+blocksize-1)/blocksize;
-       int numsamples2;
-       U16* samples2;
-       numsamples2 = numblocks * blocksize;
-       samples2 = malloc(sizeof(U16)*numsamples2);
-       memcpy(samples2, samples, numsamples*sizeof(U16));
-       memset(&samples2[numsamples], 0, sizeof(U16)*(numsamples2 - numsamples));
-       numsamples = numsamples2;
-       samples = samples2;
+        int numblocks = (numsamples+blocksize-1)/blocksize;
+        int numsamples2;
+        U16* samples2;
+        numsamples2 = numblocks * blocksize;
+        samples2 = malloc(sizeof(U16)*numsamples2);
+        memcpy(samples2, samples, numsamples*sizeof(U16));
+        memset(&samples2[numsamples], 0, sizeof(U16)*(numsamples2 - numsamples));
+        numsamples = numsamples2;
+        free(samples);
+        samples = samples2;
     }
 
     tag = swf_InsertTag(tag, ST_DEFINESOUND);
@@ -1288,12 +1545,10 @@ void s_sound(char*name, char*filename)
                 mp3.SampRate,
                 mp3.Channels,
                 mp3.NumFrames);
-       mp3_clear(&mp3);
+        mp3_clear(&mp3);
     }
     else
-    {
         swf_SetSoundDefine(tag, samples, numsamples);
-    }
     
     tag = swf_InsertTag(tag, ST_NAMECHARACTER);
     swf_SetU16(tag, id);
@@ -1307,14 +1562,12 @@ void s_sound(char*name, char*filename)
     sound->tag = tag;
     sound->id = id;
 
-    if(dictionary_lookup(&sounds, name))
-       syntaxerror("sound %s defined twice", name);
     dictionary_put2(&sounds, name, sound);
     
     incrementid();
 
-    if(samples)
-       free(samples);
+    if (samples)
+        free(samples);
 }
 
 static char* gradient_getToken(const char**p)
@@ -1345,30 +1598,42 @@ GRADIENT parseGradient(const char*str)
     memset(&gradient, 0, sizeof(GRADIENT));
     gradient.ratios = rfx_calloc(16*sizeof(U8));
     gradient.rgba = rfx_calloc(16*sizeof(RGBA));
-    while(*p) {
-       char*posstr,*colorstr;
-       int pos;
-       RGBA color;
-       posstr = gradient_getToken(&p);
-       if(!*posstr)
-           break;
-       pos = (int)(parsePercent(posstr)*255.0);
-       if(pos == lastpos)
-           pos++;
-       if(!*p) syntaxerror("Error in shape data: Color expected after %s", posstr);
-       colorstr = gradient_getToken(&p);
-       color = parseColor(colorstr);
-       if(gradient.num == 16) {
-           warning("gradient record too big- max size is 16, rest ignored");
-           break;
+    
+    while(*p)
+    {
+        char*posstr,*colorstr;
+        int pos;
+        RGBA color;
+        posstr = gradient_getToken(&p);
+        if(!*posstr)
+        {
+            free(posstr);
+            break;
+        }
+        pos = (int)(parsePercent(posstr)*255.0);
+        if(pos == lastpos)
+            pos++;
+        if(!*p)
+        {
+            rfx_free(gradient.ratios);
+            rfx_free(gradient.rgba);
+            free(posstr);
+            syntaxerror("Error in shape data: Color expected after %s", posstr);
+        }
+        colorstr = gradient_getToken(&p);
+        color = parseColor(colorstr);
+        if(gradient.num == 16)
+        {
+            warning("gradient record too big- max size is 16, rest ignored");
+            break;
+        }
+        gradient.ratios[gradient.num] = pos;
+        gradient.rgba[gradient.num] = color;
+        gradient.num++;
+        free(posstr);
+        free(colorstr);
+        lastpos = pos;
        }
-       gradient.ratios[gradient.num] = pos;
-       gradient.rgba[gradient.num] = color;
-       gradient.num++;
-       free(posstr);
-       free(colorstr);
-       lastpos = pos;
-    }
     return gradient;
 }
 
@@ -1381,8 +1646,6 @@ void s_gradient(char*name, const char*text, int radial, int rotate)
     gradient->radial = radial;
     gradient->rotate = rotate;
 
-    if(dictionary_lookup(&gradients, name))
-       syntaxerror("gradient %s defined twice", name);
     dictionary_put2(&gradients, name, gradient);
 }
     
@@ -1390,6 +1653,9 @@ void s_gradientglow(char*name, char*gradient, float blurx, float blury,
                    float angle, float distance, float strength, char innershadow, 
                    char knockout, char composite, char ontop, int passes)
 {
+    if(dictionary_lookup(&filters, name))
+        syntaxerror("filter %s defined twice", name);
+    
     gradient_t* g = dictionary_lookup(&gradients, gradient);
 
     composite = 1;
@@ -1410,13 +1676,14 @@ void s_gradientglow(char*name, char*gradient, float blurx, float blury,
     filter->ontop = ontop;
     filter->passes = passes;
 
-    if(dictionary_lookup(&filters, name))
-       syntaxerror("filter %s defined twice", name);
     dictionary_put2(&filters, name, filter);
 }
 
 void s_dropshadow(char*name, RGBA color, double blurx, double blury, double angle, double distance, double strength, char innershadow, char knockout, char composite, int passes)
 {
+    if(dictionary_lookup(&filters, name))
+        syntaxerror("filter %s defined twice", name);
+
     composite = 1;
     FILTER_DROPSHADOW* filter = rfx_calloc(sizeof(FILTER_DROPSHADOW));
     filter->type = FILTERTYPE_DROPSHADOW;
@@ -1431,13 +1698,14 @@ void s_dropshadow(char*name, RGBA color, double blurx, double blury, double angl
     filter->composite = composite;
     filter->passes = passes;
 
-    if(dictionary_lookup(&filters, name))
-       syntaxerror("filter %s defined twice", name);
     dictionary_put2(&filters, name, filter);
 }
 
 void s_bevel(char*name, RGBA shadow, RGBA highlight, double blurx, double blury, double angle, double distance, double strength, char innershadow, char knockout, char composite, char ontop, int passes)
 {
+    if(dictionary_lookup(&filters, name))
+        syntaxerror("filter %s defined twice", name);
+
     composite = 1;
     FILTER_BEVEL* filter = rfx_calloc(sizeof(FILTER_BEVEL));
     filter->type = FILTERTYPE_BEVEL;
@@ -1454,21 +1722,20 @@ void s_bevel(char*name, RGBA shadow, RGBA highlight, double blurx, double blury,
     filter->ontop = ontop;
     filter->passes = passes;
 
-    if(dictionary_lookup(&filters, name))
-       syntaxerror("filter %s defined twice", name);
     dictionary_put2(&filters, name, filter);
 }
 
 void s_blur(char*name, double blurx, double blury, int passes)
 {
+    if(dictionary_lookup(&filters, name))
+        syntaxerror("filter %s defined twice", name);
+
     FILTER_BLUR* filter = rfx_calloc(sizeof(FILTER_BLUR));
     filter->type = FILTERTYPE_BLUR;
     filter->blurx = blurx;
     filter->blury = blury;
     filter->passes = passes;
 
-    if(dictionary_lookup(&filters, name))
-       syntaxerror("filter %s defined twice", name);
     dictionary_put2(&filters, name, filter);
 }
 
@@ -1476,8 +1743,10 @@ void s_action(const char*text)
 {
     ActionTAG* a = 0;
     a = swf_ActionCompile(text, stack[0].swf->fileVersion);
-    if(!a) {
-       syntaxerror("Couldn't compile ActionScript");
+    if(!a)
+    {
+        swf_ActionFree(a);
+        syntaxerror("Couldn't compile ActionScript");
     }
 
     tag = swf_InsertTag(tag, ST_DOACTION);
@@ -1492,8 +1761,10 @@ void s_initaction(const char*character, const char*text)
     ActionTAG* a = 0;
     character_t*c = 0;
     a = swf_ActionCompile(text, stack[0].swf->fileVersion);
-    if(!a) {
-       syntaxerror("Couldn't compile ActionScript");
+    if(!a) 
+    {
+        swf_ActionFree(a);
+        syntaxerror("Couldn't compile ActionScript");
     }
 
     c = (character_t*)dictionary_lookup(&characters, character);
@@ -1531,6 +1802,9 @@ int s_swf3action(char*name, char*action)
 
 void s_outline(char*name, char*format, char*source)
 {
+    if(dictionary_lookup(&outlines, name))
+        syntaxerror("outline %s defined twice", name);
+        
     outline_t* outline;
 
     drawer_t draw;
@@ -1551,8 +1825,6 @@ void s_outline(char*name, char*format, char*source)
     outline->shape = shape;
     outline->bbox = bounds;
     
-    if(dictionary_lookup(&outlines, name))
-       syntaxerror("outline %s defined twice", name);
     dictionary_put2(&outlines, name, outline);
 }
 
@@ -1706,28 +1978,28 @@ void s_endClip()
     currentdepth++;
 }
 
-void setPlacement(TAG*tag, U16 id, U16 depth, MATRIX m, char*name, parameters_t*p, char move)
-{
-    SWFPLACEOBJECT po;
-    FILTERLIST flist;
-    swf_GetPlaceObject(NULL, &po);
-    po.id = id;
-    po.depth = depth;
-    po.matrix = m;
-    po.cxform = p->cxform;
-    po.name = name;
-    po.move = move;
-    if(move)
-       po.id = 0;
-    if(p->blendmode) {
-       po.blendmode = p->blendmode;
-    }
-    if(p->filter) {
-       flist.num = 1;
-       flist.filter[0] = p->filter;
-       po.filters = &flist;
-    }
-    swf_SetPlaceObject(tag, &po);
+void setStartparameters(instance_t* i, parameters_t* p, TAG* tag)
+{
+    history_begin(i->history, "x", currentframe, tag, p->x);
+    history_begin(i->history, "y", currentframe, tag, p->y);
+    history_begin(i->history, "scalex", currentframe, tag, p->scalex);
+    history_begin(i->history, "scaley", currentframe, tag, p->scaley);
+    history_begin(i->history, "cxform.r0", currentframe, tag, p->cxform.r0);
+    history_begin(i->history, "cxform.g0", currentframe, tag, p->cxform.g0);
+    history_begin(i->history, "cxform.b0", currentframe, tag, p->cxform.b0);
+    history_begin(i->history, "cxform.a0", currentframe, tag, p->cxform.a0);
+    history_begin(i->history, "cxform.r1", currentframe, tag, p->cxform.r1);
+    history_begin(i->history, "cxform.g1", currentframe, tag, p->cxform.g1);
+    history_begin(i->history, "cxform.b1", currentframe, tag, p->cxform.b1);
+    history_begin(i->history, "cxform.a1", currentframe, tag, p->cxform.a1);
+    history_begin(i->history, "rotate", currentframe, tag, p->rotate);
+    history_begin(i->history, "shear", currentframe, tag, p->shear);
+    history_begin(i->history, "pivot.x", currentframe, tag, p->pivot.x);
+    history_begin(i->history, "pivot.y", currentframe, tag, p->pivot.y);
+    history_begin(i->history, "pin.x", currentframe, tag, p->pin.x);
+    history_begin(i->history, "pin.y", currentframe, tag, p->pin.y);
+    history_begin(i->history, "blendmode", currentframe, tag, p->blendmode);
+    history_beginFilter(i->history, currentframe, tag, p->filter);
 }
 
 void s_put(char*instance, char*character, parameters_t p)
@@ -1735,228 +2007,104 @@ void s_put(char*instance, char*character, parameters_t p)
     character_t* c = dictionary_lookup(&characters, character);
     instance_t* i;
     MATRIX m;
-    if(!c) {
-       syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
-    }
+    if(!c) 
+        syntaxerror("character %s not known (in .put %s=%s)", character, instance, character);
     
     i = s_addinstance(instance, c, currentdepth);
     i->parameters = p;
     m = s_instancepos(i->character->size, &p);
    
-    if(p.blendmode || p.filter) {
-       if(stack[0].swf->fileVersion < 8) {
-           if(p.blendmode) warning("blendmodes only supported for flash version>=8");
-           else            warning("filters only supported for flash version>=8");
+    if(p.blendmode || p.filter)
+    {
+        if(stack[0].swf->fileVersion < 8) 
+        {
+            if(p.blendmode)
+                warning("blendmodes only supported for flash version>=8");
+            else
+                warning("filters only supported for flash version>=8");
+        }
+        tag = swf_InsertTag(tag, ST_PLACEOBJECT3);
        }
-       tag = swf_InsertTag(tag, ST_PLACEOBJECT3);
-    } else {
-       tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
-    }
+    else
+        tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
     setPlacement(tag, c->id, currentdepth, m, instance, &p, 0);
-    
+    setStartparameters(i, &p, tag);
     i->lastTag = tag;
     i->lastFrame = currentframe;
     currentdepth++;
-    
 }
 
-void s_jump(char*instance, parameters_t p)
+void recordChanges(history_t* history, parameters_t p, int changeFunction, interpolation_t* inter)
 {
-    instance_t* i = dictionary_lookup(&instances, instance);
-    MATRIX m;
-    if(!i) {
-       syntaxerror("instance %s not known", instance);
+    if (p.set & SF_X)
+        history_remember(history, "x", currentframe, changeFunction, p.x, inter);
+    if (p.set & SF_Y)
+        history_remember(history, "y", currentframe, changeFunction, p.y, inter);
+    if (p.set & SF_SCALEX)
+        history_remember(history, "scalex", currentframe, changeFunction, p.scalex, inter);
+    if (p.set & SF_SCALEY)
+        history_remember(history, "scaley", currentframe, changeFunction, p.scaley, inter);
+    if (p.set & SF_CX_R)
+    {
+        history_remember(history, "cxform.r0", currentframe, changeFunction, p.cxform.r0, inter);
+        history_remember(history, "cxform.r1", currentframe, changeFunction, p.cxform.r1, inter);
     }
-
-    i->parameters = p;
-    m = s_instancepos(i->character->size, &p);
-
-    if(p.blendmode || p.filter) {
-       tag = swf_InsertTag(tag, ST_PLACEOBJECT3);
-    } else {
-       tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
+    if (p.set & SF_CX_G)
+    {
+        history_remember(history, "cxform.g0", currentframe, changeFunction, p.cxform.g0, inter);
+        history_remember(history, "cxform.g1", currentframe, changeFunction, p.cxform.g1, inter);
     }
-    setPlacement(tag, 0, i->depth, m, 0, &p, 1);
-
-    i->lastTag = tag;
-    i->lastFrame = currentframe;
-}
-
-RGBA interpolateColor(RGBA c1, RGBA c2, float ratio)
-{
-    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;
-    return c;
-}
-
-FILTER* interpolateFilter(FILTER*filter1,FILTER*filter2, float ratio)
-{
-    if(!filter1 && !filter2)
-       return 0;
-    if(!filter1)
-       return interpolateFilter(filter2,filter1,1-ratio);
-
-    if(filter2 && filter2->type != filter1->type)
-       syntaxerror("can't interpolate between %s and %s filters yet", filtername[filter1->type], filtername[filter2->type]);
-   
-    if(filter1->type == FILTERTYPE_BLUR) {
-       FILTER_BLUR*f1 = (FILTER_BLUR*)filter1;
-       FILTER_BLUR*f2 = (FILTER_BLUR*)filter2;
-       if(f2 && 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;
-       return (FILTER*)f;
-    } else if (filter1->type == FILTERTYPE_DROPSHADOW) {
-       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 && 
-          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);
-       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;
-    } else if (filter1->type == FILTERTYPE_BEVEL) {
-       FILTER_BEVEL*f1 = (FILTER_BEVEL*)filter1;
-       FILTER_BEVEL*f2 = (FILTER_BEVEL*)filter2;
-       if(f2 && !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;
-       FILTER_BEVEL*f = (FILTER_BEVEL*)swf_NewFilter(FILTERTYPE_BEVEL);
-       memcpy(f, f1, sizeof(FILTER_BEVEL));
-       f->shadow = interpolateColor(f1->shadow, f2->shadow, ratio);
-       f->highlight = interpolateColor(f1->highlight, f2->highlight, ratio);
-       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;
-    } /*else if (filter1->type == FILTERTYPE_GRADIENTGLOW) {
-       FILTER_GRADIENTGLOW*f = (FILTER_GRADIENTGLOW*)swf_NewFilter(FILTERTYPE_GRADIENTGLOW);
-       // can't interpolate gradients
-       memcpy(f, filter1, sizeof(FILTER_GRADIENTGLOW));
-       return (FILTER*)f;
-    }*/ else {
-       syntaxerror("can't interpolate %s filters yet", filtername[filter1->type]);
+    if (p.set & SF_CX_B)
+    {
+        history_remember(history, "cxform.b0", currentframe, changeFunction, p.cxform.b0, inter);
+        history_remember(history, "cxform.b1", currentframe, changeFunction, p.cxform.b1, inter);
     }
-    return 0;
+    if (p.set & SF_CX_A)
+    {
+        history_remember(history, "cxform.a0", currentframe, changeFunction, p.cxform.a0, inter);
+        history_remember(history, "cxform.a1", currentframe, changeFunction, p.cxform.a1, inter);
+    }
+    if (p.set & SF_ROTATE)
+        history_remember(history, "rotate", currentframe, changeFunction, p.rotate, inter);
+    if (p.set & SF_SHEAR)
+        history_remember(history, "shear", currentframe, changeFunction, p.shear, inter);
+    if (p.set & SF_PIVOT)
+    {
+        history_remember(history, "pivot.x", currentframe, changeFunction, p.pivot.x, inter);
+        history_remember(history, "pivot.y", currentframe, changeFunction, p.pivot.y, inter);
+    }
+    if (p.set & SF_PIN)
+    {
+        history_remember(history, "pin.x", currentframe, changeFunction, p.pin.x, inter);
+        history_remember(history, "pin.y", currentframe, changeFunction, p.pin.y, inter);
+    }
+    if (p.set & SF_BLEND)
+        history_remember(history, "blendmode", currentframe, changeFunction, p.blendmode, inter);
+    if (p.set & SF_FILTER)
+        history_rememberFilter(history, currentframe, changeFunction, p.filter, inter);
 }
 
-parameters_t s_interpolate(parameters_t*p1, parameters_t*p2, int pos, int num)
+void s_jump(char* instance, parameters_t p)
 {
-    parameters_t p;
-    float ratio;
-    if(num==0 || num==1)
-       return *p1;
-    ratio = (float)pos/(float)num;
-    
-    p.x = (p2->x-p1->x)*ratio + p1->x;
-    p.y = (p2->y-p1->y)*ratio + p1->y;
-    p.scalex = (p2->scalex-p1->scalex)*ratio + p1->scalex;
-    p.scaley = (p2->scaley-p1->scaley)*ratio + p1->scaley;
-    p.rotate = (p2->rotate-p1->rotate)*ratio + p1->rotate;
-    p.shear = (p2->shear-p1->shear)*ratio + p1->shear;
-
-    p.cxform.r0 = ((float)p2->cxform.r0-(float)p1->cxform.r0)*ratio + p1->cxform.r0;
-    p.cxform.g0 = ((float)p2->cxform.g0-(float)p1->cxform.g0)*ratio + p1->cxform.g0;
-    p.cxform.b0 = ((float)p2->cxform.b0-(float)p1->cxform.b0)*ratio + p1->cxform.b0;
-    p.cxform.a0 = ((float)p2->cxform.a0-(float)p1->cxform.a0)*ratio + p1->cxform.a0;
-
-    p.cxform.r1 = (p2->cxform.r1-p1->cxform.r1)*ratio + p1->cxform.r1;
-    p.cxform.g1 = (p2->cxform.g1-p1->cxform.g1)*ratio + p1->cxform.g1;
-    p.cxform.b1 = (p2->cxform.b1-p1->cxform.b1)*ratio + p1->cxform.b1;
-    p.cxform.a1 = (p2->cxform.a1-p1->cxform.a1)*ratio + p1->cxform.a1;
-
-    p.pivot.x = (p2->pivot.x-p1->pivot.x)*ratio + p1->pivot.x;
-    p.pivot.y = (p2->pivot.y-p1->pivot.y)*ratio + p1->pivot.y;
-    p.pin.x = (p2->pin.x-p1->pin.x)*ratio + p1->pin.x;
-    p.pin.y = (p2->pin.y-p1->pin.y)*ratio + p1->pin.y;
-
-    p.filter = interpolateFilter(p1->filter, p2->filter, ratio);
-    return p;
+    instance_t* i = dictionary_lookup(&instances, instance);
+    if(!i)
+        syntaxerror("instance %s not known", instance);
+    recordChanges(i->history, p, CF_JUMP, 0);
 }
 
-void s_change(char*instance, parameters_t p2)
+void s_change(char*instance, parameters_t p2, interpolation_t* inter)
 {
     instance_t* i = dictionary_lookup(&instances, instance);
-    MATRIX m;
-    parameters_t p1;
-    TAG*t;
-    int frame, allframes;
-    if(!i) {
-       syntaxerror("instance %s not known", instance);
-    }
-    p1 = i->parameters;
-    
-    allframes = currentframe - i->lastFrame - 1;
-    if(allframes < 0) {
-       warning(".change ignored. can only .put/.change an object once per frame.");
-       return;
-    }
-    
-    m = s_instancepos(i->character->size, &p2);
-    if(p2.blendmode || p2.filter) {
-       tag = swf_InsertTag(tag, ST_PLACEOBJECT3);
-    } else {
-       tag = swf_InsertTag(tag, ST_PLACEOBJECT2);
-    }
-    setPlacement(tag, 0, i->depth, m, 0, &p2, 1);
-    i->parameters = p2;
-
-    /* o.k., we got the start and end point set. Now iterate though all the
-       tags in between, inserting object changes after each new frame */
-    t = i->lastTag;
-    i->lastTag = tag;
-    if(!t) syntaxerror("internal error(6)");
-    frame = 0;
-    while(frame < allframes) {
-       if(t->id == ST_SHOWFRAME) {
-           parameters_t p;
-           MATRIX m;
-           TAG*lt;
-           frame ++;
-           p = s_interpolate(&p1, &p2, frame, allframes);
-           m = s_instancepos(i->character->size, &p); //needed?
-
-           i->lastFrame = currentframe;
-           if(p.blendmode || p.filter) {
-               lt = swf_InsertTag(t, ST_PLACEOBJECT3);
-           } else {
-               lt = swf_InsertTag(t, ST_PLACEOBJECT2);
-           }
-           setPlacement(lt, 0, i->depth, m, 0, &p, 1);
-           t = lt;
-           if(frame == allframes)
-               break;
-       }
-       t = t->next;
-       if(!t) 
-           syntaxerror("internal error(8) (frame=%d/%d)", frame, allframes);
-    }
+    if(!i)
+        syntaxerror("instance %s not known", instance);
+    recordChanges(i->history, p2, CF_CHANGE, inter);
 }
 
 void s_delinstance(char*instance)
 {
     instance_t* i = dictionary_lookup(&instances, instance);
-    if(!i) {
-       syntaxerror("instance %s not known", instance);
-    }
+    if(!i)
+        syntaxerror("instance %s not known", instance);
     tag = swf_InsertTag(tag, ST_REMOVEOBJECT2);
     swf_SetU16(tag, i->depth);
     dictionary_del(&instances, instance);
@@ -1964,21 +2112,33 @@ void s_delinstance(char*instance)
 
 void s_qchange(char*instance, parameters_t p)
 {
+    instance_t* i = dictionary_lookup(&instances, instance);
+    if(!i)
+        syntaxerror("instance %s not known", instance);
+    recordChanges(i->history, p, CF_QCHANGE, 0);
 }
 
 void s_end()
 {
     if(!stackpos)
        syntaxerror(".end unexpected");
-    if(stack[stackpos-1].type == 0)
-       s_endSWF();
-    else if(stack[stackpos-1].type == 1)
-       s_endSprite();
-    else if(stack[stackpos-1].type == 2)
-       s_endClip();
-    else if(stack[stackpos-1].type == 3)
-       s_endButton();
-    else syntaxerror("internal error 1");
+    switch (stack[stackpos-1].type)
+    {
+        case 0:
+            s_endSWF();
+            break;
+        case 1:
+            s_endSprite();
+            break;
+        case 2:
+            s_endClip();
+            break;
+        case 3:
+            s_endButton();
+            break;
+        default:
+            syntaxerror("internal error 1");
+    }
 }
 
 // ------------------------------------------------------------------------
@@ -2062,12 +2222,17 @@ int parseTwip(char*str)
        char*s;
        *dot++ = 0;
        for(s=str;s<dot-1;s++)
-           if(*s<'0' || *s>'9')
-               syntaxerror("Not a coordinate: \"%s\"", str);
-       for(s=dot;*s;s++) {
-           if(*s<'0' || *s>'9')
-               syntaxerror("Not a coordinate: \"%s\"", str);
-       }
+        if(*s<'0' || *s>'9')
+        {
+            free(old);
+            syntaxerror("Not a coordinate: \"%s\"", str);
+        }
+    for(s=dot;*s;s++)
+        if(*s<'0' || *s>'9')
+        {
+            free(old);
+            syntaxerror("Not a coordinate: \"%s\"", str);
+        }
        if(l>2 || (l==2 && (dot[1]!='0' && dot[1]!='5'))) {
            dot[1] = ((dot[1]-0x30)/5)*5 + 0x30;
            dot[2] = 0;
@@ -2326,6 +2491,34 @@ static dictionary_t points;
 static mem_t mpoints;
 int points_initialized = 0;
 
+static int c_interpolation(map_t *args)
+{
+    int i;
+    char* name = lu(args, "name");
+    if (dictionary_lookup(&interpolations, name))
+        syntaxerror("interpolation %s defined twice", name);
+    
+    interpolation_t* inter = (interpolation_t*)malloc(sizeof(interpolation_t));
+    char* functionstr = lu(args, "function");
+    inter->function = 0;
+    for (i = 0; i < sizeof(interpolationFunctions) / sizeof(interpolationFunctions[0]); i++)
+        if (!strcmp(functionstr,interpolationFunctions[i]))
+        {
+            inter->function = i + 1;
+            break;
+        }
+    if (!inter->function)
+        syntaxerror("unkown interpolation function %s", functionstr);
+    inter->speed = parseFloat(lu(args, "speed"));
+    inter->amplitude = parseFloat(lu(args, "amplitude"));
+    inter->growth = parseFloat(lu(args, "growth"));
+    inter->bounces = parseInt(lu(args, "bounces"));
+    inter->damping = parseInt(lu(args, "damping"));
+    
+    dictionary_put2(&interpolations, name, inter);
+    return 0;
+}
+
 SPOINT getPoint(SRECT r, char*name)
 {
     int l=0;
@@ -2422,7 +2615,10 @@ static int c_gradient(map_t*args)
 
     readToken();
     if(type != RAWDATA)
-       syntaxerror("colon (:) expected");
+        syntaxerror("colon (:) expected");
+
+    if(dictionary_lookup(&gradients, name))
+        syntaxerror("gradient %s defined twice", name);
 
     s_gradient(name, text, radial, rotate);
 
@@ -2587,13 +2783,12 @@ static int c_stop(map_t*args)
 {
     char*name = map_lookup(args, "name");
 
-    if(s_playsound(name, 0,0,1)) {
-       return 0;
-    } else if(s_swf3action(name, "stop")) {
-       return 0;
-    }
+    if(s_playsound(name, 0,0,1))
+        return 0;
+    else if(s_swf3action(name, "stop"))
+        return 0;
     syntaxerror("I don't know anything about sound/movie \"%s\"", name);
-    return 0;
+       return 0;
 }
 
 static int c_nextframe(map_t*args) 
@@ -2640,7 +2835,7 @@ static int c_placement(map_t*args, int type)
     char* pinstr = lu(args, "pin");
     char* as = map_lookup(args, "as");
     char* blendmode = lu(args, "blend");
-    char*filterstr = lu(args, "filter");
+    char* filterstr = lu(args, "filter");
     U8 blend;
     MULADD r,g,b,a;
     float oldwidth;
@@ -2648,25 +2843,32 @@ static int c_placement(map_t*args, int type)
     SRECT oldbbox;
     MULADD luminance;
     parameters_t p;
+    U32 set = 0x00000000;
 
-    if(type==9) { // (?) .rotate  or .arcchange
-       pivotstr = lu(args, "pivot");
-       anglestr = lu(args, "angle");
-    } else {
-       xstr = lu(args, "x");
-       ystr = lu(args, "y");
+    if(type==9)
+    { // (?) .rotate  or .arcchange
+        pivotstr = lu(args, "pivot");
+        anglestr = lu(args, "angle");
+    }
+    else
+    {
+        xstr = lu(args, "x");
+        ystr = lu(args, "y");
     }
+    
     if(luminancestr[0])
-       luminance = parseMulAdd(luminancestr);
-    else {
-       luminance.add = 0;
-       luminance.mul = 256;
+        luminance = parseMulAdd(luminancestr);
+    else
+    {
+        luminance.add = 0;
+        luminance.mul = 256;
     }
 
-    if(scalestr[0]) {
-       if(scalexstr[0]||scaleystr[0])
-           syntaxerror("scalex/scaley and scale cannot both be set");
-       scalexstr = scaleystr = scalestr;
+    if(scalestr[0])
+    {
+        if(scalexstr[0]||scaleystr[0])
+            syntaxerror("scalex/scaley and scale cannot both be set");
+        scalexstr = scaleystr = scalestr;
     }
     
     if(type == 0 || type == 4)  {
@@ -2682,159 +2884,216 @@ static int c_placement(map_t*args, int type)
     }
 
     /* x,y position */
-    if(xstr[0]) {
-       if(isRelative(xstr)) {
-           if(type == 0 || type == 4)
-               syntaxerror("relative x values not allowed for initial put or startclip");
-           p.x += parseTwip(getOffset(xstr))*getSign(xstr);
-       } else {
-           p.x = parseTwip(xstr);
-       }
-    }
-    if(ystr[0]) {
-       if(isRelative(ystr)) {
-           if(type == 0 || type == 4)
-               syntaxerror("relative y values not allowed for initial put or startclip");
-           p.y += parseTwip(getOffset(ystr))*getSign(ystr);
-       } else {
-           p.y = parseTwip(ystr);
+    if(xstr[0]) 
+    {
+        if(isRelative(xstr))
+        {
+            if(type == 0 || type == 4)
+                syntaxerror("relative x values not allowed for initial put or startclip");
+            p.x += parseTwip(getOffset(xstr))*getSign(xstr);
+        }
+        else
+        {
+            p.x = parseTwip(xstr);
+        }
+        set = set | SF_X;
+     }
+    if(ystr[0])
+    {
+        if(isRelative(ystr))
+        {
+            if(type == 0 || type == 4)
+                syntaxerror("relative y values not allowed for initial put or startclip");
+            p.y += parseTwip(getOffset(ystr))*getSign(ystr);
+        }
+        else
+        {
+            p.y = parseTwip(ystr);
+        }
+        set = set | SF_Y;
        }
-    }
 
     /* scale, scalex, scaley */
-    if(character) {
-       oldbbox = s_getCharBBox(character);
-    } else {
-       oldbbox = s_getInstanceBBox(instance);
-    }
+    if(character)
+        oldbbox = s_getCharBBox(character);
+    else
+        oldbbox = s_getInstanceBBox(instance);
     oldwidth = oldbbox.xmax - oldbbox.xmin;
     oldheight = oldbbox.ymax - oldbbox.ymin;
-    if(scalexstr[0]) {
-       if(oldwidth==0) p.scalex = 1.0;
-       else {      
-           if(scalexstr[0])
-               p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
-       }
-    }
-    if(scaleystr[0]) {
-       if(oldheight==0) p.scaley = 1.0;
-       else {
-           if(scaleystr[0])
-               p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
-       }
-    }
+    if(scalexstr[0])
+    {
+        if(oldwidth==0)
+            p.scalex = 1.0;
+        else
+            if(scalexstr[0])
+                p.scalex = (float)(parseNewSize(scalexstr, oldwidth))/oldwidth;
+        set = set | SF_SCALEX;
+    }
+   if(scaleystr[0])
+   {
+        if(oldheight==0)
+            p.scaley = 1.0;
+        else
+            if(scaleystr[0])
+                p.scaley = (float)(parseNewSize(scaleystr, oldheight))/oldheight;
+        set = set | SF_SCALEY;
+   }
    
     /* rotation */
-    if(rotatestr[0]) {
-       if(isRelative(rotatestr)) {
-           p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
-       } else {
-           p.rotate = parseFloat(rotatestr);
+    if(rotatestr[0])
+    {
+        if(isRelative(rotatestr))
+            p.rotate += parseFloat(getOffset(rotatestr))*getSign(rotatestr);
+        else
+            p.rotate = parseFloat(rotatestr);
+        set = set | SF_ROTATE;
        }
-    }
 
     /* shearing */
-    if(shearstr[0]) {
-       if(isRelative(shearstr)) {
-           p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
-       } else {
-           p.shear = parseFloat(shearstr);
-       }
+    if(shearstr[0])
+    {
+        if(isRelative(shearstr))
+            p.shear += parseFloat(getOffset(shearstr))*getSign(shearstr);
+        else
+            p.shear = parseFloat(shearstr);
+        set = set | SF_SHEAR;
     }
 
-    if(pivotstr[0]) {
-       if(isPoint(pivotstr)) 
-           p.pivot = parsePoint(pivotstr);
-       else 
-           p.pivot = getPoint(oldbbox, pivotstr);
+    if(pivotstr[0])
+    {
+        if(isPoint(pivotstr))
+            p.pivot = parsePoint(pivotstr);
+        else 
+            p.pivot = getPoint(oldbbox, pivotstr);
+        set = set | SF_PIVOT;
     }
-    if(pinstr[0]) {
-       if(isPoint(pinstr))
-           p.pin = parsePoint(pinstr);
-       else
-           p.pin = getPoint(oldbbox, pinstr);
+    
+    if(pinstr[0])
+    {
+        if(isPoint(pinstr))
+            p.pin = parsePoint(pinstr);
+        else
+            p.pin = getPoint(oldbbox, pinstr);
+        set = set | SF_PIN;
     }
        
     /* color transform */
 
-    if(rstr[0] || luminancestr[0]) {
-       MULADD r;
-       if(rstr[0])
-           r = parseMulAdd(rstr);
-       else {
-           r.add = p.cxform.r0;
-           r.mul = p.cxform.r1;
-       }
-       r = mergeMulAdd(r, luminance);
-       p.cxform.r0 = r.mul;p.cxform.r1 = r.add;
-    }
-    if(gstr[0] || luminancestr[0]) {
-       MULADD g;
-       if(gstr[0])
-           g = parseMulAdd(gstr);
-       else {
-           g.add = p.cxform.g0;
-           g.mul = p.cxform.g1;
-       }
-       g = mergeMulAdd(g, luminance);
-       p.cxform.g0 = g.mul;p.cxform.g1 = g.add;
-    }
-    if(bstr[0] || luminancestr[0]) {
-       MULADD b;
-       if(bstr[0])
-           b = parseMulAdd(bstr);
-       else {
-           b.add = p.cxform.b0;
-           b.mul = p.cxform.b1;
+    if(rstr[0] || luminancestr[0])
+    {
+        MULADD r;
+        if(rstr[0])
+            r = parseMulAdd(rstr);
+        else
+        {
+            r.add = p.cxform.r0;
+            r.mul = p.cxform.r1;
+        }
+        r = mergeMulAdd(r, luminance);
+        p.cxform.r0 = r.mul;
+        p.cxform.r1 = r.add;
+        set = set | SF_CX_R;
        }
-       b = mergeMulAdd(b, luminance);
-       p.cxform.b0 = b.mul;p.cxform.b1 = b.add;
-    }
-    if(astr[0]) {
-       MULADD a = parseMulAdd(astr);
-       p.cxform.a0 = a.mul;p.cxform.a1 = a.add;
+    if(gstr[0] || luminancestr[0])
+    {
+        MULADD g;
+        if(gstr[0])
+            g = parseMulAdd(gstr);
+        else
+        {
+            g.add = p.cxform.g0;
+            g.mul = p.cxform.g1;
+        }
+        g = mergeMulAdd(g, luminance);
+        p.cxform.g0 = g.mul;
+        p.cxform.g1 = g.add;
+        set = set | SF_CX_G;
+    }
+    if(bstr[0] || luminancestr[0])
+    {
+        MULADD b;
+        if(bstr[0])
+            b = parseMulAdd(bstr);
+        else
+        {
+            b.add = p.cxform.b0;
+            b.mul = p.cxform.b1;
+        }
+        b = mergeMulAdd(b, luminance);
+        p.cxform.b0 = b.mul;
+        p.cxform.b1 = b.add;
+        set = set | SF_CX_B;
+    }
+    if(astr[0])
+    {
+        MULADD a = parseMulAdd(astr);
+        p.cxform.a0 = a.mul;
+        p.cxform.a1 = a.add;
+        set = set | SF_CX_A;
     }
 
-    if(blendmode[0]) {
-       int t;
-       blend = 255;
-       for(t=0;blendModeNames[t];t++) {
-           if(!strcmp(blendModeNames[t], blendmode)) {
-               blend = t;
-               break;
-           }
-       }
-       if(blend == 255) {
-           syntaxerror("unknown blend mode: '%s'", blendmode);
+    if(blendmode[0])
+    {
+        int t;
+        blend = 255;
+        for(t = 0; blendModeNames[t]; t++)
+        {
+            if(!strcmp(blendModeNames[t], blendmode))
+            {
+                blend = t;
+                break;
+            }
+        }
+        if(blend == 255)
+        {
+            syntaxerror("unknown blend mode: '%s'", blendmode);
+        }
+        p.blendmode = blend;
+        set = set | SF_BLEND;
        }
-       p.blendmode = blend;
-    }
     
-    if(filterstr[0]) {
-       FILTER*f = dictionary_lookup(&filters, filterstr);
-       if(!f) 
-           syntaxerror("Unknown filter %s", filterstr);
-       p.filter = f;
-    }
-
-
-    if(type == 0)
-       s_put(instance, character, p);
-    else if(type == 1)
-       s_change(instance, p);
-    else if(type == 2)
-       s_qchange(instance, p);
-    else if(type == 3)
-       s_jump(instance, p);
-    else if(type == 4)
-       s_startclip(instance, character, p);
-    else if(type == 5) {
-       if(as && as[0]) {
-           s_buttonput(character, as, p);
-       } else {
-           s_buttonput(character, "shape", p);
-       }
+    if(filterstr[0])
+    {
+        FILTER*f = dictionary_lookup(&filters, filterstr);
+        if(!f) 
+            syntaxerror("Unknown filter %s", filterstr);
+        p.filter = f;
+        set = set | SF_FILTER;
     }
+
+    p.set = set;
+
+    switch (type)
+    {
+        case 0:
+            s_put(instance, character, p);
+            break;
+        case 1:
+            {
+                char* interstr = lu(args, "interpolation");
+                interpolation_t* inter = (interpolation_t*)dictionary_lookup(&interpolations, interstr);
+                if (!inter)
+                    syntaxerror("unkown interpolation %s", interstr);
+                s_change(instance, p, inter);
+            }
+            break;
+        case 2:
+            s_qchange(instance, p);
+            break;
+        case 3:
+            s_jump(instance, p);
+            break;
+        case 4:
+            s_startclip(instance, character, p);
+            break;
+        case 5:
+            if(as && as[0])
+                s_buttonput(character, as, p);
+            else
+                s_buttonput(character, "shape", p);
+            break;
+//        default: 
+       }
     return 0;
 }
 static int c_put(map_t*args) 
@@ -2844,6 +3103,8 @@ static int c_put(map_t*args)
 }
 static int c_change(map_t*args) 
 {
+    if (currentframe == 0)
+        warning("change commands in frame 1 will be ignored, please use the put command to set object parameters");
     c_placement(args, 1);
     return 0;
 }
@@ -2988,7 +3249,17 @@ static int c_font(map_t*args)
 {
     char*name = lu(args, "name");
     char*filename = lu(args, "filename");
-    s_font(name, filename);
+    fontData* usage = getFontData(name);
+    char* glyphs = usage->glyphs;
+    if (usage->needsAll)
+        glyphs = "";
+    else
+        if (usage->notUsed)
+        {
+            printf("font %s was defined but not used\n", name);
+            return 0;
+        }
+    s_font(name, filename, glyphs);
     return 0;
 }
 
@@ -3293,11 +3564,12 @@ static struct {
  {"font", c_font, "name filename"},
  {"soundtrack", c_soundtrack, "filename"},
  {"quicktime", c_quicktime, "url"},
-
     // generators of primitives
 
  {"point", c_point, "name x=0 y=0"},
  {"gradient", c_gradient, "name @radial=0 rotate=0 scale= scalex= scaley= x= y= width= height= r= shear="}, //extra parameters like .texture
+ {"interpolation", c_interpolation, "name function=linear speed=1.3 amplitude=0 bounces=2 growth=1.5, damping=2"},
  {"outline", c_outline, "name format=simple"},
  {"textshape", c_textshape, "name font size=100% text"},
 
@@ -3333,7 +3605,7 @@ static struct {
     // object placement tags
  {"put", c_put,             "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},
  {"startclip", c_startclip, "<i> x=0 y=0 red=+0 green=+0 blue=+0 alpha=+0 luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},
- {"change", c_change,   "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},
+ {"change", c_change,   "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below= interpolation=linear"},
  {"arcchange", c_arcchange,   "name pivot= angle= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},
  {"qchange", c_qchange, "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},
  {"jump", c_jump,       "name x= y= red= green= blue= alpha= luminance= scale= scalex= scaley= blend= filter= pivot= pin= shear= rotate= ratio= above= below="},