]> git.sesse.net Git - mlt/commitdiff
Revise affine to use interpolation and sub-pixel positioning.
authorDan Dennedy <dan@dennedy.org>
Thu, 3 Jun 2010 05:42:35 +0000 (22:42 -0700)
committerDan Dennedy <dan@dennedy.org>
Thu, 3 Jun 2010 05:42:35 +0000 (22:42 -0700)
src/modules/plus/filter_affine.c
src/modules/plus/interp.h [new file with mode: 0644]
src/modules/plus/transition_affine.c

index 1c05fbd64b0eb6ea51a367828deb99f67e7467c1..755d16bb2fc97ded9a921bfe82f55888c336001f 100644 (file)
@@ -38,7 +38,7 @@ static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *
 
        // Get the image
        int error = 0;
-       *format = mlt_image_yuv422;
+       *format = mlt_image_rgb24a;
        //mlt_frame_get_image( this, image, format, width, height, 0 );
 
        // Only process if we have no error and a valid colour space
@@ -76,8 +76,7 @@ static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *
                        mlt_properties_pass( MLT_PRODUCER_PROPERTIES( producer ), properties, "producer." );
                        mlt_properties_pass( MLT_TRANSITION_PROPERTIES( transition ), properties, "transition." );
                        mlt_service_get_frame( MLT_PRODUCER_SERVICE( producer ), &a_frame, 0 );
-                       mlt_properties_set( MLT_FRAME_PROPERTIES( a_frame ), "rescale.interp", "nearest" );
-                       mlt_properties_set_int( MLT_FRAME_PROPERTIES( a_frame ), "distort", 1 );
+//                     mlt_properties_set_int( MLT_FRAME_PROPERTIES( a_frame ), "distort", 1 );
 
                        // Special case - aspect_ratio = 0
                        if ( mlt_properties_get_double( frame_properties, "aspect_ratio" ) == 0 )
@@ -89,7 +88,7 @@ static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *
                        mlt_transition_process( transition, a_frame, this );
                        mlt_frame_get_image( a_frame, image, format, width, height, writable );
                        mlt_properties_set_data( frame_properties, "affine_frame", a_frame, 0, (mlt_destructor)mlt_frame_close, NULL );
-                       mlt_properties_set_data( frame_properties, "image", *image, *width * *height * 2, NULL, NULL );
+                       mlt_properties_set_data( frame_properties, "image", *image, *width * *height * 4, NULL, NULL );
                        mlt_properties_set_data( frame_properties, "alpha", mlt_frame_get_alpha_mask( a_frame ), *width * *height, NULL, NULL );
                }
        }
