none
How to crop an image using BitmapTransform?

    Question

  • On another thread, it is said that the "CroppedBitmap" class is currently not available in WinRT.

    So, is there anyway we can crop a image using BitmapTransform?

    Input: Image, cropping location and size

    Output: the cropped image

    Thanks!


    Thanks.

    Monday, February 13, 2012 7:47 AM

Answers

  • Working code method for BitmapTransform and cropping:

    The code takes an image, scales it to 100 x 100, and then renders the bottom right corner of the image.

     async private void BitmapTransformTest()
            {
                // hard coded image location
                string filePath = "C:\\Users\\Public\\Pictures\\Sample Pictures\\fantasy-dragons-wallpaper.jpg";
    
                StorageFile file = await StorageFile.GetFileFromPathAsync(filePath);
                if (file == null)
                    return;
    
                // create a stream from the file and decode the image
                var fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
                BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fileStream);
                
    
                // create a new stream and encoder for the new image
                InMemoryRandomAccessStream ras = new InMemoryRandomAccessStream();
                BitmapEncoder enc = await BitmapEncoder.CreateForTranscodingAsync(ras, decoder);
    
                // convert the entire bitmap to a 100px by 100px bitmap
                enc.BitmapTransform.ScaledHeight = 100;
                enc.BitmapTransform.ScaledWidth = 100;
                
                
                BitmapBounds bounds = new BitmapBounds();
                bounds.Height = 50;
                bounds.Width = 50;
                bounds.X = 50;
                bounds.Y = 50;
                enc.BitmapTransform.Bounds = bounds;
                
                // write out to the stream
                try
                {
                    await enc.FlushAsync();
                }
                catch (Exception ex)
                {
                    string s = ex.ToString();
                }
    
                // render the stream to the screen
                BitmapImage bImg = new BitmapImage();
                bImg.SetSource(ras);
                img.Source = bImg; // image element in xaml
    
            }


    • Edited by RobHogue Wednesday, February 15, 2012 9:29 PM
    • Marked as answer by Tintin- Wednesday, February 22, 2012 5:50 AM
    Wednesday, February 15, 2012 9:26 PM

