Resizing images in Silverlight 3 using WriteableBitmap - silverlight

This is my first day with Silverlight. I’m trying to prototype an application which (among other functions) should be able to resize user supplied images. It should be able to handle and display several resized images at once. The most obviously approaches I've tried seem to "leak" memory in the sense that the original bitmaps are still being referenced in some way which causes Silverlight to allocate hundreds of megabytes of memory after a while. I just want to be able to load the images one by one, resize them and keep the small versions.
To be precise, I've tried the following:
Creating a list of System.Windows.Controls.Image's (and scaling them). I'm not surprised that this has not worked.
Creating a list of rectangles filled by image brushes. I'm not surprised either.
Rendering the bitmaps into System.Windows.Media.Imaging.WriteableBitmap. I expected this to perform well; I assumed that the bitmaps are really just drawn directly and not referenced in any way. However, the memory consumption indicated otherwise.
Here is a snippet of the relevant piece of code:
// create image source
Stream stream = file.OpenRead();
BitmapImage bmpImg = new BitmapImage();
bmpImg.SetSource(stream);
stream.Close();
// create temporary image from it
Image tmpImg = new Image();
tmpImg.Source = bmpImg;
// this is required by WriteableBitmap
tmpImg.Measure(new Size(100, 100));
tmpImg.Arrange(new Rect(0, 0, 100, 100));
// prepare scaling to 100x100
ScaleTransform scaleTrans = new ScaleTransform();
double scale = (double)100 / (double)Math.Max(bmpImg.PixelHeight, bmpImg.PixelWidth);
scaleTrans.CenterX = 0;
scaleTrans.CenterY = 0;
scaleTrans.ScaleX = scale;
scaleTrans.ScaleY = scale;
// render
WriteableBitmap writeableBitmap = new WriteableBitmap(100, 100);
writeableBitmap.Render(tmpImg, scaleTrans);
writeableBitmap.Invalidate();
// final image
Image img = new Image();
img.Source = writeableBitmap;
I hope I'm not missing anything silly, but it looks to OK to me and does the right thing (except the memory problem). Please also bear in mind that code quality is not supposed to be a production quality; it’s just a quick and dirty prototype.
I noticed that I’m not alone; I found questions related to image manipulation in Silverlight. I’m also aware of the fact that I could use some third party library, do processing on the server or write something even myself, but I’m surprised that Silverlight does not offer any basic image manipulation functions. It does not seem to be an uncommon requirement given where Silverlight positions itself.

Have you looked at the WriteableBitmapEx project? It's an open source project with a tonne of extension methods for the WriteableBitmap class. Here's how you resize:
BitmapImage image = new BitmapImage();
image.SetSource(dialog.File.OpenRead());
WriteableBitmap bitmap = new WriteableBitmap(image);
WriteableBitmap resizedBitmap = bitmap.Resize(500, 500, WriteableBitmapExtensions.Interpolation.Bilinear);
// For uploading
byte[] data = resizedBitmap.ToByteArray();

I don't know about the specifics here, but if you're leaking resources - you might look at which of your objects implements the IDisposable interface. I would guess that the Stream and Image classes implement this interface. And if they do - calling Dispose() on them (or wrapping their usage in a "Using" statement) will cause them to free their resources immediately rather than waiting for the garbage collector to kick in eventually.

Related

WPF 3D graphics redraw when data changes

I am rendering a 3D contour surface from data. I get new data every few seconds from real time monitoring and need to redraw using the new data. All of the examples I can find only show how render the 3D contour surface from fixed data.
I can't find an example of explanation of how to (clear, delete, erase) the mesh or model and redraw with new data.
Can anyone point me to an example or explain how to redraw the 3D contour surface with the new data.
I have solved this issue with the help of several answered questions.
Removed all children of the Model3DGroup 'main_model_3D_group'.
for (int i = main_model_3D_group.Children.Count-1; i >= 0; i--)
{
if (main_model_3D_group.Children[i] is GeometryModel3D)
main_model_3D_group.Children.RemoveAt(i);
}
rebuild the GeometryModel3D objects
add the GeometryModel3D objects back to the children of the main_model_3D_group.
Second Issue encountered:
I used an ImageBrush to get a gradient colored surface material.
The ImageBrush.ImageSource locks the file so you can't save the same filename with new color data.
Solved this with the following function;
public ImageSource BitmapFromUri(Uri source)
{
bitmap.BeginInit();
bitmap.UriSource = source;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.EndInit();
return bitmap;
}
This function leaves Uri source (xxx.PNG file) unlocked, but the 2nd call doesn't reload the file (assume because it is cached). Solved this by using 2 filename (odd/even). So every other call used a different filename.

