C# 图片自由变换 任意扭曲

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

  先看看效果

 

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

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

接着就介绍算法里面调用的方法层次

  把已经弄懂(并不代表完全懂的)的代码贴出来,首先是最外层的方法

复制代码
 1         public void CreateTransform(Bitmap src,ref Bitmap dst, List<double> xcorner, List<double> ycorner, Aaf_callback callbackfunc) 2         { 3             int right = 0, bottom = 0; 4  5             //主要是根据新图片的坐标,计算出图片的宽和高,构造目标图片的Bitmap的 6             double offx = xcorner[0]; 7             double offy = ycorner[0]; 8             for (int i = 1; i < 4; i++) 9             {10                 if (xcorner[i] < offx) offx = xcorner[i];11                 if (ycorner[i] < offy) offy = ycorner[i];12             }13 14             for (int i = 0; i < 4; i++)15             {16                 xcorner[i] -= offx;17                 ycorner[i] -= offy;18                 if (roundup(xcorner[i]) > right) right = roundup(xcorner[i]);19                 if (roundup(ycorner[i]) > bottom) bottom = roundup(ycorner[i]);20             }21             dst = new Bitmap(right, bottom);22             Transform(src, dst, xcorner, ycorner, null);23         }
复制代码

  上面这个方法只是定了目标图片的尺寸,其余什么都没做。下面这个方法还没做多少转换的事

复制代码
 1         private void Transform(Bitmap src,Bitmap dst, List<double> xcorner, List<double> ycorner, Aaf_callback callbackfunc) 2         { 3             //Make sure the coordinates are valid 4             if (xcorner.Count != 4 || ycorner.Count != 4) 5                 return ; 6  7             //Load the src bitmaps information 8  9             //create the intial arrays10             //根据原图片生成一个比原图宽高多一个单位的图片,11             //这个矩阵就是用来记录转换后各个像素点左上角的坐标12             pixelgrid = new AafPnt[(src.Width + 1) * (src.Height + 1)];13             polyoverlap = new AafPnt[16];14             polysorted = new AafPnt[16];15             corners = new AafPnt[4];16 17             //Load the corners array18             double[] dx = { 0.0, 1.0, 1.0, 0.0 };19             double[] dy = { 0.0, 0.0, 1.0, 1.0 };20             for (int i = 0; i < 4; i++)21             {22                 corners[i].x = dx[i];23                 corners[i].y = dy[i];24             }25 26             //Find the rectangle of dst to draw to27             outstartx = rounddown(xcorner[0]);28             outstarty = rounddown(ycorner[0]);29             outwidth = 0;30             outheight = 0;31             //这里计算出变换后起始点的坐标32             for (int i = 1; i < 4; i++)33             {34                 if (rounddown(xcorner[i]) < outstartx) outstartx = rounddown(xcorner[i]);35                 if (rounddown(ycorner[i]) < outstarty) outstarty = rounddown(ycorner[i]);36             }37             for (int i = 0; i < 4; i++)38             {39                 if (roundup(xcorner[i] - outstartx) > outwidth) outwidth = roundup(xcorner[i] - outstartx);40                 if (roundup(ycorner[i] - outstarty) > outheight) outheight = roundup(ycorner[i] - outstarty);41             }42 43 44             //fill out pixelgrid array45             //计算出理想目标图片中各个“像素格”中的左上角的坐标46             if (CreateGrid(src, xcorner, ycorner))47             {48                 //Do the transformation49                 //进行转换50                DoTransform(src,dst, callbackfunc);51             }52 53             //Return if the function completed properly54             return ;55         }
复制代码

  下面这个方法则是计算出原图像中每个像素点的左上角的点到目标图像中的坐标,结果是存放在pixelgrid中,这个二维数组的行和列都比原图像的宽高多1,这个关系我当初没搞懂。用比较极限的思想,假设现在这幅图片只有一个像素组成,宽和高都是1,然后如果单纯存储一个左上角的坐标,是无法组成一个四边形的,这就需要把其他三个角的坐标相应地记录,这是存储的数组的行和列均要比原图的宽和高多1,就是也就是一个2行2列的数组能存放4个数值,刚好就容纳了那一个像素点的四个角的坐标值。扩大到真实的图片也同样道理。不过这个方法我看不明白,貌似用到了向量的思想。大致是按照新图片四条边来计算的。

复制代码
 1         private bool CreateGrid(Bitmap src, List<double> xcorner, List<double> ycorner) 2         { 3             //mmm geometry 4             double[] sideradius = new double[4]; 5             double[] sidecos = new double[4]; 6             double[] sidesin = new double[4]; 7  8             //First we find the radius, cos, and sin of each side of the polygon created by xcorner and ycorner 9             int j;10             for (int i = 0; i < 4; i++)11             {12                 j = ja[i];13                 sideradius[i] = Math.Sqrt((xcorner[i] - xcorner[j]) * (xcorner[i] - xcorner[j]) + (ycorner[i] - ycorner[j]) * (ycorner[i] - ycorner[j]));14                 sidecos[i] = (xcorner[j] - xcorner[i]) / sideradius[i];15                 sidesin[i] = (ycorner[j] - ycorner[i]) / sideradius[i];16             }17 18             //Next we create two lines in Ax + By = C form19             for (int x = 0; x < src.Width + 1; x++)20             {21                 double topdist = ((double)x / (src.Width)) * sideradius[0];22                 double ptxtop = xcorner[0] + topdist * sidecos[0];23                 double ptytop = ycorner[0] + topdist * sidesin[0];24 25                 double botdist = (1.0 - (double)x / (src.Width)) * sideradius[2];26                 double ptxbot = xcorner[2] + botdist * sidecos[2];27                 double ptybot = ycorner[2] + botdist * sidesin[2];28 29                 double Ah = ptybot - ptytop;30                 double Bh = ptxtop - ptxbot;31                 double Ch = Ah * ptxtop + Bh * ptytop;//叉乘32 33                 for (int y = 0; y < src.Height + 1; y++)34                 {35                     double leftdist = (1.0 - (double)y / (src.Height)) * sideradius[3];36                     double ptxleft = xcorner[3] + leftdist * sidecos[3];37                     double ptyleft = ycorner[3] + leftdist * sidesin[3];38 39                     double rightdist = ((double)y / (src.Height)) * sideradius[1];40                     double ptxright = xcorner[1] + rightdist * sidecos[1];41                     double ptyright = ycorner[1] + rightdist * sidesin[1];42 43                     double Av = ptyright - ptyleft;44                     double Bv = ptxleft - ptxright;45                     double Cv = Av * ptxleft + Bv * ptyleft;46 47                     //Find where the lines intersect and store that point in the pixelgrid array48                     double det = Ah * Bv - Av * Bh;49                     if (AafAbs(det) < 1e-9)50                     {51                         return false;52                     }53                     else54                     {55                         int ind = x + y * (src.Width + 1);56                         pixelgrid[ind].x = (Bv * Ch - Bh * Cv) / det;57                         pixelgrid[ind].y = (Ah * Cv - Av * Ch) / det;58                     }59                 }60             }61 62             //Yayy we didn't fail63             return true;64         }
复制代码

  下面这个方法就利用上面的方法计算出的坐标点集合进行按比例填色。上面每个像素点的四个角的坐标,都会在下面方法重新提取出来组回一个四边形,具体还是结合代码和注释看看

复制代码
  1         private void DoTransform(Bitmap src,Bitmap dst, Aaf_callback callbackfunc)  2         {  3               4             //Get source bitmap's information  5             if (src == null) return ;  6   7             //Create the source dib array and the dstdib array  8             aaf_dblrgbquad[] dbldstdib = new aaf_dblrgbquad[outwidth * outheight];  9             for (int i = 0; i < dbldstdib.Length; i++) 10                 dbldstdib[i] = new aaf_dblrgbquad(); 11  12             //Create polygon arrays 13             AafPnt[] p = new AafPnt[4]; 14             AafPnt[] poffset = new AafPnt[4]; 15  16             //Loop through the source's pixels 17             //遍历原图(实质上是pixelgrid)各个点 18             for (int x = 0; x < src.Width; x++) 19             { 20                 for (int y = 0; y < src.Height; y++) 21                 { 22                     //取当前点 下一点 右一点 斜右下角点 四点才组成一个四边形 23                     //这个四边形是原图像的一个像素点 24                     //Construct the source pixel's rotated polygon from pixelgrid 25                     p[0] = pixelgrid[x + y * (src.Width + 1)]; 26                     p[1] = pixelgrid[(x + 1) + y * (src.Width + 1)]; 27                     p[2] = pixelgrid[(x + 1) + (y + 1) * (src.Width + 1)]; 28                     p[3] = pixelgrid[x + (y + 1) * (src.Width + 1)]; 29  30                     //Find the scan area on the destination's pixels 31                     int mindx = int.MaxValue; 32                     int mindy = int.MaxValue; 33                     int maxdx = int.MinValue; 34                     int maxdy = int.MinValue; 35                     for (int i = 0; i < 4; i++) 36                     { 37                         if (rounddown(p[i].x) < mindx) mindx = rounddown(p[i].x); 38                         if (roundup(p[i].x) > maxdx) maxdx = roundup(p[i].x); 39                         if (rounddown(p[i].y) < mindy) mindy = rounddown(p[i].y); 40                         if (roundup(p[i].y) > maxdy) maxdy = roundup(p[i].y); 41                     } 42  43                     int SrcIndex = x + y * src.Width; 44                     //遍历四边形包含了目标图几个像素点 45                     //按照相交面积占整个像素的的百分比,把颜色按照该比例存放一个目标像素点颜色的数组中 46                     //这里计算出来的颜色只是初步颜色,还没到最终结果 47                     //loop through the scan area to find where source(x, y) overlaps with the destination pixels 48                     for (int xx = mindx - 1; xx <= maxdx; xx++) 49                     { 50                         if (xx < 0 || xx >= dst.Width) 51                             continue; 52                         for (int yy = mindy - 1; yy <= maxdy; yy++) 53                         { 54                             if (yy < 0 || yy >= dst.Height) 55                                 continue; 56  57                             //offset p and by (xx,yy) and put that into poffset 58                             for (int i = 0; i < 4; i++) 59                             { 60                                 poffset[i].x = p[i].x - xx; 61                                 poffset[i].y = p[i].y - yy; 62                             } 63  64                             //FIND THE OVERLAP *a whole lot of code pays off here* 65                             //这里则是计算出覆盖了面积占当前像素的百分比 66                             double dbloverlap = PixOverlap(poffset); 67                             //按照百分比来为目标像素点累加颜色 68                             //因为一个目标像素点有可能有几个原来像素的覆盖了 69                             if (dbloverlap > 0) 70                             { 71                                 int dstindex = xx + yy * outwidth; 72                                 int srcWidth = src.Width; 73                                 Color srcColor; 74                                 if (SrcIndex == 0) 75                                     srcColor = src.GetPixel(0, 0); 76                                 else 77                                  srcColor = src.GetPixel(SrcIndex%src.Width  ,  SrcIndex/src.Width ); 78                                 //Add the rgb and alpha values in proportion to the overlap area 79                                 dbldstdib[dstindex].Red += (double)((srcColor.R) * dbloverlap); 80                                 dbldstdib[dstindex].Blue += (double)(srcColor.B) * dbloverlap; 81                                 dbldstdib[dstindex].Green += (double)(srcColor.G) * dbloverlap; 82                                 dbldstdib[dstindex].Alpha += dbloverlap; 83                             } 84                         } 85                     } 86                 } 87                 if (callbackfunc != null) 88                 { 89                     //Send the callback message 90                     double percentdone = (double)(x + 1) / (double)(src.Width); 91                     if (callbackfunc(percentdone)) 92                     { 93                         dbldstdib = null; 94                         p = null; 95                         poffset = null; 96                         return ; 97                     } 98                 } 99             }100 101             //Free memory no longer needed102 103 104             //Create final destination bits105             RGBQUDA[] dstdib = new RGBQUDA[dst.Width * dst.Height];106             for (int i = 0; i < dstdib.Length; i++)107                 dstdib[i] = new RGBQUDA(){R= 0,G= 0,B= 0};108 109             //这里是实际上真正像素点的颜色,并且填到了目标图片中去110             //Write to dstdib with the information stored in dbldstdib111             for (int x = 0; x < outwidth; x++)112             {113                 if (x + outstartx >= dst.Width)114                     continue;115                 for (int y = 0; y < outheight; y++)116                 {117                     if (y + outstarty >= dst.Height)118                         continue;119                     int offindex = x + y * outwidth;120                     int dstindex = x + outstartx + (y + outstarty) * dst.Width;121 122                     int dstIndexX = dstindex / dst.Width;123                     int dstIndexY = dstindex % dst.Width;124                     if (dbldstdib[offindex].Alpha > 1)125                     {126                         //handles wrap around for non-convex transformations127                         dstdib[dstindex].R = byterange(dbldstdib[offindex].Red / dbldstdib[offindex].Alpha);128                         dstdib[dstindex].G = byterange(dbldstdib[offindex].Green / dbldstdib[offindex].Alpha);129                         dstdib[dstindex].B = byterange(dbldstdib[offindex].Blue / dbldstdib[offindex].Alpha);130                     }131                     else132                     {133                         //Color dstColor = dst.GetPixel(dstIndexX, dstIndexY);134                         dstdib[dstindex].R = byterange(dbldstdib[offindex].Red + (1 - dbldstdib[offindex].Alpha) * (double)dstdib[dstindex].R);135                         dstdib[dstindex].G = byterange(dbldstdib[offindex].Green + (1 - dbldstdib[offindex].Alpha) * (double)dstdib[dstindex].G);136                         dstdib[dstindex].B = byterange(dbldstdib[offindex].Blue + (1 - dbldstdib[offindex].Alpha) * (double)dstdib[dstindex].B);137                     }138                     dst.SetPixel(dstIndexY,dstIndexX , Color.FromArgb(dstdib[dstindex].R, dstdib[dstindex].G, dstdib[dstindex].B));139                 }140             }141 142             //:D143             return ;144         }
复制代码

  里面调用到的计算相交面积的方法PixOverlap就不列出来了,因为还没看懂,看明白了也会在本文中补充。若想看的,本文最后会贴出所有源码。

  希望有看的明白的朋友能多指点一下,谢谢!还要感谢一个人,sa姐,在我阅读这个算法时给了不少灵感为我。搞这个算法,让我想起了大三上的一门课《医学图像处理》,我的老师涂泳秋老师。

复制代码
  1     public delegate bool Aaf_callback(double paraDouble);  2   3     struct AafPnt  4     {  5         public double x, y;  6         public AafPnt(double x, double y)  7         {  8             this.x = x < 0 ? 0 : x;  9             this.y = y < 0 ? 0 : y; 10         } 11     } 12  13     class aaf_dblrgbquad 14     { 15         public double Red{get;set;} 16         public double Green{get;set;} 17         public double  Blue{get;set;} 18         public double Alpha { get; set; } 19     } 20  21     class aaf_indll 22     { 23         public aaf_indll next; 24         public int ind; 25     } 26  27     class Aaform 28     { 29         private AafPnt[] pixelgrid; 30         private AafPnt[] polyoverlap; 31         private AafPnt[] polysorted; 32         private AafPnt[] corners; 33  34         private int outstartx; 35         private int outstarty; 36         private int outwidth; 37         private int outheight; 38  39         int polyoverlapsize; 40         int polysortedsize; 41  42         int[] ja = new int[] { 1, 2, 3, 0 }; 43  44         public void CreateTransform(Bitmap src,ref Bitmap dst, List<double> xcorner, List<double> ycorner, Aaf_callback callbackfunc) 45         { 46             int right = 0, bottom = 0; 47  48             //主要是根据新图片的坐标,计算出图片的宽和高,构造目标图片的Bitmap的 49             double offx = xcorner[0]; 50             double offy = ycorner[0]; 51             for (int i = 1; i < 4; i++) 52             { 53                 if (xcorner[i] < offx) offx = xcorner[i]; 54                 if (ycorner[i] < offy) offy = ycorner[i]; 55             } 56  57             for (int i = 0; i < 4; i++) 58             { 59                 xcorner[i] -= offx; 60                 ycorner[i] -= offy; 61                 if (roundup(xcorner[i]) > right) right = roundup(xcorner[i]); 62                 if (roundup(ycorner[i]) > bottom) bottom = roundup(ycorner[i]); 63             } 64             dst = new Bitmap(right, bottom); 65             Transform(src, dst, xcorner, ycorner, null); 66         } 67  68         private void Transform(Bitmap src,Bitmap dst, List<double> xcorner, List<double> ycorner, Aaf_callback callbackfunc) 69         { 70             //Make sure the coordinates are valid 71             if (xcorner.Count != 4 || ycorner.Count != 4) 72                 return ; 73  74             //Load the src bitmaps information 75  76             //create the intial arrays 77             //根据原图片生成一个比原图宽高多一个单位的图片, 78             //这个矩阵就是用来记录转换后各个像素点左上角的坐标 79             pixelgrid = new AafPnt[(src.Width + 1) * (src.Height + 1)]; 80             polyoverlap = new AafPnt[16]; 81             polysorted = new AafPnt[16]; 82             corners = new AafPnt[4]; 83  84             //Load the corners array 85             double[] dx = { 0.0, 1.0, 1.0, 0.0 }; 86             double[] dy = { 0.0, 0.0, 1.0, 1.0 }; 87             for (int i = 0; i < 4; i++) 88             { 89                 corners[i].x = dx[i]; 90                 corners[i].y = dy[i]; 91             } 92  93             //Find the rectangle of dst to draw to 94             outstartx = rounddown(xcorner[0]); 95             outstarty = rounddown(ycorner[0]); 96             outwidth = 0; 97             outheight = 0; 98             //这里计算出变换后起始点的坐标 99             for (int i = 1; i < 4; i++)100             {101                 if (rounddown(xcorner[i]) < outstartx) outstartx = rounddown(xcorner[i]);102                 if (rounddown(ycorner[i]) < outstarty) outstarty = rounddown(ycorner[i]);103             }104             for (int i = 0; i < 4; i++)105             {106                 if (roundup(xcorner[i] - outstartx) > outwidth) outwidth = roundup(xcorner[i] - outstartx);107                 if (roundup(ycorner[i] - outstarty) > outheight) outheight = roundup(ycorner[i] - outstarty);108             }109 110 111             //fill out pixelgrid array112             //计算出理想目标图片中各个“像素格”中的左上角的坐标113             if (CreateGrid(src, xcorner, ycorner))114             {115                 //Do the transformation116                 //进行转换117                DoTransform(src,dst, callbackfunc);118             }119 120             //Return if the function completed properly121             return ;122         }123 124         private bool CreateGrid(Bitmap src, List<double> xcorner, List<double> ycorner)125         {126             //mmm geometry127             double[] sideradius = new double[4];128             double[] sidecos = new double[4];129             double[] sidesin = new double[4];130 131             //First we find the radius, cos, and sin of each side of the polygon created by xcorner and ycorner132             int j;133             for (int i = 0; i < 4; i++)134             {135                 j = ja[i];136                 sideradius[i] = Math.Sqrt((xcorner[i] - xcorner[j]) * (xcorner[i] - xcorner[j]) + (ycorner[i] - ycorner[j]) * (ycorner[i] - ycorner[j]));137                 sidecos[i] = (xcorner[j] - xcorner[i]) / sideradius[i];138                 sidesin[i] = (ycorner[j] - ycorner[i]) / sideradius[i];139             }140 141             //Next we create two lines in Ax + By = C form142             for (int x = 0; x < src.Width + 1; x++)143             {144                 double topdist = ((double)x / (src.Width)) * sideradius[0];//每个像素点变换后的坐标点145                 double ptxtop = xcorner[0] + topdist * sidecos[0];146                 double ptytop = ycorner[0] + topdist * sidesin[0];147 148                 double botdist = (1.0 - (double)x / (src.Width)) * sideradius[2];149                 double ptxbot = xcorner[2] + botdist * sidecos[2];150                 double ptybot = ycorner[2] + botdist * sidesin[2];151 152                 double Ah = ptybot - ptytop;153                 double Bh = ptxtop - ptxbot;154                 double Ch = Ah * ptxtop + Bh * ptytop;//叉乘155 156                 for (int y = 0; y < src.Height + 1; y++)157                 {158                     double leftdist = (1.0 - (double)y / (src.Height)) * sideradius[3];159                     double ptxleft = xcorner[3] + leftdist * sidecos[3];160                     double ptyleft = ycorner[3] + leftdist * sidesin[3];161 162                     double rightdist = ((double)y / (src.Height)) * sideradius[1];163                     double ptxright = xcorner[1] + rightdist * sidecos[1];164                     double ptyright = ycorner[1] + rightdist * sidesin[1];165 166                     double Av = ptyright - ptyleft;167                     double Bv = ptxleft - ptxright;168                     double Cv = Av * ptxleft + Bv * ptyleft;169 170                     //Find where the lines intersect and store that point in the pixelgrid array171                     double det = Ah * Bv - Av * Bh;172                     if (AafAbs(det) < 1e-9)173                     {174                         return false;175                     }176                     else177                     {178                         int ind = x + y * (src.Width + 1);179                         pixelgrid[ind].x = (Bv * Ch - Bh * Cv) / det;180                         pixelgrid[ind].y = (Ah * Cv - Av * Ch) / det;181                     }182                 }183             }184 185             //Yayy we didn't fail186             return true;187         }188 189         private void DoTransform(Bitmap src,Bitmap dst, Aaf_callback callbackfunc)190         {191             192             //Get source bitmap's information193             if (src == null) return ;194 195             //Create the source dib array and the dstdib array196             aaf_dblrgbquad[] dbldstdib = new aaf_dblrgbquad[outwidth * outheight];197             for (int i = 0; i < dbldstdib.Length; i++)198                 dbldstdib[i] = new aaf_dblrgbquad();199 200             //Create polygon arrays201             AafPnt[] p = new AafPnt[4];202             AafPnt[] poffset = new AafPnt[4];203 204             //Loop through the source's pixels205             //遍历原图(实质上是pixelgrid)各个点206             for (int x = 0; x < src.Width; x++)207             {208                 for (int y = 0; y < src.Height; y++)209                 {210                     //取当前点 下一点 右一点 斜右下角点 四点才组成一个四边形211                     //这个四边形是原图像的一个像素点212                     //Construct the source pixel's rotated polygon from pixelgrid213                     p[0] = pixelgrid[x + y * (src.Width + 1)];214                     p[1] = pixelgrid[(x + 1) + y * (src.Width + 1)];215                     p[2] = pixelgrid[(x + 1) + (y + 1) * (src.Width + 1)];216                     p[3] = pixelgrid[x + (y + 1) * (src.Width + 1)];217 218                     //Find the scan area on the destination's pixels219                     int mindx = int.MaxValue;220                     int mindy = int.MaxValue;221                     int maxdx = int.MinValue;222                     int maxdy = int.MinValue;223                     for (int i = 0; i < 4; i++)224                     {225                         if (rounddown(p[i].x) < mindx) mindx = rounddown(p[i].x);226                         if (roundup(p[i].x) > maxdx) maxdx = roundup(p[i].x);227                         if (rounddown(p[i].y) < mindy) mindy = rounddown(p[i].y);228                         if (roundup(p[i].y) > maxdy) maxdy = roundup(p[i].y);229                     }230 231                     int SrcIndex = x + y * src.Width;232                     //遍历四边形包含了目标图几个像素点233                     //按照相交面积占整个像素的的百分比,把颜色按照该比例存放一个目标像素点颜色的数组中234                     //这里计算出来的颜色只是初步颜色,还没到最终结果235                     //loop through the scan area to find where source(x, y) overlaps with the destination pixels236                     for (int xx = mindx - 1; xx <= maxdx; xx++)237                     {238                         if (xx < 0 || xx >= dst.Width)239                             continue;240                         for (int yy = mindy - 1; yy <= maxdy; yy++)241                         {242                             if (yy < 0 || yy >= dst.Height)243                                 continue;244 245                             //offset p and by (xx,yy) and put that into poffset246                             for (int i = 0; i < 4; i++)247                             {248                                 poffset[i].x = p[i].x - xx;249                                 poffset[i].y = p[i].y - yy;250                             }251 252                             //FIND THE OVERLAP *a whole lot of code pays off here*253                             //这里则是计算出覆盖了面积占当前像素的百分比254                             double dbloverlap = PixOverlap(poffset);255                             //按照百分比来为目标像素点累加颜色256                             //因为一个目标像素点有可能有几个原来像素的覆盖了257                             if (dbloverlap > 0)258                             {259                                 int dstindex = xx + yy * outwidth;260                                 int srcWidth = src.Width;261                                 Color srcColor;262                                 if (SrcIndex == 0)263                                     srcColor = src.GetPixel(0, 0);264                                 else265                                  srcColor = src.GetPixel(SrcIndex%src.Width  ,  SrcIndex/src.Width );266                                 //Add the rgb and alpha values in proportion to the overlap area267                                 dbldstdib[dstindex].Red += (double)((srcColor.R) * dbloverlap);268                                 dbldstdib[dstindex].Blue += (double)(srcColor.B) * dbloverlap;269                                 dbldstdib[dstindex].Green += (double)(srcColor.G) * dbloverlap;270                                 dbldstdib[dstindex].Alpha += dbloverlap;271                             }272                         }273                     }274                 }275                 if (callbackfunc != null)276                 {277                     //Send the callback message278                     double percentdone = (double)(x + 1) / (double)(src.Width);279                     if (callbackfunc(percentdone))280                     {281                         dbldstdib = null;282                         p = null;283                         poffset = null;284                         return ;285                     }286                 }287             }288 289             //Free memory no longer needed290 291 292             //Create final destination bits293             RGBQUDA[] dstdib = new RGBQUDA[dst.Width * dst.Height];294             for (int i = 0; i < dstdib.Length; i++)295                 dstdib[i] = new RGBQUDA(){R= 0,G= 0,B= 0};296 297             //这里是实际上真正像素点的颜色,并且填到了目标图片中去298             //Write to dstdib with the information stored in dbldstdib299             for (int x = 0; x < outwidth; x++)300             {301                 if (x + outstartx >= dst.Width)302                     continue;303                 for (int y = 0; y < outheight; y++)304                 {305                     if (y + outstarty >= dst.Height)306                         continue;307                     int offindex = x + y * outwidth;308                     int dstindex = x + outstartx + (y + outstarty) * dst.Width;309 310                     int dstIndexX = dstindex / dst.Width;311                     int dstIndexY = dstindex % dst.Width;312                     if (dbldstdib[offindex].Alpha > 1)313                     {314                         //handles wrap around for non-convex transformations315                         dstdib[dstindex].R = byterange(dbldstdib[offindex].Red / dbldstdib[offindex].Alpha);316                         dstdib[dstindex].G = byterange(dbldstdib[offindex].Green / dbldstdib[offindex].Alpha);317                         dstdib[dstindex].B = byterange(dbldstdib[offindex].Blue / dbldstdib[offindex].Alpha);318                     }319                     else320                     {321                         //Color dstColor = dst.GetPixel(dstIndexX, dstIndexY);322                         dstdib[dstindex].R = byterange(dbldstdib[offindex].Red + (1 - dbldstdib[offindex].Alpha) * (double)dstdib[dstindex].R);323                         dstdib[dstindex].G = byterange(dbldstdib[offindex].Green + (1 - dbldstdib[offindex].Alpha) * (double)dstdib[dstindex].G);324                         dstdib[dstindex].B = byterange(dbldstdib[offindex].Blue + (1 - dbldstdib[offindex].Alpha) * (double)dstdib[dstindex].B);325                     }326                     dst.SetPixel(dstIndexY,dstIndexX , Color.FromArgb(dstdib[dstindex].R, dstdib[dstindex].G, dstdib[dstindex].B));327                 }328             }329 330             //:D331             return ;332         }333 334         double PixOverlap(AafPnt[] p)335         {336             polyoverlapsize = 0;337             polysortedsize = 0;338 339             double minx, maxx, miny, maxy;340             int j;341 342             double z;343 344             for (int i = 0; i < 4; i++)345             {346                 //Search for source points within the destination quadrolateral347                 if (p[i].x >= 0 && p[i].x <= 1 && p[i].y >= 0 && p[i].y <= 1)348                     polyoverlap[polyoverlapsize++] = p[i];349 350                 //Search for destination points within the source quadrolateral351                 if (PtinConvexPolygon(p, corners[i]))352                     polyoverlap[polyoverlapsize++] = corners[i];353 354                 //Search for line intersections355                 j = ja[i];356                 minx = aaf_min(p[i].x, p[j].x);357                 miny = aaf_min(p[i].y, p[j].y);358                 maxx = aaf_max(p[i].x, p[j].x);359                 maxy = aaf_max(p[i].y, p[j].y);360 361                 if (minx < 0.0 && 0.0 < maxx)362                 {//Cross left363                     z = p[i].y - p[i].x * (p[i].y - p[j].y) / (p[i].x - p[j].x);364                     if (z >= 0.0 && z <= 1.0)365                     {366                         polyoverlap[polyoverlapsize].x = 0.0;367                         polyoverlap[polyoverlapsize++].y = z;368                     }369                 }370                 if (minx < 1.0 && 1.0 < maxx)371                 {//Cross right372                     z = p[i].y + (1 - p[i].x) * (p[i].y - p[j].y) / (p[i].x - p[j].x);373                     if (z >= 0.0 && z <= 1.0)374                     {375                         polyoverlap[polyoverlapsize].x = 1.0;376                         polyoverlap[polyoverlapsize++].y = z;377                     }378                 }379                 if (miny < 0.0 && 0.0 < maxy)380                 {//Cross bottom381                     z = p[i].x - p[i].y * (p[i].x - p[j].x) / (p[i].y - p[j].y);382                     if (z >= 0.0 && z <= 1.0)383                     {384                         polyoverlap[polyoverlapsize].x = z;385                         polyoverlap[polyoverlapsize++].y = 0.0;386                     }387                 }388                 if (miny < 1.0 && 1.0 < maxy)389                 {//Cross top390                     z = p[i].x + (1 - p[i].y) * (p[i].x - p[j].x) / (p[i].y - p[j].y);391                     if (z >= 0.0 && z <= 1.0)392                     {393                         polyoverlap[polyoverlapsize].x = z;394                         polyoverlap[polyoverlapsize++].y = 1.0;395                     }396                 }397             }398 399             //Sort the points and return the area400             SortPoints();401             return Area();402         }403 404         private double Area()405         {406             double ret = 0.0;407             //Loop through each triangle with respect to (0, 0) and add the cross multiplication408             for (int i = 0; i + 1 < polysortedsize; i++)409                 ret += polysorted[i].x * polysorted[i + 1].y - polysorted[i + 1].x * polysorted[i].y;410             //Take the absolute value over 2411             return AafAbs(ret) / 2.0;412         }413 414         private void SortPoints()415         {416             //Why even bother?417             if (polyoverlapsize < 3)418                 return;419 420             //polyoverlap is a triangle, points cannot be out of order421             if (polyoverlapsize == 3)422             {423                 polysortedsize = polyoverlapsize - 1;424                 polysorted[0].x = polyoverlap[1].x - polyoverlap[0].x;425                 polysorted[0].y = polyoverlap[1].y - polyoverlap[0].y;426                 polysorted[1].x = polyoverlap[2].x - polyoverlap[0].x;427                 polysorted[1].y = polyoverlap[2].y - polyoverlap[0].y;428                 return;429             }430 431 432             aaf_indll root = new aaf_indll();433             root.next = null;434 435             //begin sorting the points.  Note that the first element is left out and all other elements are offset by it's values436             for (int i = 1; i < polyoverlapsize; i++)437             {438                 polyoverlap[i].x = polyoverlap[i].x - polyoverlap[0].x;439                 polyoverlap[i].y = polyoverlap[i].y - polyoverlap[0].y;440 441                 aaf_indll node = root;442                 //Loop until the point polyoverlap[i] is can be sorted (counter) clockwiswe (I'm not sure which way it's sorted)443                 while (true)444                 {445                     if (node.next != null)446                     {447                         if (polyoverlap[i].x * polyoverlap[node.next.ind].y - polyoverlap[node.next.ind].x * polyoverlap[i].y < 0)448                         {449                             //Insert point before this element450                             aaf_indll temp = node.next;451                             node.next = new aaf_indll();452                             node.next.ind = i;453                             node.next.next = temp;454                             break;455                         }456                     }457                     else458                     {459                         //Add point to the end of list460                         node.next = new aaf_indll();461                         node.next.ind = i;462                         node.next.next = null;463                         break;464                     }465                     node = node.next;466                 }467             }468 469             //We can leave out the first point because it's offset position is going to be (0, 0)470             polysortedsize = 0;471 472             aaf_indll node2 = root;473             aaf_indll temp2;474 475             //Add the sorted points to polysorted and clean up memory476             while (node2 != null)477             {478                 temp2 = node2;479                 node2 = node2.next;480                 if (node2 != null)481                     polysorted[polysortedsize++] = polyoverlap[node2.ind];482 483             }484         }485 486         private bool PtinConvexPolygon(AafPnt[] p, AafPnt pt)487         {488             int dir = 0;489             int j;490 491             //Basically what we are doing is seeing if pt is on the same side of each face of the polygon through cross multiplication492             for (int i = 0; i < 4; i++)493             {494                 j = ja[i];495                 double cross = (p[i].x - pt.x) * (p[j].y - pt.y) - (p[j].x - pt.x) * (p[i].y - pt.y);496 497                 if (cross == 0)498                     continue;499 500                 if (cross > 0)501                 {502                     if (dir == -1)503                         return false;504 505                     dir = 1;506                 }507                 else508                 {509                     if (dir == 1)510                         return false;511 512                     dir = -1;513                 }514             }515             return true;516         }517 518         int roundup(double a) { if (AafAbs(a - round(a)) < 1e-9) return round(a); else if ((int)a > a) return (int)a; else return (int)a + 1; }519         int rounddown(double a) { if (AafAbs(a - round(a)) < 1e-9) return round(a); else if ((int)a < a) return (int)a; else return (int)a - 1; }520         int round(double a) { return (int)(a + 0.5); }521         byte byterange(double a) { int b = round(a); if (b <= 0) return 0; else if (b >= 255) return 255; else return (byte)b; }522         double AafAbs(double a) { return (((a) < 0) ? (-(a)) : (a)); }523         double aaf_min(double a, double b) { if (a < b) return a; else return b; }524         double aaf_max(double a, double b) { if (a > b) return a; else return b; }525     }526 527     class RGBQUDA528     {529         public byte R { get; set; }530         public byte G { get; set; }531         public byte B { get; set; }532     }
复制代码
0 0
原创粉丝点击