Cropping Images in Windows Phone 7

26 September 2010

Recently I had to do some work in cropping an image programmatically through Windows Phone 7, and while it's not hard, it wasn't immediately obvious how to do it, so I thought I would share.

While the Clip property does seem to crop images, the image still remains its original size, making it not a true "crop". Instead, we'll use the WriteableBitmap class found as part of Silverlight.

WriteableBitmap takes in a few constructors, including a fixed pixel width/height constructor, and once constructed, you call the .Render() method and pass in UIElements (so that's Textblocks, Buttons, etc - but most importantly: Image), and a Transform if you want to resize/translate your UIElement.

Therefore, to crop an image, we just need to create a WriteableBitmap with the size of the target cropped area, and pass in a transform that will move the Image to the right area within the crop surface.

After rendering all the UIElements required on the WriteableBitmap, we need to call the .Invalidate() method to force it to redraw its contents and display properly.

Here's some sample code that crops an image (provided as stream data from, say, a WebService class) to a square:

internal static Stream CropSquare(Stream stream)
{
    //turn stream into bitmapimage object
    BitmapImage bitmapImage = new BitmapImage();
    bitmapImage.SetSource(stream);

    //calculate bounding box
    int iOriginalWidth = bitmapImage.PixelWidth;
    int iOriginalHeight = bitmapImage.PixelHeight;

    if (iOriginalWidth == iOriginalHeight)
    {
        //image is already square - return it
        return stream;
    }

    int smallestSide = (iOriginalHeight < iOriginalWidth) ? iOriginalHeight : iOriginalWidth;

    //generate temporary control to render image
    Image temporaryImage = new Image { Source = bitmapImage, Width = iOriginalWidth, Height = iOriginalHeight };

    //create writeablebitmap
    WriteableBitmap wb = new WriteableBitmap(smallestSide, smallestSide);
    wb.Render(temporaryImage, new TranslateTransform { X = (iOriginalWidth - smallestSide) / -2, Y = (iOriginalHeight - smallestSide) / -2 });
    wb.Invalidate();

    //get stream from writeablebitmap
    Stream streamResizedImage = new MemoryStream(); //will need to be disposed by whatever is using this method
    wb.SaveJpeg(streamResizedImage, smallestSide, smallestSide, 0, 70);            
    return streamResizedImage;  
}

As shown above, we can resize, skew and pretty much do anything we want to an image by passing in the correct combination of translations to the render method of the WriteableBitmap. Note that to convert the WriteableBitmap to a stream, the .SaveJpeg() method was called. I'm not entirely sure which version of Silverlight supports this, or whether it's a Windows Phone 7 specific extension as I couldn't find it on MSDN, but it works in the RTM WP7 tools so I imagine it's not going to be a problem.

Here is a sample project that I made while working all this out, which demonstrates the code I provided in this post in use.

Hope that was helpful!

Tags: images, wp7, writeablebitmap

Add a Comment

No Comments