when to use WriteableBitmap and BitmapImage in silverlight

I am trying to display image using BitmapImage for some time and it worked.I have changed the image and it stopped working.
For Bitmapimage I was using this code:
`ms.Seek(0, SeekOrigin.Begin); // ms is memory stream
BitmapImage b = new BitmapImage();
b.SetSource(ms);
image.ImageSource = b;`
I have ran into piece of code where it was checking if the length of the bytes[] ==14400
if(bytes.length == 14400)
{
var bmp = new WriteableBitmap(width, height);
Buffer.BlockCopy(buffer, 0, bmp.Pixels, 0, buffer.Length);
}
I want to know when to use WriteableBitmap and BitmapImage .
From iProgrammer:
Bitmaps are generally implemented as immutable objects. What this means is that once you create a bitmap you can't make any changes to it. You can manipulate bitmaps by creating new versions, which then immediately become immutable....
The WriteableBitmap, as its name suggests, isn't immutable and you can get at its individual pixels and manipulate them as much as you want. This is the ideal way to work when you need dynamic bitmaps.
iProgrammer - WriteableBitmap
From MSDN:
"Use the WriteableBitmap class to update and render a bitmap on a per-frame basis..." MSDN - WriteableBitmap Class
The Examples section of the MSDN article also shows how to update a WritableBitmap image when responding to mouse events. The code in the example erases pixels of the image by setting the pixel's ARGB values to 0 when the mouse's right button is down. The code also shows how to update individual pixels in the image where the mouse's left button is down. Essentially the code shows a rudimentary pixel image editor.
The point, however, is that you can't change image data when using regular bitmap - you have to use WritableBitmap instead. You can, however, render both if you wish.

Releasing BitmapImages used as Image control Source (memory problem)

I have a simple WPF window with an Image control and a Slider control under it. When the user moves the slider, I'm loading and setting various images as Source of the Image-control. I got quite a few images, varying in size up to a 200 KB, but when I move that slider back and fourth, the program starts to eat quite a lot of memory. Hundreds and hundreds of megs of memory. Most of it gets garbage collected eventyally, but not all.
Maybe WPF isn't the way to go or should I force a G/C? I've tried loading the image as Bitmap and getting the bitmap source with Imaging.CreateBitmapSourceFromHBitmap() and Win32-api's to delete and dispose and so on, but I'm just making things worse :)
I guess I should try to grab the existing image source and release it somehow before loading and assigning a new one.
Any ideas?
EDIT
I'm adding some sample code that worked fine and seems to keep the memory low and fine:
private Image _lastImage;
// Event when user moves the slider control, load image using the filname in
// the _images[] array which contains hundreds of images
private void SliderChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (_lastImage != null)
_lastImage.Dispose();
var image = Image.FromFile(_images[(int)ImageSlider.Value]);
Snapshot.Source = ImageToBitmapImage(image);
_lastImage = image;
}
private static ImageSource ImageToBitmapImage(Image image)
{
BitmapImage bitmapImage;
using (var ms = new MemoryStream())
{
image.Save(ms, ImageFormat.Jpeg);
bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = new MemoryStream(ms.ToArray());
bitmapImage.EndInit();
}
return bitmapImage;
}
Forcing a GC collection is indeed horrible. Instead call the .Dispose() method of the Bitmap object, that's what it's there for!
The method you are using (Imaging.CreateBitmapSourceFromHBitmap()) is unmanaged and probably requires that you manually .Dispose() of it.
WPF allows you to load a bitmap via a given URI : see http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.bitmapimage.aspx
The GC will take care of collecting any managed objects you have (by using BitmapImage for example).
You are probably loading too many images to memory. the source images accumulate memory pretty fast (1024x786 image at 32bit color takes up around 3mb of ram). The GC has to work very hard to clean them up. You could reduce the size of the image after loading (scale it down) and use up less memory
Finally, the GC does not guarantee that all memory will be released after the GC cycle.
It promises "best effort". although typically calling GC.Collect(0); will force a full GC cycle and release almost everything that can be released.

