• C#实现图片自由变换 任意扭曲的算法


    之前想过要做个地铁驾驶的游戏,其中想把一些原本是矩形图片弄成一个梯形,但是发现GID+上面没有类似的方法。于是在谷歌谷了一下。没有!只能找到令人垂涎的,并没有源码。按照自己的想法尝试了一两天,有点效果,但实际上不是那样。后来知道那个在数字图像处理中叫“透视变换”。于是上网找了相关资料,原理找了,看了不明白。代码没多少,有ActionScript的,不明;有C的,不明。真笨啊!后来在CodeProject上面看到一份外国人的博文【http://www.codeproject.com/Articles/13201/Anti-Aliased-Image-Transformation-Aaform】,全英文看不太明白,但看了一幅图,大概知道他意思了。下了份源码看看,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 arrays
    10             //根据原图片生成一个比原图宽高多一个单位的图片,
    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 array
    18             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 to
    27             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 array
    45             //计算出理想目标图片中各个“像素格”中的左上角的坐标
    46             if (CreateGrid(src, xcorner, ycorner))
    47             {
    48                 //Do the transformation
    49                 //进行转换
    50                DoTransform(src,dst, callbackfunc);
    51             }
    52 
    53             //Return if the function completed properly
    54             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 form
    19             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 array
    48                     double det = Ah * Bv - Av * Bh;
    49                     if (AafAbs(det) < 1e-9)
    50                     {
    51                         return false;
    52                     }
    53                     else
    54                     {
    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 fail
    63             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 needed
    102 
    103 
    104             //Create final destination bits
    105             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 dbldstdib
    111             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 transformations
    127                         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                     else
    132                     {
    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             //:D
    143             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 array
    112             //计算出理想目标图片中各个“像素格”中的左上角的坐标
    113             if (CreateGrid(src, xcorner, ycorner))
    114             {
    115                 //Do the transformation
    116                 //进行转换
    117                DoTransform(src,dst, callbackfunc);
    118             }
    119 
    120             //Return if the function completed properly
    121             return ;
    122         }
    123 
    124         private bool CreateGrid(Bitmap src, List<double> xcorner, List<double> ycorner)
    125         {
    126             //mmm geometry
    127             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 ycorner
    132             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 form
    142             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 array
    171                     double det = Ah * Bv - Av * Bh;
    172                     if (AafAbs(det) < 1e-9)
    173                     {
    174                         return false;
    175                     }
    176                     else
    177                     {
    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 fail
    186             return true;
    187         }
    188 
    189         private void DoTransform(Bitmap src,Bitmap dst, Aaf_callback callbackfunc)
    190         {
    191             
    192             //Get source bitmap's information
    193             if (src == null) return ;
    194 
    195             //Create the source dib array and the dstdib array
    196             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 arrays
    201             AafPnt[] p = new AafPnt[4];
    202             AafPnt[] poffset = new AafPnt[4];
    203 
    204             //Loop through the source's pixels
    205             //遍历原图(实质上是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 pixelgrid
    213                     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 pixels
    219                     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 pixels
    236                     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 poffset
    246                             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                                 else
    265                                  srcColor = src.GetPixel(SrcIndex%src.Width  ,  SrcIndex/src.Width );
    266                                 //Add the rgb and alpha values in proportion to the overlap area
    267                                 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 message
    278                     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 needed
    290 
    291 
    292             //Create final destination bits
    293             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 dbldstdib
    299             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 transformations
    315                         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                     else
    320                     {
    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             //:D
    331             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 quadrolateral
    347                 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 quadrolateral
    351                 if (PtinConvexPolygon(p, corners[i]))
    352                     polyoverlap[polyoverlapsize++] = corners[i];
    353 
    354                 //Search for line intersections
    355                 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 left
    363                     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 right
    372                     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 bottom
    381                     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 top
    390                     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 area
    400             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 multiplication
    408             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 2
    411             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 order
    421             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 values
    436             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 element
    450                             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                     else
    458                     {
    459                         //Add point to the end of list
    460                         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 memory
    476             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 multiplication
    492             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                 else
    508                 {
    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 RGBQUDA
    528     {
    529         public byte R { get; set; }
    530         public byte G { get; set; }
    531         public byte B { get; set; }
    532     }
  • 相关阅读:
    101 上传之手动触发选在文件的上传弹出选择文件框
    11js统一设置多个样式cssText
    11 路由切换时候去掉某个路由的动画效果
    摩尔投票法
    三元环计数
    一些求和式的估算 & 杜教筛时间复杂度证明
    常胜将军!
    20220205 数论
    疯狂JAVA讲义学习——基础代码练习——内部类
    JAVA面向对象学习——java集合———List——List集合代表一个元素有序、可重复的集合,集合中每个元素都有其对应的顺序索引。List集合允许使用重复的元素,并通过索引访问。
  • 原文地址:https://www.cnblogs.com/fag888/p/5789183.html
Copyright © 2020-2023  润新知