The TImageListEx component is a TImageList descendant that can use the images from another image list to generate disabled images, which can be used on toolbars and other user interface elements.
There are several benefits of the TImageListEx component:
It eliminates the need for creating disabled glyphs.
It eliminates the need for adding the disabled glyphs to an additional TImageList component at design time.
It can drastically reduce the size of the .dfm file and of the entire application, especially in large applications that use a lot of glyphs.
It's extremely fast, taking only milliseconds to disable all images in an image list, even when there are number of images.
It's extremely lightweight. (If you add it to an application that already uses the standard TImageList component, it won't increase the size of the executable at all, and if you add it to an application that doesn't use the standard TImageList component, the overhead is only 2 KB.)
To create the TImageListEx component, you have to derive it from the standard TImageList component and then create the DisableImage function for disabling an image and the AcquireDisabled procedure that uses the DisableImage function to disable all images from another image list.
The DisableImage function is not really complex. It first determines the "transparent" color by extracting the bottom-right pixel of the source image. Then it uses the "transparent" pixel to determine which pixels should be converted to grayscale and which should be copied. All pixels that match the "transparent" pixel are directly copied to the destination bitmap and all other pixels are first converted to grayscale and then copied to the destination image. Finally, it returns the "transparent" pixel as Result because we have to pass this value to the AddMasked method of TImageList component when adding images to the image list.
The following listing shows the entire DisableImage function.
Listing 28-4: Disabling an image
type TImageListEx = class(TImageList) private { Private declarations } protected { Protected declarations } function DisableImage(Source, Dest: TBitmap): TColor; end; ... function TImageListEx.DisableImage(Source, Dest: TBitmap): TColor; type TMyPixel = record Blue: Byte; Green: Byte; Red: Byte; end; PMyPixelArray = ^TMyPixelArray; TMyPixelArray = array[0..32767] of TMyPixel; var pSrc: PMyPixelArray; pDest: PMyPixelArray; x: Integer; y: Integer; gray: Integer; { these will hold the transparent color } tr: Integer; tg: Integer; tb: Integer; begin { first, make sure both are pf24bit } Source.PixelFormat := pf24bit; Dest.PixelFormat := pf24bit; { pixel at [W-1,H-1] in the Source is treated as "transparent" } pSrc := Source.ScanLine[Pred(Source.Height)]; with pSrc[Pred(Source.Width)] do begin tr := Red; tg := Green; tb := Blue; end; for y := 0 to Pred(Source.Height) do begin pSrc := Source.ScanLine[y]; pDest := Dest.ScanLine[y]; for x := 0 to Pred(Source.Width) do begin { if px <> transparent then grayscale it } if (pSrc[x].Red <> tr) or (pSrc[x].Green <> tg) or (pSrc[x].Blue <> tb) then begin gray := (pSrc[x].Red * 3 + pSrc[x].Blue * 4 + pSrc[x].Green * 2) div 9; pDest[x].Red := gray; pDest[x].Green := gray; pDest[x].Blue := gray; end else begin { if transparent then copy it } pDest[x].Red := pSrc[x].Red; pDest[x].Green := pSrc[x].Green; pDest[x].Blue := pSrc[x].Blue; end; // if pSrc end; // for x end; // for y { return the color used as transparent; we need to pass this to the AddMasked method when adding images to the image list } Result := RGB(tr, tg, tb); end;
The AcquireDisabled procedure accepts a source TCustomImageList object as the sole parameter and simply loops through its images, converting them to grayscale using the DisableImage function we created above.
To access a particular bitmap in a TImageList, you have to call the GetBitmap method, which accepts the image's index and a TBitmap where the GetBitmap method will store the requested image:
function GetBitmap(Index: Integer; Image: TBitmap): Boolean;
Finally, after you've acquired the original glyph from the source list and converted it to grayscale using the DisableImage function, you have to call the TImageList's AddMasked method to add the new glyph to the list. When calling the AddMasked method, you have to pass the glyph's transparent color as the MaskColor parameter to have the TImageList component generate the glyph's mask, which is used to draw the glyph transparently.
The following listing shows the AcquireDisabled procedure.
Listing 28-5: The AcquireDisabled procedure
procedure TImageListEx.AcquireDisabled(AList: TCustomImageList); var cnt: Integer; newImage: TBitmap; tempSource: TBitmap; transparent: TColor; begin { use AList's image size } Width := AList.Width; Height := AList.Height; { make sure there are no old images in the list } Clear; tempSource := TBitmap.Create; try newImage := TBitmap.Create; try for cnt := 0 to Pred(AList.Count) do begin { erase the old images because the GetBitmap method doesn't } tempSource.Assign(nil); newImage.Assign(nil); { resize the new image } newImage.Width := Width; newImage.Height := Height; { get image from the source image list } AList.GetBitmap(cnt, tempSource); { generate the new image } transparent := DisableImage(tempSource, newImage); { add the new image to the list } AddMasked(newImage, transparent); end; finally newImage.Free; end; finally tempSource.Free; end; end;
To use the TImageListEx component in an application, you have to do three things:
Remove an existing TImageList component that contains disabled glyphs.
Add the TImageListEx component to the Designer Surface and assign it to the appropriate Disabled properties of the components that used the original TImageList component.
Call the TImageListEx component's AcquireDisabled method in the main form's OnCreate event handler and pass the TImageList that contains normal glyphs to create appropriate disabled glyphs.
For instance, you can use the Delphi Text Editor example that we created earlier to test this component. First, remove the original disabled TImageList component and drop a TImageListEx component on the Designer Surface. Then, assign the TImageListEx component to the DisabledImages property of the main toolbar and write the following line in the OnCreate event handler of the main form:
procedure TMainForm.FormCreate(Sender: TObject); begin ImageListEx1.AcquireDisabled(Normal); end;
The TImageListEx will correctly create all disabled glyphs, except for the Delete button. The original Delete glyph cannot be successfully converted because this glyph, unlike almost all other GlyFX glyphs, treats the bottom-right pixel as a part of the image instead of as the "transparent" pixel. The following figure shows the original (erroneous) glyph and the updated glyph that can be successfully converted to grayscale by the TImageListEx component.
Figure 28-6: The troublesome Delete glyph
The toolbar in the following figure shows what the disabled glyphs generated by the TImageListEx component look like.
Figure 28-7: The final version of the Delphi Text Editor that uses the TImageListEx component
Finally, Figure 28-8 shows the impact of the TImageListEx component on the size of the Delphi Text Editor's executable. Although the Delphi Text Editor only had 10 disabled glyphs, the TImageListEx component managed to reduce the size of the executable by 17,408 bytes (17 KB).
Figure 28-8: The difference in the size of the executable after using the TImageListEx component