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.
Related
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.
I have a scenario of loading bitmap to a ImageView from a byteArray. I use the following code to achieve this.
Inside a class that extends ImageView
this.Post(() =>
{
using (customImage = BitmapFactory.DecodeByteArray(byteArray, 0, byteArray.Count(), options))
{
using (var renderBitemap = customImage.Copy(Bitmap.Config.Argb4444, true))
{
customImage?.Recycle();
options.Dispose();
options = null;
if (m_pdfParent.undisposedPageImages.Contains(m_pageIndex))
{
this.SetImageBitmap(renderBitemap);
}
stream = null;
}
});
As you can see the bitmap conversion also occurs in the UI thread (It is a heavy process, right?), this blocks the UI.
1) On using only the SetimageBitmap method in the UI thread => I get an object disposed exception.
2) On removing the this.Post and running everything in the background thread => I get change an exception that only the thread that created a view can alter the View.
Is there a way to improve this code snippet?(Setting bitmap from byteArray to an ImageView without blocking the UI thread)
Since I was not able to find the actual code that I use usually I will just be guiding you through the process which should be more than enough.
As you can see the bitmap conversion also occurs in the UI thread (It is a heavy process, right?), this blocks the UI.
This is true Bitmaps, in general, are really heavy and can cause OOM without any doubts and hence handling them should be your utmost priority!, To handle bitmaps you need to do the following things that will optimize them to the maximum and really fast as well without blocking your UI.
Use the async methods to actually convert the bytes to bitmap using the following method:
var bmp = await BitmapFactory.DecodeByteArrayAsync(byteArray, 0, byteArray.Count(), options);
This will not do the decoding on the UI thread and not freeze your application, Also do not add this into your using state as that would dispose of it while it is still being referenced by your Image which will, in turn, throw an exception.
Also, the options do not need to be disposed of they are not heavy at all and can be GC'd when the time is right just leave it as you can actually even reuse this later if needed!
Also, the Bitmap's recycle method recycles your bitmap ergo making it useless and REMEMBER if a UI element is using this bitmap you will be getting an object was disposed exception and the StackTrace sometimes will not be clear what caused it i.e. confusion for no apparent reason. The best way to handle this is only clean your Bitmaps when you know they are useless now i.e. on page level when you go to another page not when you are still using it, Also your page reappearing needs to be handled i.e. image should be reassigned so that the above exception is not thrown again!
Also, make sure you optimize your image bitmap to the size of the View holder i.e. make sure that your image is only supposed to be in which you can do with the below method
You can use AsyncTask to load and show image, AsyncTask do the task in another thread but will return to the UI thread once the task is done, code like this:
class BitmapWorkerTask : AsyncTask<byte[], int, Bitmap>
{
private MainActivity mainActivity;
public BitmapWorkerTask(MainActivity mainActivity)
{
this.mainActivity = mainActivity;
}
protected override Bitmap RunInBackground(params byte[][] #params)
{
return null;
}
protected override Java.Lang.Object DoInBackground(params Java.Lang.Object[] native_parms)
{
base.DoInBackground(native_parms);
BitmapFactory.Options options = new BitmapFactory.Options();
options.InJustDecodeBounds = false;
Thread.Sleep(4000);
return BitmapFactory.DecodeByteArray(mainActivity.imageData, 0, mainActivity.imageData.Length, options);
}
protected override void OnPostExecute(Bitmap result)
{
base.OnPostExecute(result);
mainActivity.imageView.SetImageBitmap(result);
}
}
and here is something in MainActivity:
ImageView imageView;
byte[] imageData;
BitmapWorkerTask workerTask;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.activity_main);
//initialize your byte array imageData here
imageView = (ImageView)FindViewById(Resource.Id.image);
workerTask = new BitmapWorkerTask(this);
workerTask.Execute();
}
Just in case if you don't need to load the exact same size of the big picture, refer to this page for loading bitmap efficiently
Async & Await is the solution to your problem. AsyncAwait Features is introduced only to avoid problems related to Blocking UI due to the long-running process.
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 a Winform with 4 PictureBox controls, each control will contain a diferent image. The process is:
An event x is raised, the eventargs from this event, contain the filenames of each image (4), an so on (file exists etc..). Then, I have to update the UI.
Commonly I use Invoke:
Invoke((ThreadStart)delegate()
{
picBig.Image = new Bitmap(strImageBig);
picLittle1.Image = new Bitmap(saLittle[0]);
picLittle2.Image = new Bitmap(saLittle[1]);
picLittle3.Image = new Bitmap(saLittle[2]);
});
// saLittle[] is a string array, contains, filenames: "image1.jpg"
But when this executes, the form freezes for a little time, about 500ms, I know it's a small interval but it's noticeable, then the app continues normally.
I was trying to find out the reason of that 'UI freeze', then, after research, I found BeginInvoke. Now my code looks like this:
BeginInvoke((MethodInvoker)delegate
{
picBig.Image = new Bitmap(strImageBig);
picLittle1.Image = new Bitmap(saLittle[0]);
picLittle2.Image = new Bitmap(saLittle[1]);
picLittle3.Image = new Bitmap(saLittle[2]);
});
This is a little faster. But the UI is still freezing for 200~300ms.
In the articles I've read, they say that BeginInvoke is a better way than Invoke.
The code is working OK, there is no problem with logic or anything else. I just want to know why this happens. I don't want to leave this doubt unclear. The project is already finished. Hope this be useful for someone else.
Maybe this is not the correct approach. I know there are many ways to update the UI from a background thread, but is there another way to make an update faster?
Or, do you think, the image loading is the reason? Is there another way to do a faster loading of images?
Thanks in advance.
This is because you're actually loading your images from disk on the UI thread along with setting the contents of the control. Calling the Bitmap constructor with a file path will go to the hard drive and load the image into memory.
Invoke and BeginInvoke will run the delegate that you provide on the thread that the control was created on, which is most likely going to be the UI thread.
...but is there another way to make an update faster?
Load the images on your background thread and, when they're actually loaded, invoke and set the images into the controls.
var big = new Bitmap(strImageBig);
var little1 = new Bitmap(saLittle[0]);
var little2 = new Bitmap(saLittle[1]);
var little3 = new Bitmap(saLittle[2]);
Invoke((ThreadStart)delegate()
{
picBig.Image = big;
picLittle1.Image = little1;
picLittle2.Image = little2;
picLittle3.Image = little3;
});
But when this executes, the form freezes for a little time, about 500ms, I know it's a small interval but it's noticeable, then the app continues normally.
Eventually, the UI thread needs to actually update the images. When the images are generated and updated on the UI thread, this will cause a (short) delay.
In a previous question I asked how to improve a bit of code. It was said that I should move it to a new thread. I'd never thought about it before so it seems like a great idea to me. So this morning I went ahead and reused a bit of code I already have for processing emails and updated the way I handle image uploads into my site.
So is this a good way to start a new thread and process the images? Is there even a need to lock it like I am?
private static object dummy = new object();
public static void Save(int nProjId, byte[] bData)
{
var worker = new ThreadStart(() => ProcessImage(nProjId,bData));
var thread = new Thread(worker);
thread.Start();
}
private static void ProcessImage(int nProjId, byte[] bData)
{
lock (dummy)
{
try
{
byte[] xlargeImage = Thumbs.ResizeImageFile(bData, 700);
byte[] largeImage = Thumbs.ResizeImageFile(bData, 500);
//improved based on previous question to use the already reduced image
byte[] mediumImage = Thumbs.ResizeImageFile(xlargeImage, 200);
byte[] smallImage = Thumbs.ResizeImageFile(xlargeImage, 100);
//existing code to actually save the images
MyGlobals.GetDataAccessComponent().File_Save(
ConfigurationManager.ConnectionStrings["ImgStore"],
nProjId,
xlargeImage,
largeImage,
mediumImage,
smallImage);
}
catch (Exception)
{
//ToDo: add error handleing
{ }
throw;
}
}
}
Oh and the images now upload and process nearly instantly (locally) so it's a HUGE help so far. I just want to make sure it's the best way to do it. Oh and I'm using a dual core machine running Server 2008 with 6gb or ram, so I have a little wiggle room to make it faster or use more threads.
I would suggest using a ThreadPool class, specifically because it will re-use a thread for you rather than you creating a new thread each time, which is a little bit more intensive.
Check out the QueueUserWorkItem method.
Also if you are not using a static resource to write to (I am not sure what exactly File_Save does) I dont think there is a need for your lock. However if you are using a static resource then you should lock just the code that is using it.
Is this for any production code? Or just a sample ? If it is not for production code, apart from using ThreadPool, you can use TPL from .NET4.0 . MS recommends using TPL instead of ThreadPool.