2

I have a simple WPF application where I display one very large image (9000x2875) and on top of it, many small images (64x64).

To do this, I have a Canvas with one Image, then I programatically add the small images as they arrive.

Now I am trying to save portions of the composite image as png files. I thought I would use a RenderTargetBitmap to render the portion of the Canvas that I wanted. My problem is that I cannot find a good way to save the right portion of the image. Here is a my current hack:

private static void SaveImage(Canvas canvas, string file, int x, int y, int width, int height)
{
  //changing 0,0 on the canvas so RenderTargetBitmap works as expected.
  canvas.RenderTransform = new MatrixTransform(1d, 0d, 0d, 1d, -x, -y);
  canvas.UpdateLayout();
  RenderTargetBitmap bmp = new RenderTargetBitmap(width, height, 96d, 96d, Pixelformats.Pbgra32);
  bmp.Render(canvas);
  PngBitmapEncoder encoder = new PngBitmapEncoder();
  encoder.Frames.Add(BitmapFrame.Create(bmp));
  using(Stream s = File.Create(file))
  {
    encoder.Save(s);
  }
}

The obvious problem with this is that the display will change due to the RenderTransform. It also makes the application slower. I did try to do a RenderTargetBitmap of the entire canvas, but that was much slower than doing this.

So my questions are:
Is there an easier way to save just a portion of the viewed image?
If not, does someone have a suggestion for a better way to go about this? (I already tried a single WriteableBitmap, but that was about as slow as doing the RenderTargetBitmap of the entire canvas.

Joel Rondeau
  • 7,486
  • 2
  • 42
  • 54

1 Answers1

5

What you want to use is a CroppedBitmap, which will allow you to save a cropped portion of your image.

// (BitmapSource bmps)
CroppedBitmap crop = new CroppedBitmap(bmps, new Int32Rect(selRect.X, selRect.Y, selRect.Width, selRect.Height));

Edit: Since there seems to be no way to get this to perform the way you want in WPF I would suggest pre-cropping the large image using GDI+ (without displaying it) and loading the region of it you want onto a smaller canvas.

Ed Bayiates
  • 11,060
  • 4
  • 43
  • 62
  • In this case, I don't have a `BitmapSource`, I have a `Canvas`. I was using a `CroppedBitmap` when I did a `RenderTargetBitmap` of the entire `Canvas`, but as I mentioned, that was extremely slow. – Joel Rondeau Jul 19 '11 at 17:02
  • You didn't mention that in your question. Have you tried making a smaller Canvas, and setting the left and top so that it only shows the portion of the image you want to render? – Ed Bayiates Jul 19 '11 at 17:07
  • I tried making a smaller canvas within my canvas, without good results. I did not try making my large canvas smaller. I will give that a try. – Joel Rondeau Jul 19 '11 at 17:10
  • It appears that a smaller canvas doesn't noticeably affect the speed of things, making it approximately identical to my existing hack. – Joel Rondeau Jul 19 '11 at 17:30
  • Have you tried a CroppedBitmap of your large image first, put that on smaller canvas, then set smaller images on the smaller canvas? – Ed Bayiates Jul 19 '11 at 17:32
  • 1
    The secret was to replace my Canvas with a small VirtualizingStackPanel (technically, I kept a canvas around for viewing and have a separate VirtualizingStackPanel for the file creation). That makes the UpdateLayout immensely faster, making it worthwhile to do. If you edit your answer to mention the VirtualizingStackPanel, I'll mark it as correct. Thank you. – Joel Rondeau Jul 19 '11 at 17:42
  • And now I feel like an idiot because all my saved images were blank (minus the ones I looked at which were created from the small canvas). – Joel Rondeau Jul 19 '11 at 18:11
  • 1
    Ok. I feel less like an idiot now. Back to a `CroppedBitmap`. Given that I have a separate canvas for viewing, there's no need to put the entire gigantic image in the second canvas. Once I know the area I need cropped, I just put that area into the canvas, add any of the smaller images that are also in the cropped area, and RenderTargetBitmap that. Works much better now. – Joel Rondeau Jul 20 '11 at 02:23