Treating a TImage’s loaded JPEG as a Bitmap in Delphi

One problem with the TImage component is that it doesn’t natively support JPegs. JPeg just wasn’t as popular a format back in the early to mid nineties when the Delphi was first developed. Well, today that is a different story. Starting with Delphi 3 , if memory serves (I didn’t every use D2), the TJPegImage component was introduced and the TImage could be extended to allow JPegs to be loaded. Today’s TImage component can load a JPeg straight from the LoadFromFile command. However, you still cannot directly manipulated it as easily as you can a BitMap. This is clear if you look at the structure of a TImage’s Picture property.

A TPicture has a BitMap property and a MetaFile property and a Graphic property that stores everything that isn’t a BitMap or a metafile.

Yet you’ll quickly see that very few routines and example on the web deal with the Graphic property. They can’t! The structure of the information in the Graphic varies depending upon the type of image.

So, the trick of dealing with JPegs, or anything else like TIFFs, GIFS or Targa’s (oh my), is to get their data uncompressed and de-interlaced and back over to the bitmap property. A little known fact is that this is exactly what Windows has to do any time you choose a JPEG as a background. Windows will load the JPEG save it to a BMP and write the name of that BMP to the registry and then display that file instead of the original JPEG. So, we all should quit blaming Borland (DevCo) for a klunky control, they are doing it the right low level way. And that’s, after all, what we like about Delphi right?

So, what we must do is once we allow a user to load a file, we will check to see if the type is a TJPegImage. If it is, then we create a TJPegImage component. We then assign the JPegImage to the BitMap property and we are done! You can now access the image data in the BitMap.

In Delphi, that looks like this:
[delphi]
SourceImage.Picture.LoadFromFile(FullFileName);
if (SourceImage.Picture.Graphic is TJPegImage)
then begin
JpegImage := TJpegImage.Create;
try
JpegImage.Assign(SourceImage.Picture.Graphic);
SourceImage.Picture.Bitmap.Assign(JpegImage);
finally
end;
FreeAndNil(JpegImage);
end;
[/delphi]

Assign is a wonderful little black box function built into many objects. Assign is one of the neatest functions in Delphi. It basically accepts other types of objects as a parameter, and converts as much of that type of object as possible.

Let’s go abstract and say you have two object types TCar and TSuitCase.
C: TCar
S: TSuitcase

Obviously can’t say S := C; or C := S; because they are of different types.
However you can say
C.Assign(S)
and inside the TCar’s Assign method have a statement that looks like:

[delphi]

if (Source is TSuitcase)
then Self.Trunk.Contents := Self.Trunk.Contents + TSuitcase(Source).Contents
else if (Source is TGasolinePump)
then …

[/delphi]

OK, that sounds pretty non-exciting, but it gets better! What if the type of object is unknown? A the TCar routine may know how to assign a TFordPerfect to itself because a TFordPerfect inherits from TCar, but a TCar is NOT going to know how to assign a TFordPrefect to itself now is it? That’s where AssignTo comes in…
[delphi]

if (Source is TSuitcase)
then Self.Trunk.Contents := Self.Trunk.Contents + TSuitcase(Source).Contents
else if (Source is TGasolinePump)
then Self.Tank.Contents := Self.Tank.Contents + TGasolinePump(Source).Gallons[1]
else Source.AssignTo(Self);

[/delphi]

AssignTo goes exactly the OPPOSITE direction. The person writing the new component than then create a routine that will allow his new component to the most common components that already exist. So following our example, TFordPrefect’s AssignTo routine might include something like this:

[delphi]

if (Dest is TCar)
then TCar(Dest).PassengerCompartment := TCar(Dest).PassengerCompartment + TPassenger(Self);

.
[/delphi]

Or in the case of a TJPegImage, the AssignTo method checks to the see if the Dest var is a TBitmap, and if it is, it goes through a long decompression and conversion routine to finally produce a true Device Independant bit map and write those raw bits into the Dest.

And that my friends is how our TImage component, that knows nothing about JPEG to Bitmap conversions gets the job done. It asks the TJPegImage AssignTo method to do all if its work for it.

You can now use the Picture.BitMap image as normal. The neat thing is that you can put those routines in your FormCreate method. So, TImages can be made to contain JPEGs at design time, making your EXE significantly smaller. And then it converts them, if appropriate, to BMPs at Runtime. It’s nice to save 600,000 of image space at next to no cost.

Kinda nifty eh?

(BTW if you are really ambitious, I’m sure you could write that routine above so that it checks to make sure the graphic isn’t a BMP, WMF or empty and then uses the class info routines to request the type of object and instationate a TPersistent variable to contain an object of that type. Then your routine can handle as a bitmap ANY image type you’ve extended your TImage to contain. More on extending TImages later…)