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

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

  先看看效果

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

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

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

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

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

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

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

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

 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姐,在我阅读这个算法时给了不少灵感为我。搞这个算法,让我想起了大三上的一门课《医学图像处理》,我的老师涂泳秋老师。

C# 图片自由变换 任意扭曲
  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     }
Aaform

 

 

你可能感兴趣的:(C#)