C# 图片自由变换 任意扭曲
之前想过要做个地铁驾驶的游戏,其中想把一些原本是矩形图片弄成一个梯形,但是发现GID+上面没有类似的方法。于是在谷歌谷了一下。没有!只能找到令人垂涎的,并没有源码。按照自己的想法尝试了一两天,有点效果,但实际上不是那样。后来知道那个在数字图像处理中叫“透视变换”。于是上网找了相关资料,原理找了,看了不明白。代码没多少,有ActionScript的,不明;有C的,不明。真笨啊!后来在CodeProject上面看到一份外国人的博文,全英文看不太明白,但看了一幅图,大概知道他意思了。下了份源码看看,C++的。好不容易翻译成C#的(感觉还是保留了不少C++风格的东西),编译通过,运行正常。后来才一步一步的阅读代码。还没全懂,先把懂的部分记录下来。以后继续研究继续补充。
先看看效果


界面是仿照某个人(网上版本太多,找不到原作者)的弄出来的,界面不是重点,重点是算法。下面就直接贴老外的那幅图大致讲讲思想。

首先是从原本图片转化成一幅理想化的目标图片,那幅图片只是理想化的,最终的图片是最右边的那幅。转换的过程就是根据转换后图片的四个角计算出目标图片的size,生成一个矩阵,就是那个Destination Image,然后把理想化的目标图片覆盖过去,把理想化图片的每个“像素格”(已经不是真正的像素格了,因为经过了扭曲变形)跟那个矩阵对比,看看覆盖了哪些格子,覆盖的面积有多少,按百分比地把颜色累加到对应的格子上。实际上那个格子就相当于新图片的像素点了。按照矩阵生成最终的目标图。
接着就介绍算法里面调用的方法层次

