之前想过要做个地铁驾驶的游戏,其中想把一些原本是矩形图片弄成一个梯形,但是发现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 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 }
来源:https://www.cnblogs.com/HopeGi/p/3428425.html