All replies

  • You can crop a bitmap using WriteableBitmap.PixelBuffer.

    Filip Skakun

    Monday, February 13, 2012 5:01 PM
  • Any code samples?

    Thanks.

    Monday, February 13, 2012 5:18 PM
  • Hi TinTin-,

    Did you try RobHogue's sample code from this previous thread?

    --Rob

    Monday, February 13, 2012 7:34 PM
    Owner
  • I read that thread, it was trying to playing all the bits within the image.

    However, in WinRT there is this BitmapTransform class, which has a property called “bounds”, sounds as if it can do something with the cropping, but I haven't found the related sample code yet.

    Bounds

      Read/write Specifies the bounding rectangle that is used to crop the bitmap

    http://msdn.microsoft.com/en-us/library/windows/apps/windows.graphics.imaging.bitmaptransform





    • Edited by Tintin- Monday, February 13, 2012 8:52 PM
    Monday, February 13, 2012 8:47 PM
  • anyone?

    Thanks.

    Tuesday, February 14, 2012 11:32 PM
  • Are you trying to crop an in memory bitmap to a smaller in memory bitmap or are you trying to crop an image while loading or saving it?

    BitmapTransform is used when encoding and decoding with BitmapEncoder and BitmapDecoder. Generally this would be done while saving or loading the file. You could theoretically use it to encode a cropped version of your bitmap and then decode that back into a usable image, but you'll likely be better off transferring the pixels between WritableBitmaps.

    --Rob

    Tuesday, February 14, 2012 11:53 PM
    Owner
  • I was actually implementing a small game.

    Given an image, in the game, it will be cropped to multiple smaller images, and displayed on different UI parts.

    Most likely I need to save those smaller images to files, so that they can be displayed on UI controls.


    Thanks.


    • Edited by Tintin- Wednesday, February 15, 2012 12:10 AM
    Wednesday, February 15, 2012 12:09 AM
  • WinRT is new, so it is difficult to find samples related to "BitmapTransform" class.

    On the related msdn page, it doesn't provide any samples.


    Thanks.

    Wednesday, February 15, 2012 12:14 AM
  • Working code method for BitmapTransform and cropping:

    The code takes an image, scales it to 100 x 100, and then renders the bottom right corner of the image.

     async private void BitmapTransformTest()
            {
                // hard coded image location
                string filePath = "C:\\Users\\Public\\Pictures\\Sample Pictures\\fantasy-dragons-wallpaper.jpg";
    
                StorageFile file = await StorageFile.GetFileFromPathAsync(filePath);
                if (file == null)
                    return;
    
                // create a stream from the file and decode the image
                var fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
                BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fileStream);
                
    
                // create a new stream and encoder for the new image
                InMemoryRandomAccessStream ras = new InMemoryRandomAccessStream();
                BitmapEncoder enc = await BitmapEncoder.CreateForTranscodingAsync(ras, decoder);
    
                // convert the entire bitmap to a 100px by 100px bitmap
                enc.BitmapTransform.ScaledHeight = 100;
                enc.BitmapTransform.ScaledWidth = 100;
                
                
                BitmapBounds bounds = new BitmapBounds();
                bounds.Height = 50;
                bounds.Width = 50;
                bounds.X = 50;
                bounds.Y = 50;
                enc.BitmapTransform.Bounds = bounds;
                
                // write out to the stream
                try
                {
                    await enc.FlushAsync();
                }
                catch (Exception ex)
                {
                    string s = ex.ToString();
                }
    
                // render the stream to the screen
                BitmapImage bImg = new BitmapImage();
                bImg.SetSource(ras);
                img.Source = bImg; // image element in xaml
    
            }


    • Edited by RobHogue Wednesday, February 15, 2012 9:29 PM
    • Marked as answer by Tintin- Wednesday, February 22, 2012 5:50 AM
    Wednesday, February 15, 2012 9:26 PM
  • I'll try. Thanks Rob.


    Thanks.

    Friday, February 17, 2012 12:18 AM
  • Hi Rob,

    Thanks a lot!

    After scale the image quality drops down a lot, is there anyway to scale the image with a better quality?


    Thanks.

    Tuesday, February 21, 2012 10:16 PM
  • If your asking about different encoding options, you will need to look at the BitmapEncoder properties to see if there an option that better suites your needs.

    My advise is to grab a base image, scale it in gimp\photoshop, and compare that image to what is generated in code. If they are not the same, you may be able to tweak an encoder property to get the results you want, and if not, you can post the images here for microsoft to look at.

    Wednesday, February 22, 2012 12:34 AM
  • Thanks Rob, I'll try.

    Your previous sample code really saved my day, I'll mark as answer, thanks :)


    Thanks.

    Wednesday, February 22, 2012 5:50 AM
  • Here is an alternative approach gettting access to the pixels as an array of bytes. The code below loads an image from a path (uri) and return the pixels as an array of bytes. The second method accepts an array of bytes, cuts out the specified rectangle and returns it as a new bitmapimage.

    // Loads an image from URI and returns it as an array of bytes

    private async Task<Byte[]> loadPixelsAsync(Uri uri)

    {

    StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(uri);                                  

    IRandomAccessStream fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read

    BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fileStream);                                       

    PixelDataProvider pixels = await decoder.GetPixelDataAsync();

    return pixels.DetachPixelData();                                                                           
    }


    // Returns a bitmap image that is cut from an array of bytes representing an image. Frame is the area cut from within width x height

    private async Task<BitmapImage> imageFromBytes(byte[] imagePixels, int width, int height, Rect frame)

    {

    BitmapImage bitmap = newBitmapImage();                                                                     

           int xStart = (int)frame.X;                                      

    int yStart = (int)frame.Y;

    int frameWidth = (int)frame.Width;

    int frameHeight = (int)frame.Height;

           byte[] framePixels = new byte[4 * frameWidth * frameHeight];                                               

    for (int y = 0; y < frameHeight; y++)

    {

    for (int x = 0; x < frameWidth; x++)

    {

    int frameOffset = (4 * y * frameWidth) + (4 * x);                                                  
           int imageOffset = (4 * (y + yStart) * width) + (4 * (x + xStart));                                 

    framePixels[frameOffset] = imagePixels[imageOffset];

    framePixels[frameOffset+1] = imagePixels[imageOffset+1];

    framePixels[frameOffset+2] = imagePixels[imageOffset+2];

    framePixels[frameOffset+3] = imagePixels[imageOffset+3];

    }

    }

    InMemoryRandomAccessStream bitmapstream = new InMemoryRandomAccessStream();

    BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, bitmapstream);

    encoder.SetPixelData(BitmapPixelFormat.Rgba8, BitmapAlphaMode.Straight, (uint)frameWidth, (uint)frameHeight, 96, 96, framePixels);

          

           try { await encoder.FlushAsync(); }

    catch { return bitmap; }

    bitmap.SetSource(bitmapstream);

    return bitmap;

    }


    Sunday, March 11, 2012 6:47 PM
  • Thanks a lot! It's working :)
    Friday, June 15, 2012 7:55 AM
  • Very helpful !! Can somebody suggest me how to save cropped BitmapImage to my local disk ?

    • Proposed as answer by thr0ttles Friday, August 10, 2012 3:12 AM
    • Unproposed as answer by thr0ttles Friday, August 10, 2012 3:12 AM
    Wednesday, July 25, 2012 9:00 AM
  • Try this on for size...

            public async void BitmapTransformAndSaveTest()
            {
                var uncroppedfile = await Windows.Storage.ApplicationData.Current.LocalFolder.GetFileAsync("uncropped.jpg");
                if (uncroppedfile == null)
                {
                    return;
                }
    
                WriteableBitmap wb = null;
    
                // create a stream from the file and decode the image
                var fileStream = await uncroppedfile.OpenAsync(Windows.Storage.FileAccessMode.Read);
                var decoder = await BitmapDecoder.CreateAsync(fileStream);
    
                // create a new stream and encoder for the new image
                using (InMemoryRandomAccessStream ras = new InMemoryRandomAccessStream())
                {
                    var enc = await BitmapEncoder.CreateForTranscodingAsync(ras, decoder);
    
                    // convert the entire bitmap to a 100px by 100px bitmap
                    enc.BitmapTransform.ScaledHeight = 100;
                    enc.BitmapTransform.ScaledWidth = 100;
    
                    var bounds = new BitmapBounds();
                    bounds.Height = 50;
                    bounds.Width = 50;
                    bounds.X = 50;
                    bounds.Y = 50;
                    enc.BitmapTransform.Bounds = bounds;
    
                    // write out to the stream
                    await enc.FlushAsync();
    
                    // create a writeable bitmap from the stream
                    ras.Seek(0);
                    wb = new WriteableBitmap(100, 100);
                    wb.SetSource(ras);
                }
    
                // save the cropped file now
                var croppedfile = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync("cropped.jpg", Windows.Storage.CreationCollisionOption.ReplaceExisting);
                using (var stream = await croppedfile.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite))
                {
                    var pixelStream = wb.PixelBuffer.AsStream();
                    var bytes = new byte[pixelStream.Length];
                    pixelStream.Seek(0, SeekOrigin.Begin);
                    await pixelStream.ReadAsync(bytes, 0, (int)pixelStream.Length);
                    await pixelStream.FlushAsync();
    
                    var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, stream);
                    encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Straight, (uint)wb.PixelWidth, (uint)wb.PixelHeight, 0, 0, bytes);
                    await encoder.FlushAsync();
                    await stream.FlushAsync();
                }
            }

    Friday, August 10, 2012 3:28 AM
  • Hi,

    I tried this code but it wasn't showing the image. Then I found that before creating the BitmapImage to show we need to set stream position to 0:

    ras.Seek(0);

    In this way it works, the image is cropped and displayed.

    Friday, October 05, 2012 4:02 AM