diff --git a/src/modules/plus/interp.h b/src/modules/plus/interp.h
new file mode 100644 (file)
index 0000000..47a71c1
--- /dev/null
@@ -0,0 +1,820 @@
+//interp.c
+/*
+ * Copyright (C) 2010 Marko Cebokli   http://lea.hamradio.si/~s57uuu
+ * This file is a part of the Frei0r plugin "c0rners"
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*******************************************************************
+ * The remapping functions use a map aray, which contains a pair
+ * of floating values fo each pixel of the output image. These
+ * represent the location in the input image, from where the value
+ * of the given output pixel should be interpolated.
+ * They are given in pixels of the input image.
+ * If the output image is wo pixels wide, then the x coordinate
+ * of the pixel in row r and column c is at  2*(r*wo+c) in the map
+ * array, and y at 2*(r*wo+c)+1
+ *
+ * The map array is usually computation intensive to generate, and
+ * he purpose of the map array is to allow fast remapping of
+ * several images (video) using the same map array.
+ ******************************************************************/
+
+
+//compile:   gcc -c -O2 -Wall -std=c99 -fPIC interp.c -o interp.o
+
+//     -std=c99 za roundf()
+//     -fPIC da lahko linkas v .so (za frei0r)
+
+#include <math.h>
+#include <stdio.h>     /* za debug printoute */
+#include <inttypes.h>
+
+//--------------------------------------------------------
+//pointer to an interpolating function
+typedef int (*interpp)(unsigned char*, int, int, float, float, unsigned char*);
+
+//************************************
+//REMAP AN IMAGE
+
+//--------------------------------------------------------
+//  vhs = vhodna slika velikosti wi x hi
+//  izs = izhodna slika velikosti wo x ho
+//  map = za vsak pixel izs pove, kje ga vzamemo is vhs
+//  bgc = background color
+//  interp = kazalec na interpolacijsko funkcijo
+void remap(int wi, int hi, int wo, int ho, unsigned char *vhs, unsigned char *izs, float *map, unsigned char bgc, interpp interp)
+{
+       int i,j;
+       float x,y;
+
+       for (i=0;i<ho;i++)
+               for (j=0;j<wo;j++)
+               {
+               x=map[2*(wo*i+j)];
+               y=map[2*(wo*i+j)+1];
+               if (x>0)
+                       interp(vhs,wi,hi,x,y,&izs[wo*i+j]);
+               else
+                       izs[wo*i+j]=bgc;        //background fill
+       }
+}
+
+
+//--------------------------------------------------------
+//for four byte (int, 32 bit) values    (packed RGB color)
+//little endian !!
+//  vhs = vhodna slika velikosti wi x hi
+//  izs = izhodna slika velikosti wo x ho
+//  map = za vsak pixel izs pove, kje ga vzamemo is vhs
+//  bgc = background color
+//  interp = kazalec na interpolacijsko funkcijo
+void remap32(int wi, int hi, int wo, int ho, unsigned char *vhs, unsigned char *izs, float *map, uint32_t bgc, interpp interp)
+{
+       int i,j;
+       float x,y;
+
+       for (i=0;i<ho;i++)
+               for (j=0;j<wo;j++)
+               {
+               x=map[2*(wo*i+j)];
+               y=map[2*(wo*i+j)+1];
+               if (x>0)
+                       interp(vhs,wi,hi,x,y,&izs[4*(wo*i+j)]);
+               else    //background fill
+               {
+                       izs[4*(wo*i+j)]=bgc;
+                       izs[4*(wo*i+j)+1]=bgc>>8;
+                       izs[4*(wo*i+j)+2]=bgc>>16;
+                       izs[4*(wo*i+j)+3]=bgc>>24;
+               }
+       }
+}
+
+//**************************************
+//HERE BEGIN THE INTERPOLATION FUNCTIONS
+
+//------------------------------------------------------
+//za debugging - z izpisovanjem
+//interpolacija "najblizji sosed" (ni prava interpolacija)
+//za byte (char) vrednosti
+//     *sl vhodni array (slika)
+//     w,h dimenzija slike je wxh
+//     x,y tocka, za katero izracuna interpolirano vrednost
+//     *v interpolirana vrednost
+int interpNNpr_b(unsigned char *sl, int w, int h, float x, float y, unsigned char *v)
+{
+       //printf("u=%5.2f v=%5.2f   ",x,y);
+       printf("u=%5.3f v=%5.3f     ",x/(w-1),y/(h-1));
+       //printf("U=%2d V=%2d   ",(int)roundf(x),(int)roundf(y));
+
+#ifdef TEST_XY_LIMITS
+       if ((x<0)||(x>w)||(y<0)||(y>h)) return -1;
+#endif
+
+       *v=sl[(int)roundf(x)+(int)roundf(y)*w];
+       return 0;
+}
+
+//------------------------------------------------------
+//interpolacija "najblizji sosed" (ni prava interpolacija)
+//za byte (char) vrednosti
+//     *sl vhodni array (slika)
+//     w,h dimenzija slike je wxh
+//     x,y tocka, za katero izracuna interpolirano vrednost
+//     *v interpolirana vrednost
+int interpNN_b(unsigned char *sl, int w, int h, float x, float y, unsigned char *v)
+{
+#ifdef TEST_XY_LIMITS
+       if ((x<0)||(x>w)||(y<0)||(y>h)) return -1;
+#endif
+
+       *v=sl[(int)roundf(x)+(int)roundf(y)*w];
+       return 0;
+}
+
+//------------------------------------------------------
+//interpolacija "najblizji sosed" (ni prava interpolacija)
+//za byte (char) vrednosti  v packed color 32 bitnem formatu
+//little endian !!
+//     *sl vhodni array (slika)
+//     w,h dimenzija slike je wxh
+//     x,y tocka, za katero izracuna interpolirano vrednost
+//     *v interpolirana vrednost
+int interpNN_b32(unsigned char *sl, int w, int h, float x, float y, unsigned char *v)
+{
+#ifdef TEST_XY_LIMITS
+       if ((x<0)||(x>w)||(y<0)||(y>h)) return -1;
+#endif
+       v[3]= sl[(int)roundf(x)*4+(int)roundf(y)*4*w+3];
+       float alpha = (float) v[3] / 255.0;
+       v[0]= v[0] * (1.0 - alpha) + sl[(int)roundf(x)*4+(int)roundf(y)*4*w] * alpha;
+       v[1]= v[1] * (1.0 - alpha) + sl[(int)roundf(x)*4+(int)roundf(y)*4*w+1] * alpha;
+       v[2]= v[2] * (1.0 - alpha) + sl[(int)roundf(x)*4+(int)roundf(y)*4*w+2] * alpha;
+
+       return 0;
+}
+
+//------------------------------------------------------
+//bilinearna interpolacija
+//za byte (char) vrednosti
+//     *sl vhodni array (slika)
+//     w,h dimenzija slike je wxh
+//     x,y tocka, za katero izracuna interpolirano vrednost
+//     *v interpolirana vrednost
+int interpBL_b(unsigned char *sl, int w, int h, float x, float y, unsigned char *v)
+{
+       int m,n,k,l;
+       float a,b;
+
+#ifdef TEST_XY_LIMITS
+       if ((x<0)||(x>w)||(y<0)||(y>h)) return -1;
+#endif
+
+       m=(int)floorf(x); n=(int)floorf(y);
+       k=n*w+m; l=(n+1)*w+m;
+       a=sl[k]+(sl[k+1]-sl[k])*(x-(float)m);
+       b=sl[l]+(sl[l+1]-sl[l])*(x-(float)m);
+       *v=a+(b-a)*(y-(float)n);
+       return 0;
+}
+
+//------------------------------------------------------
+//bilinearna interpolacija
+//za byte (char) vrednosti  v packed color 32 bitnem formatu
+int interpBL_b32(unsigned char *sl, int w, int h, float x, float y, unsigned char *v)
+{
+       int m,n,k,l,n1,l1,k1;
+       float a,b;
+
+#ifdef TEST_XY_LIMITS
+       if ((x<0)||(x>w)||(y<0)||(y>h)) return -1;
+#endif
+
+       m=(int)floorf(x); n=(int)floorf(y);
+       k=n*w+m; l=(n+1)*w+m;
+       k1=4*(k+1); l1=4*(l+1); n1=4*((n+1)*w+m);
+       l=4*l; k=4*k;
+
+       a=sl[k+3]+(sl[k1+3]-sl[k+3])*(x-(float)m);
+       b=sl[l+3]+(sl[l1+3]-sl[n1+3])*(x-(float)m);
+       v[3]=a+(b-a)*(y-(float)n);
+       float alpha = (float) v[3] / 255.0;
+
+       a=sl[k]+(sl[k1]-sl[k])*(x-(float)m);
+       b=sl[l]+(sl[l1]-sl[n1])*(x-(float)m);
+       v[0]= v[0] * (1.0 - alpha) + (a+(b-a)*(y-(float)n)) * alpha;
+
+       a=sl[k+1]+(sl[k1+1]-sl[k+1])*(x-(float)m);
+       b=sl[l+1]+(sl[l1+1]-sl[n1+1])*(x-(float)m);
+       v[1]= v[1] * (1.0 - alpha) + (a+(b-a)*(y-(float)n)) * alpha;
+
+       a=sl[k+2]+(sl[k1+2]-sl[k+2])*(x-(float)m);
+       b=sl[l+2]+(sl[l1+2]-sl[n1+2])*(x-(float)m);
+       v[2]= v[2] * (1.0 - alpha) + (a+(b-a)*(y-(float)n)) * alpha;
+
+       return 0;
+}
+
+//------------------------------------------------------
+//bikubicna interpolacija  "smooth"
+//za byte (char) vrednosti
+//kar Aitken-Neville formula iz Bronstajna
+//     *sl vhodni array (slika)
+//     w,h dimenzija slike je wxh
+//     x,y tocka, za katero izracuna interpolirano vrednost
+//     *v interpolirana vrednost
+int interpBC_b(unsigned char *sl, int w, int h, float x, float y, unsigned char *v)
+{
+       int i,j,l,m,n;
+       float k;
+       float p[4],p1[4],p2[4],p3[4],p4[4];
+
+#ifdef TEST_XY_LIMITS
+       if ((x<0)||(x>w)||(y<0)||(y>h)) return -1;
+#endif
+
+       m=(int)ceilf(x)-2; if (m<0) m=0; if ((m+5)>w) m=w-4;
+       n=(int)ceilf(y)-2; if (n<0) n=0; if ((n+5)>h) n=h-4;
+
+       //njaprej po y  (stiri stolpce)
+       for (i=0;i<4;i++)
+       {
+               l=m+(i+n)*w;
+               p1[i]=sl[l];
+               p2[i]=sl[l+1];
+               p3[i]=sl[l+2];
+               p4[i]=sl[l+3];
+       }
+       for (j=1;j<4;j++)
+               for (i=3;i>=j;i--)
+               {
+               k=(y-i-n)/j;
+               p1[i]=p1[i]+k*(p1[i]-p1[i-1]);
+               p2[i]=p2[i]+k*(p2[i]-p2[i-1]);
+               p3[i]=p3[i]+k*(p3[i]-p3[i-1]);
+               p4[i]=p4[i]+k*(p4[i]-p4[i-1]);
+       }
+
+       //zdaj pa po x
+       p[0]=p1[3]; p[1]=p2[3]; p[2]=p3[3]; p[3]=p4[3];
+       for (j=1;j<4;j++)
+               for (i=3;i>=j;i--)
+                       p[i]=p[i]+(x-i-m)/j*(p[i]-p[i-1]);
+
+       if (p[3]<0.0) p[3]=0.0;                 //printf("p=%f ",p[3]);
+       if (p[3]>256.0) p[3]=255.0;             //printf("p=%f ",p[3]);
+
+       *v=p[3];
+
+       return 0;
+}
+
+//------------------------------------------------------
+//bikubicna interpolacija  "smooth"
+//za byte (char) vrednosti  v packed color 32 bitnem formatu
+int interpBC_b32(unsigned char *sl, int w, int h, float x, float y, unsigned char *v)
+{
+       int i,j,b,l,m,n;
+       float k;
+       float p[4],p1[4],p2[4],p3[4],p4[4];
+       float alpha = 1.0;
+
+#ifdef TEST_XY_LIMITS
+       if ((x<0)||(x>w)||(y<0)||(y>h)) return -1;
+#endif
+
+       m=(int)ceilf(x)-2; if (m<0) m=0; if ((m+5)>w) m=w-4;
+       n=(int)ceilf(y)-2; if (n<0) n=0; if ((n+5)>h) n=h-4;
+
+
+       for (b=3;b>-1;b--)
+       {
+               //njaprej po y  (stiri stolpce)
+               for (i=0;i<4;i++)
+               {
+                       l=m+(i+n)*w;
+                       p1[i]=sl[4*l+b];
+                       p2[i]=sl[4*(l+1)+b];
+                       p3[i]=sl[4*(l+2)+b];
+                       p4[i]=sl[4*(l+3)+b];
+               }
+               for (j=1;j<4;j++)
+                       for (i=3;i>=j;i--)
+                       {
+                               k=(y-i-n)/j;
+                               p1[i]=p1[i]+k*(p1[i]-p1[i-1]);
+                               p2[i]=p2[i]+k*(p2[i]-p2[i-1]);
+                               p3[i]=p3[i]+k*(p3[i]-p3[i-1]);
+                               p4[i]=p4[i]+k*(p4[i]-p4[i-1]);
+                       }
+
+               //zdaj pa po x
+               p[0]=p1[3]; p[1]=p2[3]; p[2]=p3[3]; p[3]=p4[3];
+               for (j=1;j<4;j++)
+                       for (i=3;i>=j;i--)
+                               p[i]=p[i]+(x-i-m)/j*(p[i]-p[i-1]);
+
+               if (p[3]<0.0) p[3]=0.0;
+               if (p[3]>256.0) p[3]=255.0;
+
+               v[b]= v[b] * (1.0 - alpha) + p[3] * alpha;
+               if (b == 3) alpha = v[b] / 255.0;
+       }
+
+       return 0;
+}
+
+//------------------------------------------------------
+//bikubicna interpolacija  "sharp"
+//za byte (char) vrednosti
+//Helmut Dersch polinom
+//     *sl vhodni array (slika)
+//     w,h dimenzija slike je wxh
+//     x,y tocka, za katero izracuna interpolirano vrednost
+//     *v interpolirana vrednost
+//!!! ODKOD SUM???  (ze po eni rotaciji v interp_test !!)
+//!!! v defish tega suma ni???
+int interpBC2_b(unsigned char *sl, int w, int h, float x, float y, unsigned char *v)
+{
+       int i,k,l,m,n;
+       float pp,p[4],wx[4],wy[4],xx;
+
+#ifdef TEST_XY_LIMITS
+       if ((x<0)||(x>w)||(y<0)||(y>h)) return -1;
+#endif
+
+       m=(int)ceilf(x)-2; if (m<0) m=0; if ((m+5)>w) m=w-4;
+       n=(int)ceilf(y)-2; if (n<0) n=0; if ((n+5)>h) n=h-4;
+
+
+       //najprej po y (stiri stolpce)
+       xx=y-n;    wy[0]=(-0.75*(xx-5.0)*xx-6.0)*xx+3.0;
+       xx=xx-1.0; wy[1]=(1.25*xx-2.25)*xx*xx+1.0;
+       xx=1.0-xx; wy[2]=(1.25*xx-2.25)*xx*xx+1.0;
+       xx=xx+1.0; wy[3]=(-0.75*(xx-5.0)*xx-6.0)*xx+3.0;
+       //se po x
+       xx=x-m;    wx[0]=(-0.75*(xx-5.0)*xx-6.0)*xx+3.0;
+       xx=xx-1.0; wx[1]=(1.25*xx-2.25)*xx*xx+1.0;
+       xx=1.0-xx; wx[2]=(1.25*xx-2.25)*xx*xx+1.0;
+       xx=xx+1.0; wx[3]=(-0.75*(xx-5.0)*xx-6.0)*xx+3.0;
+
+       k=n*w+m;
+       for (i=0;i<4;i++)
+       {
+               p[i]=0.0;
+               l=k+i;
+               p[i]=wy[0]*sl[l]; l+=w;
+               p[i]+=wy[1]*sl[l]; l+=w;
+               p[i]+=wy[2]*sl[l]; l+=w;
+               p[i]+=wy[3]*sl[l];
+       }
+
+       pp=wx[0]*p[0];
+       pp+=wx[1]*p[1];
+       pp+=wx[2]*p[2];
+       pp+=wx[3]*p[3];
+
+       if (pp<0.0) pp=0.0;
+       if (pp>256.0) pp=255.0;
+
+       *v=pp;
+       return 0;
+}
+
+//------------------------------------------------------
+//bikubicna interpolacija  "sharp"
+//za byte (char) vrednosti  v packed color 32 bitnem formatu
+//!!! ODKOD SUM???  (ze po eni rotaciji v interp_test !!)
+//!!! v defish tega suma ni???
+int interpBC2_b32(unsigned char *sl, int w, int h, float x, float y, unsigned char *v)
+{
+       int b,i,k,l,m,n,u;
+       float pp,p[4],wx[4],wy[4],xx;
+
+#ifdef TEST_XY_LIMITS
+       if ((x<0)||(x>w)||(y<0)||(y>h)) return -1;
+#endif
+
+       m=(int)ceilf(x)-2; if (m<0) m=0; if ((m+5)>w) m=w-4;
+       n=(int)ceilf(y)-2; if (n<0) n=0; if ((n+5)>h) n=h-4;
+
+       //najprej po y (stiri stolpce)
+       xx=y-n;    wy[0]=(-0.75*(xx-5.0)*xx-6.0)*xx+3.0;
+       xx=xx-1.0; wy[1]=(1.25*xx-2.25)*xx*xx+1.0;
+       xx=1.0-xx; wy[2]=(1.25*xx-2.25)*xx*xx+1.0;
+       xx=xx+1.0; wy[3]=(-0.75*(xx-5.0)*xx-6.0)*xx+3.0;
+       //se po x
+       xx=x-m;    wx[0]=(-0.75*(xx-5.0)*xx-6.0)*xx+3.0;
+       xx=xx-1.0; wx[1]=(1.25*xx-2.25)*xx*xx+1.0;
+       xx=1.0-xx; wx[2]=(1.25*xx-2.25)*xx*xx+1.0;
+       xx=xx+1.0; wx[3]=(-0.75*(xx-5.0)*xx-6.0)*xx+3.0;
+
+       k=4*(n*w+m); u=4*w;
+       for (b=0;b<4;b++)
+       {
+               for (i=0;i<4;i++)
+               {
+                       p[i]=0.0;
+                       l=k+4*i;
+                       p[i]=wy[0]*sl[l]; l+=u;
+                       p[i]+=wy[1]*sl[l]; l+=u;
+                       p[i]+=wy[2]*sl[l]; l+=u;
+                       p[i]+=wy[3]*sl[l];
+               }
+               k++;
+
+               pp=wx[0]*p[0];
+               pp+=wx[1]*p[1];
+               pp+=wx[2]*p[2];
+               pp+=wx[3]*p[3];
+
+               if (pp<0.0) pp=0.0;
+               if (pp>256.0) pp=255.0;
+
+               v[b]=pp;
+       }
+
+       return 0;
+}
+
+//------------------------------------------------------
+//spline 4x4 interpolacija
+//za byte (char) vrednosti
+//Helmut Dersch polinom
+//     *sl vhodni array (slika)
+//     w,h dimenzija slike je wxh
+//     x,y tocka, za katero izracuna interpolirano vrednost
+//     *v interpolirana vrednost
+int interpSP4_b(unsigned char *sl, int w, int h, float x, float y, unsigned char *v)
+{
+       int i,j,m,n;
+       float pp,p[4],wx[4],wy[4],xx;
+
+#ifdef TEST_XY_LIMITS
+       if ((x<0)||(x>w)||(y<0)||(y>h)) return -1;
+#endif
+
+       m=(int)ceilf(x)-2; if (m<0) m=0; if ((m+5)>w) m=w-4;
+       n=(int)ceilf(y)-2; if (n<0) n=0; if ((n+5)>h) n=h-4;
+
+       //najprej po y (stiri stolpce)
+       xx=y-n;    wy[0]=((-0.333333*(xx-1.0)+0.8)*(xx-1.0)-0.466667)*(xx-1.0);
+       xx=xx-1.0; wy[1]=((xx-1.8)*xx-0.2)*xx+1.0;
+       xx=1.0-xx; wy[2]=((xx-1.8)*xx-0.2)*xx+1.0;
+       xx=xx+1.0; wy[3]=((-0.333333*(xx-1.0)+0.8)*(xx-1.0)-0.466667)*(xx-1.0);
+       //se po x
+       xx=x-m;    wx[0]=((-0.333333*(xx-1.0)+0.8)*(xx-1.0)-0.466667)*(xx-1.0);
+       xx=xx-1.0; wx[1]=((xx-1.8)*xx-0.2)*xx+1.0;
+       xx=1.0-xx; wx[2]=((xx-1.8)*xx-0.2)*xx+1.0;
+       xx=xx+1.0; wx[3]=((-0.333333*(xx-1.0)+0.8)*(xx-1.0)-0.466667)*(xx-1.0);
+
+       for (i=0;i<4;i++)
+       {
+               p[i]=0.0;
+               for (j=0;j<4;j++)
+               {
+                       p[i]=p[i]+wy[j]*sl[(j+n)*w+i+m];
+               }
+       }
+
+       pp=0.0;
+       for (i=0;i<4;i++)
+               pp=pp+wx[i]*p[i];
+
+       if (pp<0.0) pp=0.0;
+       if (pp>256.0) pp=255.0;
+
+       *v=pp;
+       return 0;
+}
+
+//------------------------------------------------------
+//spline 4x4 interpolacija
+//za byte (char) vrednosti  v packed color 32 bitnem formatu
+int interpSP4_b32(unsigned char *sl, int w, int h, float x, float y, unsigned char *v)
+{
+       int i,j,m,n,b;
+       float pp,p[4],wx[4],wy[4],xx;
+
+#ifdef TEST_XY_LIMITS
+       if ((x<0)||(x>w)||(y<0)||(y>h)) return -1;
+#endif
+
+       m=(int)ceilf(x)-2; if (m<0) m=0; if ((m+5)>w) m=w-4;
+       n=(int)ceilf(y)-2; if (n<0) n=0; if ((n+5)>h) n=h-4;
+
+       //najprej po y (stiri stolpce)
+       xx=y-n;    wy[0]=((-0.333333*(xx-1.0)+0.8)*(xx-1.0)-0.466667)*(xx-1.0);
+       xx=xx-1.0; wy[1]=((xx-1.8)*xx-0.2)*xx+1.0;
+       xx=1.0-xx; wy[2]=((xx-1.8)*xx-0.2)*xx+1.0;
+       xx=xx+1.0; wy[3]=((-0.333333*(xx-1.0)+0.8)*(xx-1.0)-0.466667)*(xx-1.0);
+       //se po x
+       xx=x-m;    wx[0]=((-0.333333*(xx-1.0)+0.8)*(xx-1.0)-0.466667)*(xx-1.0);
+       xx=xx-1.0; wx[1]=((xx-1.8)*xx-0.2)*xx+1.0;
+       xx=1.0-xx; wx[2]=((xx-1.8)*xx-0.2)*xx+1.0;
+       xx=xx+1.0; wx[3]=((-0.333333*(xx-1.0)+0.8)*(xx-1.0)-0.466667)*(xx-1.0);
+
+       for (b=0;b<4;b++)
+       {
+               for (i=0;i<4;i++)
+               {
+                       p[i]=0.0;
+                       for (j=0;j<4;j++)
+                       {
+                               p[i]=p[i]+wy[j]*sl[4*((j+n)*w+i+m)+b];
+                       }
+               }
+
+               pp=0.0;
+               for (i=0;i<4;i++)
+                       pp=pp+wx[i]*p[i];
+
+               if (pp<0.0) pp=0.0;
+               if (pp>256.0) pp=255.0;
+
+               v[b]=pp;
+       }
+
+       return 0;
+}
+
+//------------------------------------------------------
+//spline 6x6 interpolacija
+//za byte (char) vrednosti
+//Helmut Dersch polinom
+//     *sl vhodni array (slika)
+//     w,h dimenzija slike je wxh
+//     x,y tocka, za katero izracuna interpolirano vrednost
+//     *v interpolirana vrednost
+//!!! PAZI, TOLE NE DELA CISTO PRAV ???   belina se siri
+//!!! zaenkrat sem dodal fudge factor...
+int interpSP6_b(unsigned char *sl, int w, int h, float x, float y, unsigned char *v)
+{
+       int i,j,m,n;
+       float pp,p[6],wx[6],wy[6],xx;
+
+#ifdef TEST_XY_LIMITS
+       if ((x<0)||(x>w)||(y<0)||(y>h)) return -1;
+#endif
+
+       m=(int)ceilf(x)-3; if (m<0) m=0; if ((m+7)>w) m=w-6;
+       n=(int)ceilf(y)-3; if (n<0) n=0; if ((n+7)>h) n=h-6;
+
+       //najprej po y (sest stolpcev)
+       xx=y-n;
+       wy[0]=((0.090909*(xx-2.0)-0.215311)*(xx-2.0)+0.124402)*(xx-2.0);
+       xx=xx-1.0;
+       wy[1]=((-0.545455*(xx-1.0)+1.291866)*(xx-1.0)-0.746411)*(xx-1.0);
+       xx=xx-1.0;
+       wy[2]=((1.181818*xx-2.167464)*xx+0.014354)*xx+1.0;
+       xx=1.0-xx;
+       wy[3]=((1.181818*xx-2.167464)*xx+0.014354)*xx+1.0;
+       xx=xx+1.0;
+       wy[4]=((-0.545455*(xx-1.0)+1.291866)*(xx-1.0)-0.746411)*(xx-1.0);
+       xx=xx+1.0;
+       wy[5]=((0.090909*(xx-2.0)-0.215311)*(xx-2.0)+0.124402)*(xx-2.0);
+       //se po x
+       xx=x-m;
+       wx[0]=((0.090909*(xx-2.0)-0.215311)*(xx-2.0)+0.124402)*(xx-2.0);
+       xx=xx-1.0;
+       wx[1]=((-0.545455*(xx-1.0)+1.291866)*(xx-1.0)-0.746411)*(xx-1.0);
+       xx=xx-1.0;
+       wx[2]=((1.181818*xx-2.167464)*xx+0.014354)*xx+1.0;
+       xx=1.0-xx;
+       wx[3]=((1.181818*xx-2.167464)*xx+0.014354)*xx+1.0;
+       xx=xx+1.0;
+       wx[4]=((-0.545455*(xx-1.0)+1.291866)*(xx-1.0)-0.746411)*(xx-1.0);
+       xx=xx+1.0;
+       wx[5]=((0.090909*(xx-2.0)-0.215311)*(xx-2.0)+0.124402)*(xx-2.0);
+
+
+       for (i=0;i<6;i++)
+       {
+               p[i]=0.0;
+               for (j=0;j<6;j++)
+               {
+                       p[i]=p[i]+wy[j]*sl[(j+n)*w+i+m];
+               }
+       }
+
+       pp=0.0;
+       for (i=0;i<6;i++)
+               pp=pp+wx[i]*p[i];
+
+       pp=0.947*pp;    //fudge factor...!!!    cca 0.947...
+       if (pp<0.0) pp=0.0;
+       if (pp>256.0) pp=255.0;
+
+       *v=pp;
+       return 0;
+}
+
+//------------------------------------------------------
+//spline 6x6 interpolacija
+//za byte (char) vrednosti  v packed color 32 bitnem formatu
+//!!! PAZI, TOLE NE DELA CISTO PRAV ???   belina se siri
+//!!! zaenkrat sem dodal fudge factor...
+int interpSP6_b32(unsigned char *sl, int w, int h, float x, float y, unsigned char *v)
+{
+       int i,b,j,m,n;
+       float pp,p[6],wx[6],wy[6],xx;
+
+#ifdef TEST_XY_LIMITS
+       if ((x<0)||(x>w)||(y<0)||(y>h)) return -1;
+#endif
+
+       m=(int)ceilf(x)-3; if (m<0) m=0; if ((m+7)>w) m=w-6;
+       n=(int)ceilf(y)-3; if (n<0) n=0; if ((n+7)>h) n=h-6;
+
+       //najprej po y (sest stolpcev)
+       xx=y-n;
+       wy[0]=((0.090909*(xx-2.0)-0.215311)*(xx-2.0)+0.124402)*(xx-2.0);
+       xx=xx-1.0;
+       wy[1]=((-0.545455*(xx-1.0)+1.291866)*(xx-1.0)-0.746411)*(xx-1.0);
+       xx=xx-1.0;
+       wy[2]=((1.181818*xx-2.167464)*xx+0.014354)*xx+1.0;
+       xx=1.0-xx;
+       wy[3]=((1.181818*xx-2.167464)*xx+0.014354)*xx+1.0;
+       xx=xx+1.0;
+       wy[4]=((-0.545455*(xx-1.0)+1.291866)*(xx-1.0)-0.746411)*(xx-1.0);
+       xx=xx+1.0;
+       wy[5]=((0.090909*(xx-2.0)-0.215311)*(xx-2.0)+0.124402)*(xx-2.0);
+       //se po x
+       xx=x-m;
+       wx[0]=((0.090909*(xx-2.0)-0.215311)*(xx-2.0)+0.124402)*(xx-2.0);
+       xx=xx-1.0;
+       wx[1]=((-0.545455*(xx-1.0)+1.291866)*(xx-1.0)-0.746411)*(xx-1.0);
+       xx=xx-1.0;
+       wx[2]=((1.181818*xx-2.167464)*xx+0.014354)*xx+1.0;
+       xx=1.0-xx;
+       wx[3]=((1.181818*xx-2.167464)*xx+0.014354)*xx+1.0;
+       xx=xx+1.0;
+       wx[4]=((-0.545455*(xx-1.0)+1.291866)*(xx-1.0)-0.746411)*(xx-1.0);
+       xx=xx+1.0;
+       wx[5]=((0.090909*(xx-2.0)-0.215311)*(xx-2.0)+0.124402)*(xx-2.0);
+
+
+       for (b=0;b<4;b++)
+       {
+               for (i=0;i<6;i++)
+               {
+                       p[i]=0.0;
+                       for (j=0;j<6;j++)
+                       {
+                               p[i]=p[i]+wy[j]*sl[4*((j+n)*w+i+m)+b];
+                       }
+               }
+
+               pp=0.0;
+               for (i=0;i<6;i++)
+                       pp=pp+wx[i]*p[i];
+
+               pp=0.947*pp;    //fudge factor...!!!    cca 0.947...
+               if (pp<0.0) pp=0.0;
+               if (pp>256.0) pp=255.0;
+
+               v[b]=pp;
+       }
+
+       return 0;
+}
+
+//------------------------------------------------------
+//truncated sinc "lanczos" 16x16 interpolacija
+//za byte (char) vrednosti
+//     *sl vhodni array (slika)
+//     w,h dimenzija slike je wxh
+//     x,y tocka, za katero izracuna interpolirano vrednost
+//     *v interpolirana vrednost
+int interpSC16_b(unsigned char *sl, int w, int h, float x, float y, unsigned char *v)
+{
+       int i,j,m,n;
+       float pp,p[16],wx[16],wy[16],xx,xxx,x1;
+       float PI=3.141592654;
+
+#ifdef TEST_XY_LIMITS
+       if ((x<0)||(x>w)||(y<0)||(y>h)) return -1;
+#endif
+
+       m=(int)ceilf(x)-8; if (m<0) m=0; if ((m+17)>w) m=w-16;
+       n=(int)ceilf(y)-8; if (n<0) n=0; if ((n+17)>h) n=h-16;
+
+       //najprej po y
+       xx=y-n;
+       for (i=7;i>=0;i--)
+       {
+               x1=xx*PI;
+               wy[7-i]=(sin(x1)/(x1))*(sin(x1*0.125)/(x1*0.125));
+               xxx=(float)(2*i+1)-xx;
+               x1=xxx*PI;
+               wy[8+i]=(sin(x1)/(x1))*(sin(x1*0.125)/(x1*0.125));
+               xx=xx-1.0;
+       }
+       //se po x
+       xx=x-m;
+       for (i=7;i>=0;i--)
+       {
+               x1=xx*PI;
+               wx[7-i]=(sin(x1)/(x1))*(sin(x1*0.125)/(x1*0.125));
+               xxx=(float)(2*i+1)-xx;
+               x1=xxx*PI;
+               wx[8+i]=(sin(x1)/(x1))*(sin(x1*0.125)/(x1*0.125));
+               xx=xx-1.0;
+       }
+
+       for (i=0;i<16;i++)
+       {
+               p[i]=0.0;
+               for (j=0;j<16;j++)
+               {
+                       p[i]=p[i]+wy[j]*sl[(j+n)*w+i+m];
+               }
+       }
+
+       pp=0.0;
+       for (i=0;i<16;i++)
+               pp=pp+wx[i]*p[i];
+
+       if (pp<0.0) pp=0.0;
+       if (pp>256.0) pp=255.0;
+
+       *v=pp;
+       return 0;
+}
+
+//------------------------------------------------------
+//truncated sinc "lanczos" 16x16 interpolacija
+//za byte (char) vrednosti  v packed color 32 bitnem formatu
+int interpSC16_b32(unsigned char *sl, int w, int h, float x, float y, unsigned char *v)
+{
+       int i,j,m,b,n;
+       float pp,p[16],wx[16],wy[16],xx,xxx,x1;
+       float PI=3.141592654;
+
+#ifdef TEST_XY_LIMITS
+       if ((x<0)||(x>w)||(y<0)||(y>h)) return -1;
+#endif
+
+       m=(int)ceilf(x)-8; if (m<0) m=0; if ((m+17)>w) m=w-16;
+       n=(int)ceilf(y)-8; if (n<0) n=0; if ((n+17)>h) n=h-16;
+
+       //najprej po y
+       xx=y-n;
+       for (i=7;i>=0;i--)
+       {
+               x1=xx*PI;
+               wy[7-i]=(sin(x1)/(x1))*(sin(x1*0.125)/(x1*0.125));
+               xxx=(float)(2*i+1)-xx;
+               x1=xxx*PI;
+               wy[8+i]=(sin(x1)/(x1))*(sin(x1*0.125)/(x1*0.125));
+               xx=xx-1.0;
+       }
+       //se po x
+       xx=x-m;
+       for (i=7;i>=0;i--)
+       {
+               x1=xx*PI;
+               wx[7-i]=(sin(x1)/(x1))*(sin(x1*0.125)/(x1*0.125));
+               xxx=(float)(2*i+1)-xx;
+               x1=xxx*PI;
+               wx[8+i]=(sin(x1)/(x1))*(sin(x1*0.125)/(x1*0.125));
+               xx=xx-1.0;
+       }
+
+       for (b=0;b<4;b++)
+       {
+               for (i=0;i<16;i++)
+               {
+                       p[i]=0.0;
+                       for (j=0;j<16;j++)
+                       {
+                               p[i]=p[i]+wy[j]*sl[4*((j+n)*w+i+m)+b];
+                       }
+               }
+
+               pp=0.0;
+               for (i=0;i<16;i++)
+                       pp=pp+wx[i]*p[i];
+
+               if (pp<0.0) pp=0.0;
+               if (pp>256.0) pp=255.0;
+
+               v[b]=pp;
+       }
+
+       return 0;
+}
index a9a813dafaa18229b0e3a1e9f1e33f4da8d58916..f08a790bb79d49d7a98357b790156971c82991ef 100644 (file)
@@ -1,7 +1,8 @@
 /*
  * transition_affine.c -- affine transformations
- * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Copyright (C) 2003-2010 Ushodaya Enterprises Limited
  * Author: Charles Yates <charles.yates@pandora.be>
+ * Author: Dan Dennedy <dan@dennedy.org>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -27,6 +28,8 @@
 #include <string.h>
 #include <math.h>
 
+#include "interp.h"
+
 /** Calculate real geometry.
 */
 