把已经弄懂(并不代表完全懂的)的代码贴出来,首先是最外层的方法
public void CreateTransform(Bitmap src,ref Bitmap dst, List<double> xcorner, List<double> ycorner, Aaf_callback callbackfunc)
{
int right = , bottom = ; //主要是根据新图片的坐标,计算出图片的宽和高,构造目标图片的Bitmap的
double offx = xcorner[];
double offy = ycorner[];
for (int i = ; i < ; i++)
{
if (xcorner[i] < offx) offx = xcorner[i];
if (ycorner[i] < offy) offy = ycorner[i];
} for (int i = ; i < ; i++)
{
xcorner[i] -= offx;
ycorner[i] -= offy;
if (roundup(xcorner[i]) > right) right = roundup(xcorner[i]);
if (roundup(ycorner[i]) > bottom) bottom = roundup(ycorner[i]);
}
dst = new Bitmap(right, bottom);
Transform(src, dst, xcorner, ycorner, null);
}
上面这个方法只是定了目标图片的尺寸,其余什么都没做。下面这个方法还没做多少转换的事
private void Transform(Bitmap src,Bitmap dst, List<double> xcorner, List<double> ycorner, Aaf_callback callbackfunc)
{
//Make sure the coordinates are valid
if (xcorner.Count != || ycorner.Count != )
return ; //Load the src bitmaps information //create the intial arrays
//根据原图片生成一个比原图宽高多一个单位的图片,
//这个矩阵就是用来记录转换后各个像素点左上角的坐标
pixelgrid = new AafPnt[(src.Width + ) * (src.Height + )];
polyoverlap = new AafPnt[];
polysorted = new AafPnt[];
corners = new AafPnt[]; //Load the corners array
double[] dx = { 0.0, 1.0, 1.0, 0.0 };
double[] dy = { 0.0, 0.0, 1.0, 1.0 };
for (int i = ; i < ; i++)
{
corners[i].x = dx[i];
corners[i].y = dy[i];
} //Find the rectangle of dst to draw to
outstartx = rounddown(xcorner[]);
outstarty = rounddown(ycorner[]);
outwidth = ;
outheight = ;
//这里计算出变换后起始点的坐标
for (int i = ; i < ; i++)
{
if (rounddown(xcorner[i]) < outstartx) outstartx = rounddown(xcorner[i]);
if (rounddown(ycorner[i]) < outstarty) outstarty = rounddown(ycorner[i]);
}
for (int i = ; i < ; i++)
{
if (roundup(xcorner[i] - outstartx) > outwidth) outwidth = roundup(xcorner[i] - outstartx);
if (roundup(ycorner[i] - outstarty) > outheight) outheight = roundup(ycorner[i] - outstarty);
} //fill out pixelgrid array
//计算出理想目标图片中各个“像素格”中的左上角的坐标
if (CreateGrid(src, xcorner, ycorner))
{
//Do the transformation
//进行转换
DoTransform(src,dst, callbackfunc);
} //Return if the function completed properly
return ;
}
下面这个方法则是计算出原图像中每个像素点的左上角的点到目标图像中的坐标,结果是存放在pixelgrid中,这个二维数组的行和列都比原图像的宽高多1,这个关系我当初没搞懂。用比较极限的思想,假设现在这幅图片只有一个像素组成,宽和高都是1,然后如果单纯存储一个左上角的坐标,是无法组成一个四边形的,这就需要把其他三个角的坐标相应地记录,这是存储的数组的行和列均要比原图的宽和高多1,就是也就是一个2行2列的数组能存放4个数值,刚好就容纳了那一个像素点的四个角的坐标值。扩大到真实的图片也同样道理。不过这个方法我看不明白,貌似用到了向量的思想。大致是按照新图片四条边来计算的。
private bool CreateGrid(Bitmap src, List<double> xcorner, List<double> ycorner)
{
//mmm geometry
double[] sideradius = new double[];
double[] sidecos = new double[];
double[] sidesin = new double[]; //First we find the radius, cos, and sin of each side of the polygon created by xcorner and ycorner
int j;
for (int i = ; i < ; i++)
{
j = ja[i];
sideradius[i] = Math.Sqrt((xcorner[i] - xcorner[j]) * (xcorner[i] - xcorner[j]) + (ycorner[i] - ycorner[j]) * (ycorner[i] - ycorner[j]));
sidecos[i] = (xcorner[j] - xcorner[i]) / sideradius[i];
sidesin[i] = (ycorner[j] - ycorner[i]) / sideradius[i];
} //Next we create two lines in Ax + By = C form
for (int x = ; x < src.Width + ; x++)
{
double topdist = ((double)x / (src.Width)) * sideradius[];
double ptxtop = xcorner[] + topdist * sidecos[];
double ptytop = ycorner[] + topdist * sidesin[]; double botdist = (1.0 - (double)x / (src.Width)) * sideradius[];
double ptxbot = xcorner[] + botdist * sidecos[];
double ptybot = ycorner[] + botdist * sidesin[]; double Ah = ptybot - ptytop;
double Bh = ptxtop - ptxbot;
double Ch = Ah * ptxtop + Bh * ptytop;//叉乘 for (int y = ; y < src.Height + ; y++)
{
double leftdist = (1.0 - (double)y / (src.Height)) * sideradius[];
double ptxleft = xcorner[] + leftdist * sidecos[];
double ptyleft = ycorner[] + leftdist * sidesin[]; double rightdist = ((double)y / (src.Height)) * sideradius[];
double ptxright = xcorner[] + rightdist * sidecos[];
double ptyright = ycorner[] + rightdist * sidesin[]; double Av = ptyright - ptyleft;
double Bv = ptxleft - ptxright;
double Cv = Av * ptxleft + Bv * ptyleft; //Find where the lines intersect and store that point in the pixelgrid array
double det = Ah * Bv - Av * Bh;
if (AafAbs(det) < 1e-)
{
return false;
}
else
{
int ind = x + y * (src.Width + );
pixelgrid[ind].x = (Bv * Ch - Bh * Cv) / det;
pixelgrid[ind].y = (Ah * Cv - Av * Ch) / det;
}
}
} //Yayy we didn't fail
return true;
}
下面这个方法就利用上面的方法计算出的坐标点集合进行按比例填色。上面每个像素点的四个角的坐标,都会在下面方法重新提取出来组回一个四边形,具体还是结合代码和注释看看
private void DoTransform(Bitmap src,Bitmap dst, Aaf_callback callbackfunc)
{ //Get source bitmap's information
if (src == null) return ; //Create the source dib array and the dstdib array
aaf_dblrgbquad[] dbldstdib = new aaf_dblrgbquad[outwidth * outheight];
for (int i = ; i < dbldstdib.Length; i++)
dbldstdib[i] = new aaf_dblrgbquad(); //Create polygon arrays
AafPnt[] p = new AafPnt[];
AafPnt[] poffset = new AafPnt[]; //Loop through the source's pixels
//遍历原图(实质上是pixelgrid)各个点
for (int x = ; x < src.Width; x++)
{
for (int y = ; y < src.Height; y++)
{
//取当前点 下一点 右一点 斜右下角点 四点才组成一个四边形
//这个四边形是原图像的一个像素点
//Construct the source pixel's rotated polygon from pixelgrid
p[] = pixelgrid[x + y * (src.Width + )];
p[] = pixelgrid[(x + ) + y * (src.Width + )];
p[] = pixelgrid[(x + ) + (y + ) * (src.Width + )];
p[] = pixelgrid[x + (y + ) * (src.Width + )]; //Find the scan area on the destination's pixels
int mindx = int.MaxValue;
int mindy = int.MaxValue;
int maxdx = int.MinValue;
int maxdy = int.MinValue;
for (int i = ; i < ; i++)
{
if (rounddown(p[i].x) < mindx) mindx = rounddown(p[i].x);
if (roundup(p[i].x) > maxdx) maxdx = roundup(p[i].x);
if (rounddown(p[i].y) < mindy) mindy = rounddown(p[i].y);
if (roundup(p[i].y) > maxdy) maxdy = roundup(p[i].y);
} int SrcIndex = x + y * src.Width;
//遍历四边形包含了目标图几个像素点
//按照相交面积占整个像素的的百分比,把颜色按照该比例存放一个目标像素点颜色的数组中
//这里计算出来的颜色只是初步颜色,还没到最终结果
//loop through the scan area to find where source(x, y) overlaps with the destination pixels
for (int xx = mindx - ; xx <= maxdx; xx++)
{
if (xx < || xx >= dst.Width)
continue;
for (int yy = mindy - ; yy <= maxdy; yy++)
{
if (yy < || yy >= dst.Height)
continue; //offset p and by (xx,yy) and put that into poffset
for (int i = ; i < ; i++)
{
poffset[i].x = p[i].x - xx;
poffset[i].y = p[i].y - yy;
} //FIND THE OVERLAP *a whole lot of code pays off here*
//这里则是计算出覆盖了面积占当前像素的百分比
double dbloverlap = PixOverlap(poffset);
//按照百分比来为目标像素点累加颜色
//因为一个目标像素点有可能有几个原来像素的覆盖了
if (dbloverlap > )
{
int dstindex = xx + yy * outwidth;
int srcWidth = src.Width;
Color srcColor;
if (SrcIndex == )
srcColor = src.GetPixel(, );
else
srcColor = src.GetPixel(SrcIndex%src.Width , SrcIndex/src.Width );
//Add the rgb and alpha values in proportion to the overlap area
dbldstdib[dstindex].Red += (double)((srcColor.R) * dbloverlap);
dbldstdib[dstindex].Blue += (double)(srcColor.B) * dbloverlap;
dbldstdib[dstindex].Green += (double)(srcColor.G) * dbloverlap;
dbldstdib[dstindex].Alpha += dbloverlap;
}
}
}
}
if (callbackfunc != null)
{
//Send the callback message
double percentdone = (double)(x + ) / (double)(src.Width);
if (callbackfunc(percentdone))
{
dbldstdib = null;
p = null;
poffset = null;
return ;
}
}
} //Free memory no longer needed //Create final destination bits
RGBQUDA[] dstdib = new RGBQUDA[dst.Width * dst.Height];
for (int i = ; i < dstdib.Length; i++)
dstdib[i] = new RGBQUDA(){R= ,G= ,B= }; //这里是实际上真正像素点的颜色,并且填到了目标图片中去
//Write to dstdib with the information stored in dbldstdib
for (int x = ; x < outwidth; x++)
{
if (x + outstartx >= dst.Width)
continue;
for (int y = ; y < outheight; y++)
{
if (y + outstarty >= dst.Height)
continue;
int offindex = x + y * outwidth;
int dstindex = x + outstartx + (y + outstarty) * dst.Width; int dstIndexX = dstindex / dst.Width;
int dstIndexY = dstindex % dst.Width;
if (dbldstdib[offindex].Alpha > )
{
//handles wrap around for non-convex transformations
dstdib[dstindex].R = byterange(dbldstdib[offindex].Red / dbldstdib[offindex].Alpha);
dstdib[dstindex].G = byterange(dbldstdib[offindex].Green / dbldstdib[offindex].Alpha);
dstdib[dstindex].B = byterange(dbldstdib[offindex].Blue / dbldstdib[offindex].Alpha);
}
else
{
//Color dstColor = dst.GetPixel(dstIndexX, dstIndexY);
dstdib[dstindex].R = byterange(dbldstdib[offindex].Red + ( - dbldstdib[offindex].Alpha) * (double)dstdib[dstindex].R);
dstdib[dstindex].G = byterange(dbldstdib[offindex].Green + ( - dbldstdib[offindex].Alpha) * (double)dstdib[dstindex].G);
dstdib[dstindex].B = byterange(dbldstdib[offindex].Blue + ( - dbldstdib[offindex].Alpha) * (double)dstdib[dstindex].B);
}
dst.SetPixel(dstIndexY,dstIndexX , Color.FromArgb(dstdib[dstindex].R, dstdib[dstindex].G, dstdib[dstindex].B));
}
} //:D
return ;
}
里面调用到的计算相交面积的方法PixOverlap就不列出来了,因为还没看懂,看明白了也会在本文中补充。若想看的,本文最后会贴出所有源码。
希望有看的明白的朋友能多指点一下,谢谢!还要感谢一个人,sa姐,在我阅读这个算法时给了不少灵感为我。搞这个算法,让我想起了大三上的一门课《医学图像处理》,我的老师涂泳秋老师。
     public delegate bool Aaf_callback(double paraDouble);
     struct AafPnt
     {
         public double x, y;
         public AafPnt(double x, double y)
         {
             this.x = x <  ?  : x;
             this.y = y <  ?  : y;
         }
     }
     class aaf_dblrgbquad
     {
         public double Red{get;set;}
         public double Green{get;set;}
         public double  Blue{get;set;}
         public double Alpha { get; set; }
     }
     class aaf_indll
     {
         public aaf_indll next;
         public int ind;
     }
     class Aaform
     {
         private AafPnt[] pixelgrid;
         private AafPnt[] polyoverlap;
         private AafPnt[] polysorted;
         private AafPnt[] corners;
         private int outstartx;
         private int outstarty;
         private int outwidth;
         private int outheight;
         int polyoverlapsize;
         int polysortedsize;
         int[] ja = new int[] { , , ,  };
         public void CreateTransform(Bitmap src,ref Bitmap dst, List<double> xcorner, List<double> ycorner, Aaf_callback callbackfunc)
         {
             int right = , bottom = ;
             //主要是根据新图片的坐标,计算出图片的宽和高,构造目标图片的Bitmap的
             double offx = xcorner[];
             double offy = ycorner[];
             for (int i = ; i < ; i++)
             {
                 if (xcorner[i] < offx) offx = xcorner[i];
                 if (ycorner[i] < offy) offy = ycorner[i];
             }
             for (int i = ; i < ; i++)
             {
                 xcorner[i] -= offx;
                 ycorner[i] -= offy;
                 if (roundup(xcorner[i]) > right) right = roundup(xcorner[i]);
                 if (roundup(ycorner[i]) > bottom) bottom = roundup(ycorner[i]);
             }
             dst = new Bitmap(right, bottom);
             Transform(src, dst, xcorner, ycorner, null);
         }
         private void Transform(Bitmap src,Bitmap dst, List<double> xcorner, List<double> ycorner, Aaf_callback callbackfunc)
         {
             //Make sure the coordinates are valid
             if (xcorner.Count !=  || ycorner.Count != )
                 return ;
             //Load the src bitmaps information
             //create the intial arrays
             //根据原图片生成一个比原图宽高多一个单位的图片,
             //这个矩阵就是用来记录转换后各个像素点左上角的坐标
             pixelgrid = new AafPnt[(src.Width + ) * (src.Height + )];
             polyoverlap = new AafPnt[];
             polysorted = new AafPnt[];
             corners = new AafPnt[];
             //Load the corners array
             double[] dx = { 0.0, 1.0, 1.0, 0.0 };
             double[] dy = { 0.0, 0.0, 1.0, 1.0 };
             for (int i = ; i < ; i++)
             {
                 corners[i].x = dx[i];
                 corners[i].y = dy[i];
             }
             //Find the rectangle of dst to draw to
             outstartx = rounddown(xcorner[]);
             outstarty = rounddown(ycorner[]);
             outwidth = ;
             outheight = ;
             //这里计算出变换后起始点的坐标
             for (int i = ; i < ; i++)
             {
                 if (rounddown(xcorner[i]) < outstartx) outstartx = rounddown(xcorner[i]);
                 if (rounddown(ycorner[i]) < outstarty) outstarty = rounddown(ycorner[i]);
             }
             for (int i = ; i < ; i++)
             {
                 if (roundup(xcorner[i] - outstartx) > outwidth) outwidth = roundup(xcorner[i] - outstartx);
                 if (roundup(ycorner[i] - outstarty) > outheight) outheight = roundup(ycorner[i] - outstarty);
             }
             //fill out pixelgrid array
             //计算出理想目标图片中各个“像素格”中的左上角的坐标
             if (CreateGrid(src, xcorner, ycorner))
             {
                 //Do the transformation
                 //进行转换
                DoTransform(src,dst, callbackfunc);
             }
             //Return if the function completed properly
             return ;
         }
         private bool CreateGrid(Bitmap src, List<double> xcorner, List<double> ycorner)
         {
             //mmm geometry
             double[] sideradius = new double[];
             double[] sidecos = new double[];
             double[] sidesin = new double[];
             //First we find the radius, cos, and sin of each side of the polygon created by xcorner and ycorner
             int j;
             for (int i = ; i < ; i++)
             {
                 j = ja[i];
                 sideradius[i] = Math.Sqrt((xcorner[i] - xcorner[j]) * (xcorner[i] - xcorner[j]) + (ycorner[i] - ycorner[j]) * (ycorner[i] - ycorner[j]));
                 sidecos[i] = (xcorner[j] - xcorner[i]) / sideradius[i];
                 sidesin[i] = (ycorner[j] - ycorner[i]) / sideradius[i];
             }
             //Next we create two lines in Ax + By = C form
             for (int x = ; x < src.Width + ; x++)
             {
                 double topdist = ((double)x / (src.Width)) * sideradius[];//每个像素点变换后的坐标点
                 double ptxtop = xcorner[] + topdist * sidecos[];
                 double ptytop = ycorner[] + topdist * sidesin[];
                 double botdist = (1.0 - (double)x / (src.Width)) * sideradius[];
                 double ptxbot = xcorner[] + botdist * sidecos[];
                 double ptybot = ycorner[] + botdist * sidesin[];
                 double Ah = ptybot - ptytop;
                 double Bh = ptxtop - ptxbot;
                 double Ch = Ah * ptxtop + Bh * ptytop;//叉乘
                 for (int y = ; y < src.Height + ; y++)
                 {
                     double leftdist = (1.0 - (double)y / (src.Height)) * sideradius[];
                     double ptxleft = xcorner[] + leftdist * sidecos[];
                     double ptyleft = ycorner[] + leftdist * sidesin[];
                     double rightdist = ((double)y / (src.Height)) * sideradius[];
                     double ptxright = xcorner[] + rightdist * sidecos[];
                     double ptyright = ycorner[] + rightdist * sidesin[];
                     double Av = ptyright - ptyleft;
                     double Bv = ptxleft - ptxright;
                     double Cv = Av * ptxleft + Bv * ptyleft;
                     //Find where the lines intersect and store that point in the pixelgrid array
                     double det = Ah * Bv - Av * Bh;
                     if (AafAbs(det) < 1e-)
                     {
                         return false;
                     }
                     else
                     {
                         int ind = x + y * (src.Width + );
                         pixelgrid[ind].x = (Bv * Ch - Bh * Cv) / det;
                         pixelgrid[ind].y = (Ah * Cv - Av * Ch) / det;
                     }
                 }
             }
             //Yayy we didn't fail
             return true;
         }
         private void DoTransform(Bitmap src,Bitmap dst, Aaf_callback callbackfunc)
         {
             //Get source bitmap's information
             if (src == null) return ;
             //Create the source dib array and the dstdib array
             aaf_dblrgbquad[] dbldstdib = new aaf_dblrgbquad[outwidth * outheight];
             for (int i = ; i < dbldstdib.Length; i++)
                 dbldstdib[i] = new aaf_dblrgbquad();
             //Create polygon arrays
             AafPnt[] p = new AafPnt[];
             AafPnt[] poffset = new AafPnt[];
             //Loop through the source's pixels
             //遍历原图(实质上是pixelgrid)各个点
             for (int x = ; x < src.Width; x++)
             {
                 for (int y = ; y < src.Height; y++)
                 {
                     //取当前点 下一点 右一点 斜右下角点 四点才组成一个四边形
                     //这个四边形是原图像的一个像素点
                     //Construct the source pixel's rotated polygon from pixelgrid
                     p[] = pixelgrid[x + y * (src.Width + )];
                     p[] = pixelgrid[(x + ) + y * (src.Width + )];
                     p[] = pixelgrid[(x + ) + (y + ) * (src.Width + )];
                     p[] = pixelgrid[x + (y + ) * (src.Width + )];
                     //Find the scan area on the destination's pixels
                     int mindx = int.MaxValue;
                     int mindy = int.MaxValue;
                     int maxdx = int.MinValue;
                     int maxdy = int.MinValue;
                     for (int i = ; i < ; i++)
                     {
                         if (rounddown(p[i].x) < mindx) mindx = rounddown(p[i].x);
                         if (roundup(p[i].x) > maxdx) maxdx = roundup(p[i].x);
                         if (rounddown(p[i].y) < mindy) mindy = rounddown(p[i].y);
                         if (roundup(p[i].y) > maxdy) maxdy = roundup(p[i].y);
                     }
                     int SrcIndex = x + y * src.Width;
                     //遍历四边形包含了目标图几个像素点
                     //按照相交面积占整个像素的的百分比,把颜色按照该比例存放一个目标像素点颜色的数组中
                     //这里计算出来的颜色只是初步颜色,还没到最终结果
                     //loop through the scan area to find where source(x, y) overlaps with the destination pixels
                     for (int xx = mindx - ; xx <= maxdx; xx++)
                     {
                         if (xx <  || xx >= dst.Width)
                             continue;
                         for (int yy = mindy - ; yy <= maxdy; yy++)
                         {
                             if (yy <  || yy >= dst.Height)
                                 continue;
                             //offset p and by (xx,yy) and put that into poffset
                             for (int i = ; i < ; i++)
                             {
                                 poffset[i].x = p[i].x - xx;
                                 poffset[i].y = p[i].y - yy;
                             }
                             //FIND THE OVERLAP *a whole lot of code pays off here*
                             //这里则是计算出覆盖了面积占当前像素的百分比
                             double dbloverlap = PixOverlap(poffset);
                             //按照百分比来为目标像素点累加颜色
                             //因为一个目标像素点有可能有几个原来像素的覆盖了
                             if (dbloverlap > )
                             {
                                 int dstindex = xx + yy * outwidth;
                                 int srcWidth = src.Width;
                                 Color srcColor;
                                 if (SrcIndex == )
                                     srcColor = src.GetPixel(, );
                                 else
                                  srcColor = src.GetPixel(SrcIndex%src.Width  ,  SrcIndex/src.Width );
                                 //Add the rgb and alpha values in proportion to the overlap area
                                 dbldstdib[dstindex].Red += (double)((srcColor.R) * dbloverlap);
                                 dbldstdib[dstindex].Blue += (double)(srcColor.B) * dbloverlap;
                                 dbldstdib[dstindex].Green += (double)(srcColor.G) * dbloverlap;
                                 dbldstdib[dstindex].Alpha += dbloverlap;
                             }
                         }
                     }
                 }
                 if (callbackfunc != null)
                 {
                     //Send the callback message
                     double percentdone = (double)(x + ) / (double)(src.Width);
                     if (callbackfunc(percentdone))
                     {
                         dbldstdib = null;
                         p = null;
                         poffset = null;
                         return ;
                     }
                 }
             }
             //Free memory no longer needed
             //Create final destination bits
             RGBQUDA[] dstdib = new RGBQUDA[dst.Width * dst.Height];
             for (int i = ; i < dstdib.Length; i++)
                 dstdib[i] = new RGBQUDA(){R= ,G= ,B= };
             //这里是实际上真正像素点的颜色,并且填到了目标图片中去
             //Write to dstdib with the information stored in dbldstdib
             for (int x = ; x < outwidth; x++)
             {
                 if (x + outstartx >= dst.Width)
                     continue;
                 for (int y = ; y < outheight; y++)
                 {
                     if (y + outstarty >= dst.Height)
                         continue;
                     int offindex = x + y * outwidth;
                     int dstindex = x + outstartx + (y + outstarty) * dst.Width;
                     int dstIndexX = dstindex / dst.Width;
                     int dstIndexY = dstindex % dst.Width;
                     if (dbldstdib[offindex].Alpha > )
                     {
                         //handles wrap around for non-convex transformations
                         dstdib[dstindex].R = byterange(dbldstdib[offindex].Red / dbldstdib[offindex].Alpha);
                         dstdib[dstindex].G = byterange(dbldstdib[offindex].Green / dbldstdib[offindex].Alpha);
                         dstdib[dstindex].B = byterange(dbldstdib[offindex].Blue / dbldstdib[offindex].Alpha);
                     }
                     else
                     {
                         //Color dstColor = dst.GetPixel(dstIndexX, dstIndexY);
                         dstdib[dstindex].R = byterange(dbldstdib[offindex].Red + ( - dbldstdib[offindex].Alpha) * (double)dstdib[dstindex].R);
                         dstdib[dstindex].G = byterange(dbldstdib[offindex].Green + ( - dbldstdib[offindex].Alpha) * (double)dstdib[dstindex].G);
                         dstdib[dstindex].B = byterange(dbldstdib[offindex].Blue + ( - dbldstdib[offindex].Alpha) * (double)dstdib[dstindex].B);
                     }
                     dst.SetPixel(dstIndexY,dstIndexX , Color.FromArgb(dstdib[dstindex].R, dstdib[dstindex].G, dstdib[dstindex].B));
                 }
             }
             //:D
             return ;
         }
         double PixOverlap(AafPnt[] p)
         {
             polyoverlapsize = ;
             polysortedsize = ;
             double minx, maxx, miny, maxy;
             int j;
             double z;
             for (int i = ; i < ; i++)
             {
                 //Search for source points within the destination quadrolateral
                 if (p[i].x >=  && p[i].x <=  && p[i].y >=  && p[i].y <= )
                     polyoverlap[polyoverlapsize++] = p[i];
                 //Search for destination points within the source quadrolateral
                 if (PtinConvexPolygon(p, corners[i]))
                     polyoverlap[polyoverlapsize++] = corners[i];
                 //Search for line intersections
                 j = ja[i];
                 minx = aaf_min(p[i].x, p[j].x);
                 miny = aaf_min(p[i].y, p[j].y);
                 maxx = aaf_max(p[i].x, p[j].x);
                 maxy = aaf_max(p[i].y, p[j].y);
                 if (minx < 0.0 && 0.0 < maxx)
                 {//Cross left
                     z = p[i].y - p[i].x * (p[i].y - p[j].y) / (p[i].x - p[j].x);
                     if (z >= 0.0 && z <= 1.0)
                     {
                         polyoverlap[polyoverlapsize].x = 0.0;
                         polyoverlap[polyoverlapsize++].y = z;
                     }
                 }
                 if (minx < 1.0 && 1.0 < maxx)
                 {//Cross right
                     z = p[i].y + ( - p[i].x) * (p[i].y - p[j].y) / (p[i].x - p[j].x);
                     if (z >= 0.0 && z <= 1.0)
                     {
                         polyoverlap[polyoverlapsize].x = 1.0;
                         polyoverlap[polyoverlapsize++].y = z;
                     }
                 }
                 if (miny < 0.0 && 0.0 < maxy)
                 {//Cross bottom
                     z = p[i].x - p[i].y * (p[i].x - p[j].x) / (p[i].y - p[j].y);
                     if (z >= 0.0 && z <= 1.0)
                     {
                         polyoverlap[polyoverlapsize].x = z;
                         polyoverlap[polyoverlapsize++].y = 0.0;
                     }
                 }
                 if (miny < 1.0 && 1.0 < maxy)
                 {//Cross top
                     z = p[i].x + ( - p[i].y) * (p[i].x - p[j].x) / (p[i].y - p[j].y);
                     if (z >= 0.0 && z <= 1.0)
                     {
                         polyoverlap[polyoverlapsize].x = z;
                         polyoverlap[polyoverlapsize++].y = 1.0;
                     }
                 }
             }
             //Sort the points and return the area
             SortPoints();
             return Area();
         }
         private double Area()
         {
             double ret = 0.0;
             //Loop through each triangle with respect to (0, 0) and add the cross multiplication
             for (int i = ; i +  < polysortedsize; i++)
                 ret += polysorted[i].x * polysorted[i + ].y - polysorted[i + ].x * polysorted[i].y;
             //Take the absolute value over 2
             return AafAbs(ret) / 2.0;
         }
         private void SortPoints()
         {
             //Why even bother?
             if (polyoverlapsize < )
                 return;
             //polyoverlap is a triangle, points cannot be out of order
             if (polyoverlapsize == )
             {
                 polysortedsize = polyoverlapsize - ;
                 polysorted[].x = polyoverlap[].x - polyoverlap[].x;
                 polysorted[].y = polyoverlap[].y - polyoverlap[].y;
                 polysorted[].x = polyoverlap[].x - polyoverlap[].x;
                 polysorted[].y = polyoverlap[].y - polyoverlap[].y;
                 return;
             }
             aaf_indll root = new aaf_indll();
             root.next = null;
             //begin sorting the points.  Note that the first element is left out and all other elements are offset by it's values
             for (int i = ; i < polyoverlapsize; i++)
             {
                 polyoverlap[i].x = polyoverlap[i].x - polyoverlap[].x;
                 polyoverlap[i].y = polyoverlap[i].y - polyoverlap[].y;
                 aaf_indll node = root;
                 //Loop until the point polyoverlap[i] is can be sorted (counter) clockwiswe (I'm not sure which way it's sorted)
                 while (true)
                 {
                     if (node.next != null)
                     {
                         if (polyoverlap[i].x * polyoverlap[node.next.ind].y - polyoverlap[node.next.ind].x * polyoverlap[i].y < )
                         {
                             //Insert point before this element
                             aaf_indll temp = node.next;
                             node.next = new aaf_indll();
                             node.next.ind = i;
                             node.next.next = temp;
                             break;
                         }
                     }
                     else
                     {
                         //Add point to the end of list
                         node.next = new aaf_indll();
                         node.next.ind = i;
                         node.next.next = null;
                         break;
                     }
                     node = node.next;
                 }
             }
             //We can leave out the first point because it's offset position is going to be (0, 0)
             polysortedsize = ;
             aaf_indll node2 = root;
             aaf_indll temp2;
             //Add the sorted points to polysorted and clean up memory
             while (node2 != null)
             {
                 temp2 = node2;
                 node2 = node2.next;
                 if (node2 != null)
                     polysorted[polysortedsize++] = polyoverlap[node2.ind];
             }
         }
         private bool PtinConvexPolygon(AafPnt[] p, AafPnt pt)
         {
             int dir = ;
             int j;
             //Basically what we are doing is seeing if pt is on the same side of each face of the polygon through cross multiplication
             for (int i = ; i < ; i++)
             {
                 j = ja[i];
                 double cross = (p[i].x - pt.x) * (p[j].y - pt.y) - (p[j].x - pt.x) * (p[i].y - pt.y);
                 if (cross == )
                     continue;
                 if (cross > )
                 {
                     if (dir == -)
                         return false;
                     dir = ;
                 }
                 else
                 {
                     if (dir == )
                         return false;
                     dir = -;
                 }
             }
             return true;
         }
         int roundup(double a) { if (AafAbs(a - round(a)) < 1e-) return round(a); else if ((int)a > a) return (int)a; else return (int)a + ; }
         int rounddown(double a) { if (AafAbs(a - round(a)) < 1e-) return round(a); else if ((int)a < a) return (int)a; else return (int)a - ; }
         int round(double a) { return (int)(a + 0.5); }
         byte byterange(double a) { int b = round(a); if (b <= ) return ; else if (b >= ) return ; else return (byte)b; }
         double AafAbs(double a) { return (((a) < ) ? (-(a)) : (a)); }
         double aaf_min(double a, double b) { if (a < b) return a; else return b; }
         double aaf_max(double a, double b) { if (a > b) return a; else return b; }
     }
     class RGBQUDA
     {
         public byte R { get; set; }
         public byte G { get; set; }
         public byte B { get; set; }
     }
