5-Minute-Snack: Loading big images into TImage without flicker
Loading images into a TImage component is easy. We do this during design-time most of the time. In the other cases it is mostly a call to LoadFromFile and we are done.
Recently, I had the task to display several images with a timer delay. Again, very simple to implement, but if you are not careful it looks hideous. This was the piece of code I implemented in the method that was triggered whenever the next image was supposed to be shown:
myImageComponent.LoadFromFile( TImageProvider.GetNextImageFilename );
The image provider returns the filename of the next image. One would think that he last image being loaded is being shown until the next image is being displayed. Sadly, the VCL handles this differently – for good reasons, I guess.
As soon as that line is invoked, the image displayed is being removed and you end up seeing an empty space where the image has been before. Depending on the size of the image file you load that might be a significant amount of time. Even if the image has a small file size, there will always be a flicker.
A simple trick helps. Sometimes more code less compact is actually better:
var lTempStream : TMemoryStream // .... begin lTempStream := TMemoryStream.Create; try lTempStream.LoadFromFile( TImageProvider.GetNextImageFilename ); lTempStream.Position := 0; myImageComponent.Picture.LoadFromStream( lTempStream ); finally lTempStream.Free; end; //... end;
In this alternative approach, we load the image into memory first. That means the delay happens when the image is still visible. Furthermore, the assignment is instantaneous, so there will be no flicker.
Subtle difference in code, big difference in the presentation of your app.
Seems like it would be even better to load into an image object and assign that instead. That way decoding is also taken out of the equation.
Extra points for doing that in a background thread, but you do have to manage overlapping requests (pretty simple to do) – make the main interface even smoother.
Loading the next graphics file to be displayed into an initially invisible image object and making that one visible after loading and decoding is completed, that’s pretty much what I am doing in my Total Commander lister plugin ImgView (http://www.fh-rosenheim.de/~diegelmann/index_English.htm#ImgView). That works fine even when browsing through an entire folder containing JPG files in the Total Commander quick view mode (invoked by a CTRL+Q key press).
Hey great work around!
Hi and thanks for your code.
I’m switching between images of various size, using C++ Builder XE3, and TImage. I tried your suggestion above, but still getting considerable flicker. In my case, I see no difference in fact.
Also, for the image to actually be updated, I need to call
TImage->Repaint(), Refresh or Invalidate(), and that is where I see the actual flicker.
Any workaround for that?
Cheers,
tk
With regard to the size you should make sure that the image component is always the same size, i.e. AutoSize should be False. Then the fitting and stretching will give you a performance hit in case you use them. You should pre-process the images if you need a different size. The component is not optimized for that either. As soon as you assign a new reference or load a new image it will be repainted. May be you could look into the animation and graphic abilities of FireMonkey for your requirement?