@@ -240,25 +243,25 @@ static void affine_shear( float this[3][3], float shear_x, float shear_y, float
        affine_multiply( this, affine );
 }
 
-static void affine_offset( float this[3][3], int x, int y )
+static void affine_offset( float this[3][3], float x, float y )
 {
        this[0][2] += x;
        this[1][2] += y;
 }
 
 // Obtain the mapped x coordinate of the input
-static inline double MapX( float this[3][3], int x, int y )
+static inline double MapX( float this[3][3], float x, float y )
 {
        return this[0][0] * x + this[0][1] * y + this[0][2];
 }
 
 // Obtain the mapped y coordinate of the input
-static inline double MapY( float this[3][3], int x, int y )
+static inline double MapY( float this[3][3], float x, float y )
 {
        return this[1][0] * x + this[1][1] * y + this[1][2];
 }
 
-static inline double MapZ( float this[3][3], int x, int y )
+static inline double MapZ( float this[3][3], float x, float y )
 {
        return this[2][0] * x + this[2][1] * y + this[2][2];
 }
@@ -266,16 +269,16 @@ static inline double MapZ( float this[3][3], int x, int y )
 #define MAX( x, y ) x > y ? x : y
 #define MIN( x, y ) x < y ? x : y
 
-static void affine_max_output( float this[3][3], float *w, float *h, float dz )
+static void affine_max_output( float this[3][3], float *w, float *h, float dz, float max_width, float max_height )
 {
-       int tlx = MapX( this, -720, 576 ) / dz;
-       int tly = MapY( this, -720, 576 ) / dz;
-       int trx = MapX( this, 720, 576 ) / dz;
-       int try = MapY( this, 720, 576 ) / dz;
-       int blx = MapX( this, -720, -576 ) / dz;
-       int bly = MapY( this, -720, -576 ) / dz;
-       int brx = MapX( this, 720, -576 ) / dz;
-       int bry = MapY( this, 720, -576 ) / dz;
+       int tlx = MapX( this, -max_width,  max_height ) / dz;
+       int tly = MapY( this, -max_width,  max_height ) / dz;
+       int trx = MapX( this,  max_width,  max_height ) / dz;
+       int try = MapY( this,  max_width,  max_height ) / dz;
+       int blx = MapX( this, -max_width, -max_height ) / dz;
+       int bly = MapY( this, -max_width, -max_height ) / dz;
+       int brx = MapX( this,  max_width, -max_height ) / dz;
+       int bry = MapY( this,  max_width, -max_height ) / dz;
 
        int max_x;
        int max_y;
@@ -298,8 +301,8 @@ static void affine_max_output( float this[3][3], float *w, float *h, float dz )
        min_y = MIN( min_y, bly );
        min_y = MIN( min_y, bry );
 
-       *w = ( float )( max_x - min_x + 1 ) / 1440.0;
-       *h = ( float )( max_y - min_y + 1 ) / 1152.0;
+       *w = ( float )( max_x - min_x + 1 ) / max_width / 2.0;
+       *h = ( float )( max_y - min_y + 1 ) / max_height / 2.0;
 }
 
 #define IN_RANGE( v, r )       ( v >= - r / 2 && v < r / 2 )
