I have created my own ImageList that maps its images to string names:
unit NamedImageList;
interface
uses
  System.Classes, System.Generics.Collections, System.SysUtils, Vcl.Controls,
  Vcl.ImgList, Vcl.Graphics, Vcl.Imaging.pngimage, Winapi.Windows;
type
  TNamedImageList = class(Vcl.Controls.TImageList)
  private
    FNameIndexMap: TDictionary<string, Integer>;
    function GetImageName(Index: Integer): string;
    procedure SetImageName(Index: Integer; const Value: string);
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    function Add(Bitmap: Vcl.Graphics.TBitmap; const ImageName: string): Integer; reintroduce; overload;
    function AddPNG(PNG: TPngImage; const ImageName: string): Integer;
    function IndexOfName(const ImageName: string): Integer;
    procedure SaveToFile(const FileName: string);
    procedure LoadFromFile(const FileName: string);
    property ImageName[Index: Integer]: string read GetImageName write SetImageName;
  end;
procedure Register;
implementation
procedure Register;
begin
  RegisterComponents('PA-Soft', [TNamedImageList]);
end;
{ TNamedImageList }
constructor TNamedImageList.Create(AOwner: TComponent);
begin
  inherited;
  FNameIndexMap := TDictionary<string, Integer>.Create;
  // Set properties for best quality with transparency
  ColorDepth := cd32Bit;
  DrawingStyle := dsTransparent;
end;
destructor TNamedImageList.Destroy;
begin
  FNameIndexMap.Free;
  inherited;
end;
function TNamedImageList.Add(Bitmap: Vcl.Graphics.TBitmap; const ImageName: string): Integer;
begin
  Result := inherited Add(Bitmap, nil);
  if Result >= 0 then
  begin
    if FNameIndexMap.ContainsKey(ImageName) then
      FNameIndexMap[ImageName] := Result
    else
      FNameIndexMap.Add(ImageName, Result);
  end;
end;
function TNamedImageList.AddPNG(PNG: TPngImage; const ImageName: string): Integer;
var
  Bitmap: Vcl.Graphics.TBitmap;
  x, y: Integer;
  BmpLine: PRGBQuad;
  AlphaLine: pByteArray;
  PngColor: TColor;
  R, G, B: Byte;
begin
  Bitmap := Vcl.Graphics.TBitmap.Create;
  try
    Bitmap.PixelFormat := pf32bit;
    Bitmap.AlphaFormat := afDefined;
    Bitmap.SetSize(PNG.Width, PNG.Height);
    // Manually copy pixels with alpha channel
    for y := 0 to PNG.Height - 1 do
    begin
      BmpLine := Bitmap.ScanLine[y];
      for x := 0 to PNG.Width - 1 do
      begin
        // Get RGB from PNG
        PngColor := PNG.Pixels[x, y];
        R := GetRValue(PngColor);
        G := GetGValue(PngColor);
        B := GetBValue(PngColor);
        BmpLine^.rgbRed := R;
        BmpLine^.rgbGreen := G;
        BmpLine^.rgbBlue := B;
        // Get Alpha from PNG
        if Assigned(PNG.AlphaScanline[y]) then
        begin
          AlphaLine := PNG.AlphaScanline[y];
          BmpLine^.rgbReserved := AlphaLine[x];
        end
        else
          BmpLine^.rgbReserved := 255; // Fully opaque if no alpha channel
        Inc(BmpLine);
      end;
    end;
    Result := inherited Add(Bitmap, nil);
    if Result >= 0 then
    begin
      if FNameIndexMap.ContainsKey(ImageName) then
        FNameIndexMap[ImageName] := Result
      else
        FNameIndexMap.Add(ImageName, Result);
    end;
  finally
    Bitmap.Free;
  end;
end;
function TNamedImageList.GetImageName(Index: Integer): string;
var
  Pair: TPair<string, Integer>;
begin
  Result := '';
  for Pair in FNameIndexMap do
    if Pair.Value = Index then
    begin
      Result := Pair.Key;
      Break;
    end;
end;
procedure TNamedImageList.SetImageName(Index: Integer; const Value: string);
var
  OldName: string;
begin
  OldName := GetImageName(Index);
  if OldName <> '' then
    FNameIndexMap.Remove(OldName);
  if Value <> '' then
    FNameIndexMap.AddOrSetValue(Value, Index);
end;
function TNamedImageList.IndexOfName(const ImageName: string): Integer;
begin
  if not FNameIndexMap.TryGetValue(ImageName, Result) then
    Result := -1;
