Print This Post

Download an image from the web and assign a stream to TPicture (for any image format)

I struggled with this myself when implementing the Pexels component. The problem is rather simple: You download an image from the web, end up with data in a stream and want to use a bitmap to use the image in your application. The tasks presents itself as rather complex due to the flexibility that Delphi offers in this regard. In defense of any other modern object-oriented programming language, they all pick a rather similar approach.

Repeating some Delphi basics. A bitmap is nothing more than a collection of pixels that has a width and a height. We can make changes to any pixel. The problem is that images from the web are not bitmaps. The images follow an encoding that is referred to as image format. It is possible to convert any image file into a TBitmap. We just need a TGraphic class that registers itself in TPicture. The whole deal is explained rather well in the comments in Vcl.Graphics.pas:

Let’s say you want to use a JPEG image in your Delphi project:

  • Make sure that JPEG is in the uses list as that registers itself in TPicture
  • Use TPicture.LoadFromFile  and TPicture.SaveToFile  to load from or save to JPEG images

The common problem asked on the web is: Where is TPicture.LoadFromStream  or TPicture.SaveToStream  ?

As explained in the comments from TPicture the whole system is based on file extensions. That means, if you have a data stream, you have to tell Delphi what kind of file it is supposed to load. Thus, you are forced back to use the TGraphic  class directly. The TPicture  loading mechanism cannot be used.

Getting back to the example loading an image from the web, we can use any of the many web components that Delphi offers. The result is stored in a TMemoryStream, for example. Of course, you can circumvent the whole problem storing the data you downloaded in a temporary file with the correct extension and you can then use the loading mechanism. But temporary files are such a hassle to create and clean up and you will see, they are not necessary.

The response stream is stored in the local variable lResponse . The URL of the web request is available as well and will be the deciding factor, which TGraphic  class to use to load the image. FPicture  is a field variable of type TPicture  that is to contain the loaded image.

If the URL ends with '.png'  we use TPNGImage to load the image from the stream, for '.jpg' and '.jpeg' we use TJPEGImage instead. For any more graphic extensions, add more classes. The amount of extensions is limited by the API you use. Considering the Pexels API it should all be JPEG images, but I already found a few PNG images and had to implement this mechanism.

Remember to add

  • PNGImage
  • JPEG

to your uses clause.

Some hints on memory management:  We assign the loaded image to FPicture . This means a copy is created in the process. Writing FPicture := lJPEG;  would be hazardous as we would have to handle the memory management of the image that was assigned before to TPicture  (before that assignment) and we would also never know when the lJPEG -reference needs to be freed. lJPEG  runs out of scope after our method is executed and this will most definitely lead to an unexpected access violation. Thus, using Assign  is the only solution. FPicture  has its ‘own’ image and we have to get rid of our local reference right away.

After this explanation, we will have to amend our code snippet as follows:

This code will make sure that our local references are freed properly. Unnecessary precaution? I will have to disagree. What if there is not enough memory for the image to be loaded from the stream? JPEG images can be huge when decompressed. What if the URL has been misnamed and the loading of the image fails with an exception? For these and many more reasons we need to wrap using try…finally!

Tags: , , , , , , ,