Aaform
C# 图片自由变换 任意扭曲的更多相关文章
- DDGScreenShot—截取图片的任意部分
		写在前面 DDGScreenShot 库提供了截取任意图片的功能, 支持手势截图,当然,输入任意的区域也可以,下面看看具体的代码 代码如下: 方法封装 /** ** 用手势截图(截取图片的任意部分) ... 
- JQuery插件让图片旋转任意角度且代码极其简单 - 摘自网友
		JQuery插件让图片旋转任意角度且代码极其简单 2012-04-01 09:57:03 我来说两句 收藏 我要投稿 引入下方的jquery.rotate.js文件,然后通过$ ... 
- JQuery插件让图片旋转任意角度且代码极其简单
		引入下方的jquery.rotate.js文件,然后通过$("选择器").rotate(角度);可以旋转任意角度, 例如$("#rotate-image").r ... 
- android中对Bitmap图片设置任意角为圆角
		http://blog.csdn.net/l448288137/article/details/48276681 最近项目开发中使用到了圆角图片,网上找到的圆角图片控件大多比较死板,只可以全圆角.其中 ... 
- JAVA对图片的任意角度旋转,以及镜像操作
		package relevantTest;/* * 该代码实现了对图像的水平镜像变换,垂直镜像变换,任意角度旋转,jtf的实时监控,以及对图像的缩放变换,以及按钮的若隐若现效果. * 在对图像进行任意 ... 
- Android中绘制圆角矩形图片及任意形状图片
		圆角矩形图片在苹果的产品中很流行,相比于普通的矩形,很多人都喜欢圆角矩形的图片,因为它避开了直角的生硬,带来更好的用户体验,下面是几个设计的例子: 下面在Android中实现将普通的矩形图片绘制成圆角 ... 
- Android 设置图片 Bitmap任意透明度
		两种思路,第一种思路是通过对Bitmap进行操作,将Bitmap的像素值get到一个int[]数组里,因为在android里Bitmap通常是ARGB8888格式,所以最高位就是A通道的值,对齐进行改 ... 
- 在word中输入任意角度旋转图片
		Sub 图片旋转任意角度() Dim sha As Shape, isa As InlineShape Static s As Integer Application.ScreenUpdating = ... 
- photoshop怎么旋转图片
		Adobe Photoshop 是一款为人熟知的功能强大的图像处理软件.在这里简单介绍一下如何在photoshop里进行图像的旋转. 工具/原料 Adobe Photoshop 软件,图像一张 方 ... 
随机推荐
- 面向对象架构模式之:领域模型(Domain Model)
			一:面向对象设计中最简单的部分与最难的部分 如果说事务脚本是 面向过程 的,那么领域模型就是 面向对象 的.面向对象的一个很重要的点就是:“把事情交给最适合的类去做”,即:“你得在一个个领域类之间跳转 ... 
- 自制Unity小游戏TankHero-2D(4)关卡+小地图图标+碰撞条件分析
			自制Unity小游戏TankHero-2D(4)关卡+小地图图标+碰撞条件分析 我在做这样一个坦克游戏,是仿照(http://game.kid.qq.com/a/20140221/028931.htm ... 
- webservice2
			按照 当然,里面没写清楚如何配置第三方jar 结果一访问就报错:org.codehaus.xfire.transport.http.XFireConfigurableServlet 4年前就搞过ws的 ... 
- 几款开源的图形化Redis客户端管理软件
			转载于:http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/98.html?1455870209 Redis是一个超精简的基于内存的键值 ... 
- js笔记——js数据类型转换
			以下内容摘录自阮一峰的<语法概述 -- JavaScript 标准参考教程(alpha)>章节『数据类型转换』,以做备忘.更多内容请查看原文. JavaScript是一种动态类型语言,变量 ... 
- redis常用操作总结
			在项目中时常会用到redis,redis看起来好像很难的样子,而且我也确认反复学习了很久,但是,总结下来,自己使用到的东西并不太多,如下作一些总结工作. 1.安装(单机) 1.1 windows, 直 ... 
- Leetcode 4 Median of Two Sorted Arrays 二分查找(二分答案+二分下标)
			貌似是去年阿里巴巴c++的笔试题,没有什么创新直接照搬的... 题意就是找出两个排序数组的中间数,其实就是找出两个排序数组的第k个数. 二分答案,先二分出一个数,再用二分算出这个数在两个排序数组排序第 ... 
- Struts2--ONGL--值栈
			ONGL:用来访问栈里对象属性的语言,通常由Struts标签来解析, 注意:要在接续OGNL的页面加入<%@ taglib prefix="s" uri="/str ... 
- 快速入门系列--MVC--06视图
			到了View的呈现板块,感觉ASP.NET MVC的学习也进入了尾声,还是比较开心的,毕竟也有了不小收获.这部分内容相对比较简单,因为之前还专门学习过如何结合HTML5与MVC框架.前文中提到过,Ac ... 
- Enterprise Solution 开发框架功能点
			1. 通用查询模块,可以通过关联数据库表,存储过程或程序代码开发查询,多个查询之间也可构成主从关联查询. 2. 业务异常处理 支持统一的异常处理. 3. 内置一个简单的SQL Server查询分析器, ... 
