I'm loading bitmaps on one thread, and then later saving them to disk on another thread.
Loading on Thread A :
BitmapSource bitmapSource = null;
using (var stream = File.OpenRead(path))
{
bitmapSource = BitmapDecoder
.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad).Frames[0];
bitmapSource.Freeze();
}
// only available to Thread B at this point (i.e after loading is complete).
Saving on Thread B :
System.Diagnostics.Debug.Assert(bitmapSource.IsFrozen);
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmapSource)); <-- Exception here
...
99% of the time, this works fine. But every once in a while, I'll get an exception at the 'BitmapFrame.Create(bitmapSource)' point, with the following call stack :
at System.Windows.Threading.Dispatcher.VerifyAccess()
at System.Windows.Media.Imaging.BitmapDecoder.get_IsDownloading()
at System.Windows.Media.Imaging.BitmapFrameDecode.get_InternalMetadata()
at System.Windows.Media.Imaging.BitmapFrame.Create(BitmapSource source)
The calling thread cannot access this object because a different thread owns it.
I'm really not sure what is going on. From my understanding, as long as you freeze the bitmapsource, you can access it from another thread? I also have a debug assert to check it is frozen, which never gets triggered. And if I check all the properties of the bitmapsource object in the debugger, I see the following :
All properties are accessable apart from the IsDownloading one. I'm not doing any downloading. I'm loading the bitmap from disk, and it's not available to the second thread until its completed loading? so it's a bit of a mystery, at least to me.
I came across this :
WPF BitmapFrame and multiple threads
The solution appears to be to wrap the bitmap in a CachedBitmap
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(new CachedBitmap(x, BitmapCreateOptions.None, BitmapCacheOption.OnLoad)));
Ideally, you could use CheckAccess() to determine it this extra step is necessary rather than always doing it, but CheckAccess() always returns true for some reason.
Related
I would like to create a worker class that can retrieve laggy resources (eg from a database or over a network), and prepare them for later, zero-lag display on the UI. That worker class should not be bound to the UI or to the UI thread i.e. it should be able to run anywhere. When done, it's output will be a populated array of some image container class that the UI can work with easily.
My first thought was to use the BitmapImage class as the image container; however that inherits from the DependencyObject class, which will restrict access from any non-UI thread. As #Filip pointed out in my earlier form of this question, this is not inherently a problem, but it would restrict the generality of my worker class.
Is there a better Windows class that I can use as the basis for the image container, and then convert to BitmapImage on the UI thread at bind time?
Or a better way altogether?
Here is some pseudo code to indicate what I'm trying to do, using an array of URIs for the source images.
In the worker class
ImageContainerClass[] prepImages(Uri[] uriSet)
{
ImageContainerClass[] iccSet = new ImageContainerClass[uriSet.Length];
for (int i = 0; i < iccSet.Length; i++)
iccSet[i] = new ImageContainerClass.retrieveImage(uriSet[i]);
return iccSet;
}
On the UI thread (at some later time):
BitmapImage bmi = new BitmapImage();
var image = iccSet[i].getImage();
<<some operation to set the source of bmi to the image>>
someImage.Source = bmi;
I think loading images into BitmapImage happens on a background thread in the platform, so there shouldn't be a need for you to do it. You could probably use BitmapDecoder on a background thread if you needed to and then maybe push a pixel buffer into a WriteableBitmap, but you wouldn't gain much from it. If you really want to force all the code to a background thread then you can use DirectX interop and have a completely separate background render thread for your stuff.
OK, so here's a solution that appears to work.
The following code will run happily on a non-UI thread:
internal async Task<InMemoryRandomAccessStream> getImage(Uri uri)
{
try {
var httpClient = new HttpClient();
IBuffer iBuffer = await httpClient.GetBufferAsync(uri);
byte[] bytes = iBuffer.ToArray();
InMemoryRandomAccessStream ims = new InMemoryRandomAccessStream();
DataWriter dataWriter = new DataWriter(ims);
dataWriter.WriteBytes(bytes);
await dataWriter.StoreAsync();
ims.Seek(0);
return ims;
} catch (Exception e) { return null; }
}
Then, the following code may be executed on the UI thread when the image needs to be displayed:
BitmapImage bmi = new BitmapImage();
bmi.SetSource(ims);
someImage.Source = bmi;
I'm sure this can be cleaned up by people more familiar with the relevant frameworks, but hopefully this will help others. My thanks to #Filip for putting me on the right track.
I have the following code that gets called 4 times a second and updates the background of a grid. When i don't dispose of of the memory stream, the memory output usage slowly grows and falls. MemoryStream has a Dispose function, But if I call it even after i dispose the Source Bitmap, the background is just white.
Do i need to dispose the stream? And if I do, what am I doing wrong?
private void Viewer_OnUpdate(object self, Bitmap sourceBitmap, Int32Rect cropArea)
{
if (sourceBitmap == null)
{
return;
}
this.Dispatcher.BeginInvoke(DispatcherPriority.Render,
new Action(
() =>
{
MemoryStream stream = new MemoryStream();
sourceBitmap.Save(stream, ImageFormat.Bmp);
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
stream.Seek(0, SeekOrigin.Begin);
bitmapImage.StreamSource = stream;
bitmapImage.EndInit();
this.GridContainer.Background =
new ImageBrush(new CroppedBitmap(bitmapImage,cropArea));
sourceBitmap.Dispose();
}));
}
Note: i'm dispatching because the calling event is always from a Non-UI thread
From MSDN documentation for BitmapImage (emphasis is mine):
Set the CacheOption property to BitmapCacheOption.OnLoad if you wish
to close the stream after the BitmapImage is created. The default
OnDemand cache option retains access to the stream until the bitmap is
needed, and cleanup is handled by the garbage collector.
First, you discovered that if you dispose the memory stream, the bitmap is affected, which means your ImageBrush seems white. So - don't dispose the memory stream.
Second, and more importantly, the memory consumption pattern you're seeing - more and more memory, then a sudden fall - is normal. That's what happens when the garbage collector runs at its own discretion. It's not a problem at all.
So don't dispose of the memory stream, let the garbage collector do its job, and don't worry about it.
Look, maybe this answer will help you.
MemoryStream in Using Statement - Do I need to call close()
If you put your MemoryStream in a using statement, you will not need to Dispose, because it will do it automatically ;)
I hope this helps
I need to create a thumbnail of an image on WP8, and currently I'm facing difficulties. In a nutshell, I know the only one way of doing this, that is using classes System.Windows.Controls.Image, System.Windows.Media.Imaging.BitmapImage and System.Windows.Media.Imaging.WritableBitmap. I'm also trying to perform thumbnail creation on the threadpool, because it's a part of other bigger operation, which is running on a threadpool.
As you have probably already understood, I'm failing with invalid cross-thread access, even when I'm trying to create an instance of the above classes. That's a shame, really, because this thumbnail even is not going to be used in the UI, only saved to a file, and displayed from the file later on. My work has nothing to do with UI thread, and I'm still facing this limitations.
So is there some other way of creating the thumbnail from an image stream (I'm getting it from PhotoChooser task)? Maybe some other API, which doesn't require those UI-bound classes? Tried to bing it, even to google it, but with no luck.
Okay, I think I'll put my own answer here as well, since it shows things from a bit of different perspective. The answer by Justin Angel is okay, but there's a couple of issues with it:
It's not possible to have a reference to Dispatcher, when the code is deep in the Model layer and running on the background thread.
I need to return the thumbnail image from the method and use it later in the same synchronization context. Otherwise I'll have to change a lot of code around this method of creating thumbnail.
With this requirements in mind, here's my solution:
private WriteableBitmap CreateThumbnail(Stream stream, int width, int height, SynchronizationContext uiThread)
{
// This hack comes from the problem that classes like BitmapImage, WritableBitmap, Image used here could
// only be created or accessed from the UI thread. And now this code called from the threadpool. To avoid
// cross-thread access exceptions, I dispatch the code back to the UI thread, waiting for it to complete
// using the Monitor and a lock object, and then return the value from the method. Quite hacky, but the only
// way to make this work currently. It's quite stupid that MS didn't provide any classes to do image
// processing on the non-UI threads.
WriteableBitmap result = null;
var waitHandle = new object();
lock (waitHandle)
{
uiThread.Post(_ =>
{
lock (waitHandle)
{
var bi = new BitmapImage();
bi.SetSource(stream);
int w, h;
double ws = (double)width / bi.PixelWidth;
double hs = (double)height / bi.PixelHeight;
double scale = (ws > hs) ? ws : hs;
w = (int)(bi.PixelWidth * scale);
h = (int)(bi.PixelHeight * scale);
var im = new Image();
im.Stretch = Stretch.UniformToFill;
im.Source = bi;
result = new WriteableBitmap(width, height);
var tr = new CompositeTransform();
tr.CenterX = (ws > hs) ? 0 : (width - w) / 2;
tr.CenterY = (ws < hs) ? 0 : (height - h) / 2;
tr.ScaleX = scale;
tr.ScaleY = scale;
result.Render(im, tr);
result.Invalidate();
Monitor.Pulse(waitHandle);
}
}, null);
Monitor.Wait(waitHandle);
}
return result;
}
I'm capturing UI thread's SynchronizationContext while I'm still in the UI thread (in View Model), and passing it further, and then I'm using closures to capture local variables, so that they're available for the callback which runs on UI thread. I'm also using lock and Monitor to synchronize these two threads and wait until the image is ready.
I'll accept my or Justin Angel's answer based on votes, if any. :)
EDIT: You can get an instance of Dispatcher's SynchronizationContext through System.Threading.SynchronizationContext.Current while you're on UI thread (in an button click handler, for example). Like this:
private async void CreateThumbnailButton_Clicked(object sender, EventArgs args)
{
SynchronizationContext uiThread = SynchronizationContext.Current;
var result = await Task.Factory.StartNew<WriteableBitmap>(() =>
{
Stream img = GetOriginalImage();// get the original image through a long synchronous operation
return CreateThumbnail(img, 163, 163, uiThread);
});
await SaveThumbnailAsync(result);
}
Yeah, using WriteableBitmap requires access to the UI string. You might have to schedule the work on the UI thread as part of your workflow using the Dispatcher class.
The only other thing I can think off is saving the image to the Phone's MediaLibrary and using the Picture.GetThumbnail() method to get a very low-res thumbnail. It might or might not work without access to the UI thread. Also, once you add pictures to the user's MediaLibrary, you can't delete those so be careful not to spam those folders.
I have the following piece of code that runs in a loop.
public void Test(Bitmap bmp)
{
FormatConvertedBitmap fBitmapSource = new FormatConvertedBitmap();
PngBitmapEncoder pngBitmapEncoder = new PngBitmapEncoder();
BitmapImage bi = new BitmapImage();
using (MemoryStream ms = new MemoryStream())
{
bmp.Save(ms, ImageFormat.Png);
bmp.Dispose();
bmp = null;
bi.BeginInit();
bi.StreamSource = ms;
bi.EndInit();
BitmapPalette pallete = new BitmapPalette(bi, 256);
...
Last line
BitmapPalette pallete = new BitmapPalette(bi, 256);
Sometimes throws the following exception
Insufficient memory to continue the execution of the program.at System.Windows.Media.Imaging.BitmapPalette..ctor(BitmapSource bitmapSource, Int32 maxColorCount)
Any ideas ? I clearly have enough memory to continue execution.
There are other sources of OutOfMemoryException in a managed program that don't have anything to do with running out of managed memory. The exception is also raised when it translates error codes returned by legacy native code. Like the E_OUTOFMEMORY error that can be returned by COM method calls. And relevant in your case, by GDI+. Which has only 20 distinct error codes to indicate failure, you'll find them documented in this answer. One of them is OutOfMemory.
Which can mean more than one thing. Running out of unmanaged memory, the kind used by GDI+ to store bitmap pixels is certainly a possibility. It can also mean that your process has run out of available GDI object handles, Windows imposes a handle quota of 10,000 GDI handles. Which is an enormous number btw, exceeding that quota almost always indicates a bug in the code. A handle leak. Which in the case of a managed program is almost always caused by forgetting to use the Image.Dispose() method and not having the garbage collector run often enough to allow the finalizer to release handles.
Sadly it can even be triggered by corrupted bitmap data, not likely in your case since you bomb on allocating the palette. Which indicates a handle leak, which ought to be readily visible in Taskmgr.exe, Processes tab. View + Select columns and tick GDI Objects. Keep an eye on the displayed value for your process while you test it. A steadily increasing number spells trouble, the show is over when it reaches 10,000. Also look at the "Commit size" column, that can show you trouble with consuming too much unmanaged memory.
I write a code that read a png image from file and show with control.
I want read image from stream and set
control.BackgroundImage = Image.FromStream(memStream);
but when use this code , occur "out of memory" exception. but when use
control.Image = Image.FromStream(memStream);
or
control.BackgroundImage = Image.FromFile(fileSource);
, that is work.
image file size is 5KB.
if (System.IO.File.Exists(imgSource))
{
using (FileStream localFileStream = new FileStream(imgSource, FileMode.Open))
{
using (MemoryStream memStream = new MemoryStream())
{
int bytesRead;
byte[] buffer = new byte[1024];
while ((bytesRead = localFileStream.Read(buffer, 0, buffer.Length)) > 0)
{
memStream.Write(buffer, 0, bytesRead);
}
retIMG = Image.FromStream(memStream);
pictureBox1.Image = retIMG; // is work
label1.Image = retIMG; // is work
button1.Image = retIMG; // is work
button1.BackgroundImage = retIMG; // don't work
groupBox1.BackgroundImage = retIMG; // don't work
panel1.BackgroundImage = retIMG; // don't work
}
}
}
I think a bug in .net framework.
please you help me?
Read the remarks on Image.FromStream on MSDN:
You must keep the stream open for the lifetime of the Image.
So if you remove the using around the creation of your MemoryStream your code works fine.
Of course you should preferrably dispose the MemoryStream once you no longer need the Image you created, although there is likely no harm in this case in not calling Dispose() and leaving it up to the GC to collect it once unused.
The fact that it seems to work with some of your code is likely pure luck and should not be considered a working solution. Always read the documentation to find out about quirks like this.
Giving some background to add to DeCaf's correct answer. GDI+ tries very hard to avoid copying the pixels of a bitmap. That's expensive, bitmaps taking dozens of megabytes is not unusual. When you load a bitmap from a file with the Bitmap constructor or Image.FromFile() then GDI+ creates a memory-mapped file. The pixels are paged-in on demand, only when needed. Very efficient but it puts a lock on the file. Clearly you were trying to avoid that in lock in this code.
You indeed avoid that lock by loading the bytes into memory yourself with a MemoryStream. But the same principle still applies, GDI+ still doesn't copy the pixels and only reads from the stream when it needs to. This goes wrong when you Dispose() the stream. Very hard to diagnose because the exception occurs later, typically when the bitmap needs to be drawn. It bombs in the painting code, you don't have any code to look at but Application.Run(). With a crappy exception message, GDI+ only has a handful of error codes. You are not out of memory, it only looks that way to GDI+, it cannot otherwise figure out why the stream suddenly isn't readable anymore.
At least part of the problem is caused by the very awkward implementation of MemoryStream.Dispose(). Dispose is meant to release unmanaged resources. A memory stream doesn't have any, it only owns memory. That's already taken care of by the garbage collector. Unfortunately they implemented it anyway. Not by actually disposing anything, since there's nothing to dispose, but by marking the MemoryStream unreadable. Which triggers the error in GDI+ when it tries to read while drawing the bitmap.
So simply remove the using statement to avoid disposing the MemoryStream to solve your problem. And don't fret about disposing it later when the bitmap is no longer in use. There's nothing to dispose, the garbage collector automatically frees the memory.
Two things which together resolved this intermittent issue, which has nothing to do with image size.
First, ensure that the image is in RGB mode and definitely not CMYK mode. In our experience, the RGB rendering is actually larger.
Second, erase (and dispose of if possible) any previous image in the image container before loading the new image, such as
Control.Image = Nothing