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中的WriteableBitmapRotateTransform,发现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

posted on 2010-08-19 20:15  ice6  阅读(3074)  评论(2编辑  收藏  举报

导航