Using resources images in WPF

From what I've seen, WPF images' source is a BitmapSource object while the Resources images are read as bitmap. I've easily found a way to convert images from Bitmap format into BitmapSource format but I believe that running this conversion every time I need an image is just costly in terms of performance and memory consumption (The Bitmaps themselves are static but I guess that the conversion allocates a new object in memory for the BitmapSource).
I thought about holding an in-memory, lazy cache for the BitmapSource objects, that way I will only need to run the conversion once per image. However, I find it hard to believe that this is the proper solution for this issue.
The nature of the application is that the data extends over time and the business objects are never disposed. Therefore, this assumption will only hold in case the implementation will hold static resources for these static images.
I will very much appreciate the proper solution for this issue.
I'm not sure I understand what you mean but you can use Image from Resources like this.
If you have an Image resource named "B1.png" and you set "Build Action" to "Resource" for it than you can use it in xaml like this.
<Image Name="c_image" Source="/YourAssemblyName;component/Resources/B1.png" />
and in code behind like this
Image img = new Image();
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.UriSource = new Uri("pack://application:,,,/YourAssemblyName;component/Resources/B1.png");
bitmapImage.EndInit();
img.Source = bitmapImage;

WPF WriteableBitmap Memory Leak?

I'm trying to figure out how to release a WriteableBitmap memory.
In the next section of code I fill the backbuffer of a WriteableBitmap with a really large amount of data from "BigImage" (3600 * 4800 px, just for testing)
If I comment the lines where bitmap and image are equaled to null, the memory it´s not release and the application consumes ~230 MB, even
when Image and bitmap are no longer used!
As you can see at the end of the code its necessary to call GC.Collect() to release the memory.
So the question is, what is the right way to free the memory used by a WriteableBitmap object? Is GC.Collect() the only way?
Any help would be great.
PS. Sorry for my bad english.
private void buttonTest_Click(object sender, RoutedEventArgs e)
{
Image image = new Image();
image.Source = new BitmapImage(new Uri("BigImage"));
WriteableBitmap bitmap = new WriteableBitmap(
(BitmapSource)image.Source);
bitmap.Lock();
// Bitmap processing
bitmap.Unlock();
image = null;
bitmap = null;
GC.Collect();
}
Are you running your test on Windows XP, using .Net 3.5 SP1?
If so, then it is a known problem, that will be fixed in 4.0.
See
http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/5d88cdf1-e992-4ad4-8f56-b5dbf92dcf1c
In general, memory should eventually be freed automatically as needed.
However, for that to happen, you need to be sure that the object is truly unused: No references to the object may exist anywhere, including references which "aren't used anymore". So, in particular, if you place your WriteableBitmap and original BitmapSource in variables of a long-lived class, they won't be released until the container is.
Also, WPF uses a retained GFX model: when you render, you're actually just storing instructions on how to render. The "instructions" on how to render a bitmap include a reference to the bitmap - so if you ever render a large bitmap, then for a while (at least as long as it's on screen - even if the version on screen is tiny) those images will be retained.
In practice; store references to these bitmaps only where they are needed, and if the context in which they live is long-lived (a long method call, or a method call generating a closure with a reference to the bitmaps, or a member of a long-lived class) then set them to null once they're no longer needed.
It is not necessary to manually free the memory; GC.Collect() should be superfluous. As a rule of thumb, only use GC.Collect during benchmarking to get an indication of memory consumption and/or to start with a clean slate. Overall performance is generally decreased by calls do GC.Collect().
Forcing a GC without setting image and bitmap to null won't clean them up because they are still being referenced locally and are thus considered root references. This is nothing specifically to do with the WriteableBitmap and more a question of how GC works.
If you don't set them to null and don't force a garbage collection then they will be collected once the method exists and a GC occurs. That is recommended above forcing the collection yourself because you're probably hurting performance rather than helping it.

Resources