C#实现对writeablebitmap的截图

  某天参加微软编程马拉松,我们做的是Jigsaw拼图小游戏,有一个功能是让玩家选择图片,然后应用将其剪裁成3*3的小块进行拼图游戏。剪裁的效果用Image.Clip实现不了,

<Image.Clip > 
    <RectangleGeometry Rect="x1 y1 width height"/>`
</Image.Clip>

Image.Clip中:

<RectangleGeometry Rect=”x1 y1 width height”/>

参数x1 y1代表以当前控件左上顶点为坐标原点,显示区域左上顶点的坐标,width height则是显示区域的宽和高。所以用:

<RectangleGeometry Rect=”233 233 233 233″/>

从大图中截取中间部分便会出现:

初始

未达到效果

显然没有达到效果,于是就采用对图片像素点的处理达到截图效果

涉及要点:
WriteableBitmap可写的位图
StorageFile 文件
IRandomAccessStream 可随机访问输入和输出流
在WriteableBitmap中图像的每个像素点是采用RGBA色彩模式(不知道是不是这样叫的)储存的,是通过对红(R)、绿(G)、蓝(B)三个颜色分量和Alpha的色彩空间(用来控制透明度)的变化以及它们相互之间的叠加来得到各式各样的颜色的。一个像素点占四个字节,四个字节依次代表蓝、绿、红、透明度。每个分量0~255范围内的强度值,对于透明度0代表全透明,255代表不透明。

具体操作见代码:

const int BigImgWidth = 699, BigImgLenth = 699;
const int SmallImgWidth = 233, SmallLenth = 233;

//定义可写的位图,参数为宽和长
WriteableBitmap bigbitmap = new WriteableBitmap(BigImgWideth, BigImgLenth);
WriteableBitmap smallbitmap = new WriteableBitmap(SmallImgWidth, SmallImgLenth);

StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/123.jpg"));//定义文件并打开123.jpg

IRandomAccessStream fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);//定义文件流
await bigbitmap.SetSourceAsync(fileStream);//给大图赋值

Stream str = bigbitmap.PixelBuffer.AsStream();
                       //将大图转化为流,这是将大图转化为byte[]数组的中间步骤
byte[] bigpixels = new byte[str.Length];  //将stream转成byte[]
str.Read(bigpixels, 0, bigpixels.Length);

byte[] smallpixels = new byte[4 * smallbitmap.PixelWidth * smallbitmap.PixelHeight];  //定义小图的位数组的大小,4 * 宽 *长,单位是像素(Pixel)

const int startx = 233, starty = 233;  //截图区域左上角点的坐标(按理说应该把截图写成函数,把startx,starty,以及小图的长宽以实参的形式传进来的)
for (int y = 0; y < smallbitmap.PixelHeight; y++)
    for (int x = 0; x < smallbitmap.PixelWidth; x++)
    {
        //核心代码就下面两句,将sindex和index为小图和大图转化成位数组(一维数组)的下标
        int sindex = 4 * (y * smallbitmap.PixelWidth + x);
        int index = 4 * ((starty + y) * bigbitmap.PixelWidth + x + startx);
        smallpixels[sindex + 0] = bigpixels[index + 0];
                                              //复制蓝色分量的值
        smallpixels[sindex + 1] = bigpixels[index + 1];
                                              //复制绿色分量的值
        smallpixels[sindex + 2] = bigpixels[index + 2];//复制红色分量
        smallpixels[sindex + 3] = bigpixels[index + 3];//复制透明度分量
    }

        Stream pixelStream = smallbitmap.PixelBuffer.AsStream();
        await pixelStream.WriteAsync(smallpixels, 0, smallpixels.Length);
        smallbitmap.Invalidate();//重新绘制整个位图
        img2.Source = smallbitmap;//将控件img2的源设为smallbitmap
    }

可以看到实现了想截哪块就截哪块的功能了。
结果