以前想過要作個地鐵駕駛的遊戲,其中想把一些本來是矩形圖片弄成一個梯形,可是發現GID+上面沒有相似的方法。因而在谷歌谷了一下。沒有!只能找到使人垂涎的,並無源碼。按照本身的想法嘗試了一兩天,有點效果,但實際上不是那樣。後來知道那個在數字圖像處理中叫「透視變換」。因而上網找了相關資料,原理找了,看了不明白。代碼沒多少,有ActionScript的,不明;有C的,不明。真笨啊!後來在CodeProject上面看到一份外國人的博文,全英文看不太明白,但看了一幅圖,大概知道他意思了。下了份源碼看看,C++的。好不容易翻譯成C#的(感受仍是保留了很多C++風格的東西),編譯經過,運行正常。後來才一步一步的閱讀代碼。還沒全懂,先把懂的部分記錄下來。之後繼續研究繼續補充。node
先看看效果算法
界面是仿照某我的(網上版本太多,找不到原做者)的弄出來的,界面不是重點,重點是算法。下面就直接貼老外的那幅圖大體講講思想。數組
首先是從本來圖片轉化成一幅理想化的目標圖片,那幅圖片只是理想化的,最終的圖片是最右邊的那幅。轉換的過程就是根據轉換後圖片的四個角計算出目標圖片的size,生成一個矩陣,就是那個Destination Image,而後把理想化的目標圖片覆蓋過去,把理想化圖片的每一個「像素格」(已經不是真正的像素格了,由於通過了扭曲變形)跟那個矩陣對比,看看覆蓋了哪些格子,覆蓋的面積有多少,按百分比地把顏色累加到對應的格子上。實際上那個格子就至關於新圖片的像素點了。按照矩陣生成最終的目標圖。ide
接着就介紹算法裏面調用的方法層次oop
把已經弄懂(並不表明徹底懂的)的代碼貼出來,首先是最外層的方法this
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 }
上面這個方法只是定了目標圖片的尺寸,其他什麼都沒作。下面這個方法還沒作多少轉換的事spa
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 }
下面這個方法就利用上面的方法計算出的座標點集合進行按比例填色。上面每一個像素點的四個角的座標,都會在下面方法從新提取出來組回一個四邊形,具體仍是結合代碼和註釋看看code
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就不列出來了,由於還沒看懂,看明白了也會在本文中補充。若想看的,本文最後會貼出全部源碼。orm
但願有看的明白的朋友能多指點一下,謝謝!還要感謝一我的,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 }