Silverlight 旋转图片 无裁切 rotating an image in silverlight without cropping
小弟刚刚学习silverlight,加上今天算是有4天了(8月15日决定学的,希望学到最后不是浪费时间一场...)
想学silverlight是因为看到新浪微博中的的 修改头像 中的图像裁切功能不错,看到了甚是喜欢就做个呗。一直做下来都很顺利,直到做到最后的图像旋转功能。不知道flash里怎么样,反正做这个图像旋转功能让我很郁闷...
问题就是当你在silverlight中用RotateTransform旋转一个UIElement(比如Image)时,它只是在图像的显示层面旋转了下,而实际的Image(Image.Source)没发生任何改变;当你用RotateTransform旋转一个WriteableBitmap时,最令人郁闷的事情发生了:你旋转的图片不是正方形的话,当你90度、270度旋转时,它会用较短的边把图片裁切成正方形。。。。。。小弟初学啊,翻遍了能搜到的资料找不到解决办法,实在找不到原因,旋转后裁切的实质我一直以为是现象.......走了不少偏路。
无奈回过头来仔细阅读msdn中的WriteableBitmap和RotateTransform,发现RotateTransform无望,Pixels是可以操作的,就从它下手吧。
这个Pixels是一维数组,先把它转换成二位数组,然后就可以使用很多其他编程环境下的方法通过变换索引位置、映射值来达到图形旋转的效果了。
旋转算法如下:
Rotation[degrees counterclockwise] | Subscript Manipulation using Pixels |
0 | Out[i, j] = In[i, j] |
90 | Out[j, Right-i-1] = In[i, j] |
180 | Out[Right-i-1, Bottom-j-1] = In[i, j] |
270 | Out[Bottom-j-1,i] = In[i, j] |
i代表x轴的值,j代表Y轴上的值(如果不知道我在表达什么,直接邮件联系我吧)
另外关于图像旋转的算法,有个很好的解释在这里:
http://www.efg2.com/Lab/ImageProcessing/RotatePixels.htm
废话不多说了,直接上代码:
/// <summary> /// 只接受 0 360 -360 270 -270 90 -90 -180 180 逆时针旋转 /// </summary> /// <param name="wb">WriteableBitmap</param> /// <param name="CounterclockwiseAngle">逆时针转转的角度</param> /// <returns>WriteableBitmap rotated without cropping</returns> private WriteableBitmap WBRotate(WriteableBitmap wb, int CounterclockwiseAngle) { int[,] wb_arr = new int[wb.PixelWidth, wb.PixelHeight]; //导致许多缺页中断的初始化循环 //for (int r = 0; r < wb.PixelHeight; r++)// r=row c=column //{ // for (int c = 0; c < wb.PixelWidth; c++) // { // wb_arr[c, r] = wb.Pixels[wb.PixelWidth * r + c]; // } //} //导致较少缺页中断的初始化循环 对上面代码的简单优化 for (int c = 0; c < wb.PixelWidth; c++)// r=row c=column { for (int r = 0; r < wb.PixelHeight; r++) { wb_arr[c, r] = wb.Pixels[wb.PixelWidth * r + c]; } } WriteableBitmap wb2; int[,] wb_arr_2; if (CounterclockwiseAngle == 0 || CounterclockwiseAngle == 360 || CounterclockwiseAngle == -360)//0度 360度 { wb2 = new WriteableBitmap(wb.PixelWidth, wb.PixelHeight); wb_arr_2 = new int[wb.PixelWidth, wb.PixelHeight]; wb_arr_2 = wb_arr; } else if (CounterclockwiseAngle == 90 || CounterclockwiseAngle == -270)//90 -270//Math.Sin( { wb2 = new WriteableBitmap(wb.PixelHeight, wb.PixelWidth); wb_arr_2 = new int[wb.PixelHeight, wb.PixelWidth]; for (int c = 0; c < wb_arr.GetLength(0); c++)//width i { for (int r = 0; r < wb_arr.GetLength(1); r++)//height j { wb_arr_2[r, wb_arr.GetLength(0) - c - 1] = wb_arr[c, r]; //90 Out[j, Right-i-1] = In[i, j] Right代表真个宽度 } } } else if (CounterclockwiseAngle == 180 || CounterclockwiseAngle == -180)//180 -180 { wb2 = new WriteableBitmap(wb.PixelWidth, wb.PixelHeight); wb_arr_2 = new int[wb.PixelWidth, wb.PixelHeight]; for (int c = 0; c < wb_arr.GetLength(0); c++)//width i { for (int r = 0; r < wb_arr.GetLength(1); r++)//height j { wb_arr_2[wb_arr.GetLength(0) - c - 1, wb_arr.GetLength(1) - r - 1] = wb_arr[c, r]; //180 Out[Right-i-1, Bottom-j-1] = In[i, j] } } } else if (CounterclockwiseAngle == 270 || CounterclockwiseAngle == -90)//270 -90 { wb2 = new WriteableBitmap(wb.PixelHeight, wb.PixelWidth); wb_arr_2 = new int[wb.PixelHeight, wb.PixelWidth]; for (int c = 0; c < wb_arr.GetLength(0); c++)//width i { for (int r = 0; r < wb_arr.GetLength(1); r++)//height j { wb_arr_2[wb_arr.GetLength(1) - r - 1, c] = wb_arr[c, r]; //270 Out[Bottom-j-1,i] = In[i, j] } } } else { return null;//如果是其他角度 就直接返回null } //将二位数组转换成Pixels for (int r = 0; r < wb_arr_2.GetLength(1); r++)// r=row c=column { for (int c = 0; c < wb_arr_2.GetLength(0); c++) { wb2.Pixels[wb2.PixelWidth * r + c] = wb_arr_2[c, r]; } } return wb2; }
再过两天,把模仿新浪微博的玩意儿做个总结发出来。
这次源代码下载: https://files.cnblogs.com/ice6/RotateImageWithoutCroppingInSL.rar
源代码里对比了用RotateTransform和本文方法的对比,截图如下:
使用RotateTransform:
使用本文方法:
BTW:请不要试图用它旋转过大的图片(比如超过5Mb),如果你想的话,请在上面代码的合适地方加上 wb = null;GC.Collect();
另外,Pixels的算法本身效率不高,有人说用Buffer.BlockCopy可以提高效率,但我不知道怎么做。有做出来的朋友告诉我声 :)
本位于2010/8/20日更新,优化了一点点算法,插入了对比图,更新了源代码。
源代码下载: https://files.cnblogs.com/ice6/RotateImageWithoutCroppingInSL.rar