delphitiff

Delphi: How to display an image by image in Timage component


I have an image file (.tiff) which has 4 images. I can not view or display these 4 images in a TImage. The TImage component display only the first frame.

how to display an image by image?


Solution

  • VCL supports tif images through Windows Imaging Component which is encapsulated by a TWICImage. However, although trivial, VCL has left out frame support of WIC (which is the term MS documentation uses to refer to multiple images in an image).

    Below quote is from 'Vcl.Graphics.pas' of XE2.

    procedure TWICImage.LoadFromStream(Stream: TStream);
    var
      ..
      BitmapDecoder: IWICBitmapDecoder;
      ...
    begin
      ...
      WicCheck(BitmapDecoder.GetFrame(0, LBitmapFrame));
      ...
    end;
    

    I quoted only a single line which immediately displays the problem. The 'decoder' is able to provide total frame count information and to retrieve any single one of them. But, as coded, only the first one is ever used.

    It is still possible to use TWICImage itself to retrieve a frame and then assign it to the picture of an TImage. Below is my attempt of doing so, it essentially duplicates the code in TWICImage.LoadFromStream, with the difference that only the second frame is ever used :). Anyway, it should be easy to modularize to be able to get frame count and display the one required.

    type
      TForm1 = class(TForm)
        Image1: TImage;
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
        procedure FormDestroy(Sender: TObject);
        procedure FormCreate(Sender: TObject);
      private
        WICImage: TWICImage;
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    uses
      activex, wincodec, consts;
    
    {$R *.dfm}
    
    procedure TForm1.Button1Click(Sender: TObject);
    
      procedure Check(Result: HRESULT);
      begin
        if Failed(Result) then
          raise EInvalidGraphic.Create(SInvalidImage);
      end;
    
    var
      fs: TFileStream;
      Adapter: IStream;
      Decoder: IWICBitmapDecoder;
      Frame: IWICBitmapFrameDecode;
      WICBmp: IWICBitmap;
      Width, Height: DWORD;
    begin
      fs := TFileStream.Create('....tif', fmShareDenyWrite);
      try
        Adapter := TStreamAdapter.Create(fs);
        Check(WICImage.ImagingFactory.CreateDecoderFromStream(Adapter,
            GUID_ContainerFormatTiff, WICDecodeMetadataCacheOnDemand, Decoder));
        Check(Decoder.GetFrame(1, Frame));
        Check(WICImage.ImagingFactory.CreateBitmapFromSource(Frame,
            WICBitmapCacheOnLoad, WICBmp));
        Check(WICBmp.GetSize(Width, Height));
        Image1.Width := Width;
        Image1.Height := Height;
        WICImage.Handle := WICBmp;
        Image1.Picture.Bitmap.Assign(WICImage);
      finally
        fs.Free;
      end;
    end;
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      WICImage := TWICImage.Create;
    end;
    
    procedure TForm1.FormDestroy(Sender: TObject);
    begin
      WICImage.Free;
    end;
    

    Note that it is not my preference to have the WIC image as a field of the form and not as a local variable. But I kept getting an AV and runtime error at program shutdown which I couldn't resolve when it is a local.