user2227547
user2227547

Reputation:

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?

Upvotes: 3

Views: 2346

Answers (2)

MJKing
MJKing

Reputation: 88

In Delphi 11, you can use TWicImage in unit Graphics.

First, you have to get the number of pages in the TIFF file to check if it's possible to load it, and then load the page (FrameIndex range is 0 to FrameCount-1).

It will be something like this:

procedure loadTiff(imagen:TImage; tiffFilename: String; pagenumber: integer);
var
  wicImg: TWicImage;
begin
  wicImg:= TWicImage.create;
  wicImg.loadFromFile(tiffFilename);
  if wicImg.frameCount > pageNumber then
     begin
     wicImg.frameIndex:= pageNumber-1;
     wicImg.loadFromFile(tiffFilename);
     end;
  imagen.picture.bitmap.assign(wicImg);
  wicImg.free;
  
end; 

If you already know the number of pages, you can load the image without checking if its out of range.

Upvotes: 1

Sertac Akyuz
Sertac Akyuz

Reputation: 54832

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.

Upvotes: 4

Related Questions