@@ -308,7 +311,6 @@ static inline void get_affine( affine_t *affine, mlt_transition this, float posi
 {
        mlt_properties properties = MLT_TRANSITION_PROPERTIES( this );
        int keyed = mlt_properties_get_int( properties, "keyed" );
-       affine_init( affine->matrix );
 
        if ( keyed == 0 )
        {
@@ -374,7 +376,7 @@ static int transition_get_image( mlt_frame a_frame, uint8_t **image, mlt_image_f
 
        // Image, format, width, height and image for the b frame
        uint8_t *b_image = NULL;
-       mlt_image_format b_format = mlt_image_yuv422;
+       mlt_image_format b_format = mlt_image_rgb24a;
        int b_width;
        int b_height;
 
@@ -392,7 +394,9 @@ static int transition_get_image( mlt_frame a_frame, uint8_t **image, mlt_image_f
        int normalised_width = mlt_properties_get_int( a_props, "normalised_width" );
        int normalised_height = mlt_properties_get_int( a_props, "normalised_height" );
 
-       double consumer_ar = mlt_properties_get_double( a_props, "consumer_aspect_ratio" ) ;
+       double consumer_ar = mlt_properties_get_double( a_props, "consumer_aspect_ratio" );
+       const char *interps = mlt_properties_get( b_props, "rescale.interp" );
+       mlt_profile profile = mlt_service_profile( MLT_TRANSITION_SERVICE(this) );
 
        // Structures for geometry
        struct mlt_geometry_item_s result;
@@ -401,167 +405,136 @@ static int transition_get_image( mlt_frame a_frame, uint8_t **image, mlt_image_f
                position = abs( position - length );
 
        // Fetch the a frame image
-       *format = mlt_image_yuv422;
+       *format = mlt_image_rgb24a;
        mlt_frame_get_image( a_frame, image, format, width, height, 1 );
 
        // Calculate the region now
        composite_calculate( this, &result, normalised_width, normalised_height, ( float )position );
 
        // Fetch the b frame image
-       result.w = ( int )( result.w * *width / normalised_width );
-       result.h = ( int )( result.h * *height / normalised_height );
-       result.x = ( int )( result.x * *width / normalised_width );
-       result.y = ( int )( result.y * *height / normalised_height );
-       //result.w -= ( int )abs( result.w ) % 2;
-       //result.x -= ( int )abs( result.x ) % 2;
-       b_width = result.w;
-       b_height = result.h;
-
-       if ( mlt_properties_get_double( b_props, "aspect_ratio" ) == 0.0 )
-               mlt_properties_set_double( b_props, "aspect_ratio", consumer_ar );
-
-       if ( !strcmp( mlt_properties_get( a_props, "rescale.interp" ), "none" ) )
+       result.w = ( result.w * *width / normalised_width );
+       result.h = ( result.h * *height / normalised_height );
+       result.x = ( result.x * *width / normalised_width );
+       result.y = ( result.y * *height / normalised_height );
+       b_width = mlt_properties_get_int(b_props, "real_width");
+       b_height = mlt_properties_get_int(b_props, "real_height");
+       if ( 0 && b_width > result.w * 2 && b_height > result.h * 2 )
        {
-               mlt_properties_set( b_props, "rescale.interp", "nearest" );
-               mlt_properties_set_double( b_props, "consumer_aspect_ratio", consumer_ar );
+               // This downscale can reduce aliasing by acting as a low pass filter.
+               b_width = b_width / 2;
+               b_height = b_height / 2;
        }
-       else
+       mlt_properties_set_int( b_props, "rescale_width", b_width );
+       mlt_properties_set_int( b_props, "rescale_height", b_height );
+       if ( mlt_properties_get_double( b_props, "aspect_ratio" ) == 0.0 )
+               mlt_properties_set_double( b_props, "aspect_ratio", consumer_ar );
+       mlt_properties_set_int( b_props, "progressive", 1 );
+       if ( interps == NULL || !strcmp( interps, "none" ) )
        {
-               mlt_properties_set( b_props, "rescale.interp", mlt_properties_get( a_props, "rescale.interp" ) );
+               mlt_properties_set( b_props, "rescale.interp", "bilinear" );
                mlt_properties_set_double( b_props, "consumer_aspect_ratio", consumer_ar );
        }
-
-       mlt_properties_set_int( b_props, "distort", mlt_properties_get_int( properties, "distort" ) );
        mlt_frame_get_image( b_frame, &b_image, &b_format, &b_width, &b_height, 0 );
-       result.w = b_width;
-       result.h = b_height;
 
        // Check that both images are of the correct format and process
-       if ( *format == mlt_image_yuv422 && b_format == mlt_image_yuv422 )
+       if ( *format == mlt_image_rgb24a && b_format == mlt_image_rgb24a )
        {
-               register int x, y;
-               register int dx, dy;
-               double dz;
+               float x, y;
+               float dx, dy;
+               float dz;
                float sw, sh;
+               uint8_t *p = *image;
 
                // Get values from the transition
                float scale_x = mlt_properties_get_double( properties, "scale_x" );
                float scale_y = mlt_properties_get_double( properties, "scale_y" );
                int scale = mlt_properties_get_int( properties, "scale" );
-
-               uint8_t *p = *image;
-               uint8_t *q = *image;
-
-               int cx = result.x + ( b_width >> 1 );
-               int cy = result.y + ( b_height >> 1 );
-               cx -= cx % 2;
-
-               int lower_x = 0 - cx;
-               int upper_x = *width - cx;
-               int lower_y = 0 - cy;
-               int upper_y = *height - cy;
-
-               int b_stride = b_width << 1;
-               int a_stride = *width << 1;
-               int x_offset = ( int )result.w >> 1;
-               int y_offset = ( int )result.h >> 1;
-
-               uint8_t *alpha = mlt_frame_get_alpha_mask( b_frame );
-               uint8_t *mask = mlt_frame_get_alpha_mask( a_frame );
-               uint8_t *pmask = mask;
-               float mix;
-
+               float sar = (float) profile->sample_aspect_num / profile->sample_aspect_den;
+               float geom_scale_x = (float) b_width / result.w / sar;
+               float geom_scale_y = (float) b_height / result.h * sar;
+               float cx = result.x + result.w / 2.0;
+               float cy = result.y + result.h / 2.0;
+               float lower_x = - cx;
+               float upper_x = (float) *width - cx;
+               float lower_y = - cy;
+               float upper_y = (float) *height - cy;
+               float x_offset = (float) b_width / 2.0;
+               float y_offset = (float) b_height / 2.0;
+               float sar_affine[3][3];
                affine_t affine;
+               interpp interp = interpBL_b32;
 
-               get_affine( &affine, this, ( float )position );
+               affine_init( affine.matrix );
 
-               q = *image;
+               // Factor aspect ratio into transforms
+               affine_init( sar_affine);
+               sar_affine[0][0] /= sar;
+               affine_multiply( affine.matrix, sar_affine );
 
+               // Compute the affine transform
+               get_affine( &affine, this, ( float )position );
                dz = MapZ( affine.matrix, 0, 0 );
+               if ( ( int )abs( dz * 1000 ) < 25 )
+                       return 0;
 
-               if ( mask == NULL )
+               // Factor scaling into the transformation based on output resolution.
+               if ( mlt_properties_get_int( properties, "distort" ) )
                {
-                       mask = mlt_pool_alloc( *width * *height );
-                       pmask = mask;
-                       memset( mask, 255, *width * *height );
+                       scale_x = geom_scale_x * ( scale_x == 0 ? 1 : scale_x );
+                       scale_y = geom_scale_y / sar * ( scale_y == 0 ? 1 : scale_y );
+               }
+               else
+               {
+                       float scaling = MIN( geom_scale_x, geom_scale_y );
+                       if ( b_height / scaling > result.h / sar )
+                               scaling = geom_scale_y / sar;
+                       else if ( b_width / scaling > result.w * sar )
+                               scaling = geom_scale_x * sar;
+                       scale_x = scaling * ( scale_x == 0 ? 1 : scale_x );
+                       scale_y = scaling * ( scale_y == 0 ? 1 : scale_y );
                }
-
-               if ( ( int )abs( dz * 1000 ) < 25 )
-                       goto getout;
-
                if ( scale )
                {
-                       affine_max_output( affine.matrix, &sw, &sh, dz );
-                       affine_scale( affine.matrix, sw, sh );
+                       affine_max_output( affine.matrix, &sw, &sh, dz, profile->width, profile->height );
+                       affine_scale( affine.matrix, sw * MIN( geom_scale_x, geom_scale_y ), sh * MIN( geom_scale_x, geom_scale_y ) );
                }
                else if ( scale_x != 0 && scale_y != 0 )
                {
                        affine_scale( affine.matrix, scale_x, scale_y );
                }
 
-               if ( alpha == NULL )
-               {
-                       for ( y = lower_y; y < upper_y; y ++ )
-                       {
-                               p = q;
-
-                               for ( x = lower_x; x < upper_x; x ++ )
-                               {
-                                       dx = MapX( affine.matrix, x, y ) / dz + x_offset;
-                                       dy = MapY( affine.matrix, x, y ) / dz + y_offset;
-
-                                       if ( dx >= 0 && dx < b_width && dy >=0 && dy < b_height )
-                                       {
-                                               pmask ++;
-                                               dx -= dx & 1;
-                                               *p ++ = *( b_image + dy * b_stride + ( dx << 1 ) );
-                                               *p ++ = *( b_image + dy * b_stride + ( dx << 1 ) + ( ( x & 1 ) << 1 ) + 1 );
-                                       }
-                                       else
-                                       {
-                                               p += 2;
-                                               pmask ++;
-                                       }
-                               }
-
-                               q += a_stride;
-                       }
-               }
-               else
+               // Invert transform aspect ratio factor
+               sar_affine[0][0] *= sar; // return to identity matrix
+               sar_affine[0][0] *= sar; // reverse the sample aspect adjustment
+               affine_multiply( affine.matrix, sar_affine );
+
+               // Set the interpolation function
+               if ( interps == NULL || strcmp( interps, "nearest" ) == 0 || strcmp( interps, "neighbor" ) == 0 )
+                       interp = interpNN_b32;
+               else if ( strcmp( interps, "tiles" ) == 0 || strcmp( interps, "fast_bilinear" ) == 0 )
+                       interp = interpNN_b32;
+               else if ( strcmp( interps, "bilinear" ) == 0 )
+                       interp = interpBL_b32;
+               else if ( strcmp( interps, "bicubic" ) == 0 )
+                       interp = interpBC_b32;
+                // TODO: lanczos 8x8
+               else if ( strcmp( interps, "hyper" ) == 0 || strcmp( interps, "sinc" ) == 0 || strcmp( interps, "lanczos" ) == 0 )
+                       interp = interpBC_b32;
+               else if ( strcmp( interps, "spline" ) == 0 ) // TODO: spline 4x4 or 6x6
+                       interp = interpBC_b32;
+
+               // Do the transform with interpolation
+               for ( y = lower_y; y < upper_y; y ++ )
                {
-                       for ( y = lower_y; y < upper_y; y ++ )
+                       for ( x = lower_x; x < upper_x; x ++ )
                        {
-                               p = q;
-
-                               for ( x = lower_x; x < upper_x; x ++ )
-                               {
-                                       dx = MapX( affine.matrix, x, y ) / dz + x_offset;
-                                       dy = MapY( affine.matrix, x, y ) / dz + y_offset;
-
-                                       if ( dx >= 0 && dx < b_width && dy >=0 && dy < b_height )
-                                       {
-                                               *pmask ++ = *( alpha + dy * b_width + dx );
-                                               mix = ( float )*( alpha + dy * b_width + dx ) / 255.0;
-                                               dx -= dx & 1;
-                                               *p = *p * ( 1 - mix ) + mix * *( b_image + dy * b_stride + ( dx << 1 ) );
-                                               p ++;
-                                               *p = *p * ( 1 - mix ) + mix * *( b_image + dy * b_stride + ( dx << 1 ) + ( ( x & 1 ) << 1 ) + 1 );
-                                               p ++;
-                                       }
-                                       else
-                                       {
-                                               p += 2;
-                                               pmask ++;
-                                       }
-                               }
-
-                               q += a_stride;
+                               dx = MapX( affine.matrix, x, y ) / dz + x_offset;
+                               dy = MapY( affine.matrix, x, y ) / dz + y_offset;
+                               if ( dx >= 0 && dx < b_width && dy >=0 && dy < b_height )
+                                       interp( b_image, b_width, b_height, dx, dy, p );
+                               p += 4;
                        }
                }
-
-getout:
-               a_frame->get_alpha_mask = NULL;
-               mlt_properties_set_data( a_props, "alpha", mask, 0, mlt_pool_release, NULL );
        }
 
        return 0;
@@ -599,8 +572,6 @@ mlt_transition transition_affine_init( mlt_profile profile, mlt_service_type typ
        mlt_transition transition = mlt_transition_new( );
        if ( transition != NULL )
        {
-               mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "sx", 1 );
-               mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "sy", 1 );
                mlt_properties_set_int( MLT_TRANSITION_PROPERTIES( transition ), "distort", 0 );
                mlt_properties_set( MLT_TRANSITION_PROPERTIES( transition ), "geometry", "0,0:100%x100%" );
                // Inform apps and framework that this is a video only transition