end;
procedure TNamedImageList.SaveToFile(const FileName: string);
var
  FS: TFileStream;
  MS: TMemoryStream;
  PNG: TPngImage;
  TempBitmap: Vcl.Graphics.TBitmap;
  I, StrLen, DataSize: Integer;
  Name: string;
  x, y: Integer;
  BmpLine: PRGBQuad;
  AlphaLine: pByteArray;
begin
  FS := TFileStream.Create(FileName, fmCreate);
  try
    // Write count
    I := Count;
    FS.WriteBuffer(I, SizeOf(Integer));
    TempBitmap := Vcl.Graphics.TBitmap.Create;
    PNG := TPngImage.Create;
    MS := TMemoryStream.Create;
    try
      for I := 0 to Count - 1 do
      begin
        // Write name (length-prefixed)
        Name := GetImageName(I);
        StrLen := Length(Name);
        FS.WriteBuffer(StrLen, SizeOf(Integer));
        if StrLen > 0 then
          FS.WriteBuffer(PChar(Name)^, StrLen * SizeOf(Char));
        // Get bitmap from ImageList
        TempBitmap.PixelFormat := pf32bit;
        TempBitmap.AlphaFormat := afDefined;
        GetBitmap(I, TempBitmap);
        // Convert bitmap to PNG with alpha channel
        PNG.Assign(TempBitmap);
        // Ensure PNG has alpha channel
        if not Assigned(PNG.AlphaScanline[0]) then
          PNG.CreateAlpha;
        // Copy alpha channel from bitmap to PNG
        for y := 0 to TempBitmap.Height - 1 do
        begin
          BmpLine := TempBitmap.ScanLine[y];
          AlphaLine := PNG.AlphaScanline[y];
          for x := 0 to TempBitmap.Width - 1 do
          begin
            AlphaLine[x] := BmpLine^.rgbReserved;
            Inc(BmpLine);
          end;
        end;
        MS.Clear;
        PNG.SaveToStream(MS);
        DataSize := MS.Size;
        FS.WriteBuffer(DataSize, SizeOf(Integer));
        if DataSize > 0 then
          FS.CopyFrom(MS, 0);
      end;
    finally
      MS.Free;
      PNG.Free;
      TempBitmap.Free;
    end;
  finally
    FS.Free;
  end;
end;
procedure TNamedImageList.LoadFromFile(const FileName: string);
var
  FS: TFileStream;
  MS: TMemoryStream;
  PNG: TPngImage;
  I, Count, StrLen, DataSize: Integer;
  Name: string;
begin
  if not FileExists(FileName) then
    raise EFileNotFoundException.Create('File not found: ' + FileName);
  Clear;
  FNameIndexMap.Clear;
  FS := TFileStream.Create(FileName, fmOpenRead);
  try
    // Read count
    FS.ReadBuffer(Count, SizeOf(Integer));
    PNG := TPngImage.Create;
    MS := TMemoryStream.Create;
    try
      for I := 0 to Count - 1 do
      begin
        // Read name (length-prefixed)
        FS.ReadBuffer(StrLen, SizeOf(Integer));
        SetLength(Name, StrLen);
        if StrLen > 0 then
          FS.ReadBuffer(PChar(Name)^, StrLen * SizeOf(Char));
        // Read PNG data
        FS.ReadBuffer(DataSize, SizeOf(Integer));
        MS.Clear;
        if DataSize > 0 then
        begin
          MS.CopyFrom(FS, DataSize);
          MS.Position := 0;
          PNG.LoadFromStream(MS);
          // Add PNG with proper alpha channel handling
          AddPNG(PNG, Name);
        end;
      end;
    finally
      MS.Free;
      PNG.Free;
    end;
  finally
    FS.Free;
  end;
end;
end.
But after saving and reloading the ImageList, and trying to extract a single image, I only get a black square:
NamedImageListFavIcons.LoadFromFile('FavIconsNamedImageList.dat');
  CodeSite.Send('NamedImageListFavIcons.Count', NamedImageListFavIcons.Count); // 127
  var GoogleComIndex := NamedImageListFavIcons.IndexOfName('google.com');
  CodeSite.Send('GoogleComIndex', GoogleComIndex); // 7
  var ThisBitmap := TBitmap.Create;
  try
    NamedImageListFavIcons.GetBitmap(GoogleComIndex, ThisBitmap);
    CodeSite.Send('google.com icon', ThisBitmap);
  finally
    ThisBitmap.Free;
  end;
Do you know how to improve/optimize this with ImageEn code?
If you get this to work, I'll donate the code to ImageEn!