If you’re storing an image in a blob field or the like, I guess it’s not uncommon to have another field that specifies the graphic type. Really though, there’s no need, since the common graphic formats (‘common’ in a Delphi context at least) all have distinct signatures that allow for their easy identification –
- Windows Bitmap (BMP): the ASCII characters ‘BM’.
- GIF: the three ASCII characters ‘GIF’.
- JPEG: a ‘new marker’ indicator ($FF) followed by a ‘start of image’ marker ($D8).
- PNG: in order, the following eight ASCII characters – #137, ‘P’, ‘N’, ‘G’, #13, #10, #26 and #10.
- TIFF: ‘II’ or ‘MM’ followed by $002A or $2A00 respectively.
- Windows icon (ICO): the second word has the value $0001.
- Windows Metafile (WMF): the first DWORD has the value $9AC6CDD7.
- Windows Enhanced Metafile (EMF): the first DWORD must equal $00000001, followed by $464D4520 (i.e., ‘EMF’ when read back to front) in the eleventh.
Of this ‘standard’ set, the ICO format is obviously a bit indeterminate when it comes to making a quick check, but the rest are unique enough. In practice, the lack of a proper signature for ICO just means you should test for it last.
Anyhow, putting this knowledge together, you can write something like this:
uses SysUtils, Classes, Graphics, GIFImg, JPEG, PngImage; const MinGraphicSize = 44; //we may test up to & including the 11th longword function FindGraphicClass(const Buffer; const BufferSize: Int64; out GraphicClass: TGraphicClass): Boolean; overload; var LongWords: array[Byte] of LongWord absolute Buffer; Words: array[Byte] of Word absolute Buffer; begin GraphicClass := nil; Result := False; if BufferSize < MinGraphicSize then Exit; case Words of $4D42: GraphicClass := TBitmap; $D8FF: GraphicClass := TJPEGImage; $4949: if Words = $002A then GraphicClass := TWicImage; //i.e., TIFF $4D4D: if Words = $2A00 then GraphicClass := TWicImage; //i.e., TIFF else if Int64(Buffer) = $A1A0A0D474E5089 then GraphicClass := TPNGImage else if LongWords = $9AC6CDD7 then GraphicClass := TMetafile else if (LongWords = 1) and (LongWords = $464D4520) then GraphicClass := TMetafile else if StrLComp(PAnsiChar(@Buffer), 'GIF', 3) = 0 then GraphicClass := TGIFImage else if Words = 1 then GraphicClass := TIcon; end; Result := (GraphicClass <> nil); end; function FindGraphicClass(Stream: TStream; out GraphicClass: TGraphicClass): Boolean; overload; var Buffer: PByte; CurPos: Int64; BytesRead: Integer; begin if Stream is TCustomMemoryStream then begin Buffer := TCustomMemoryStream(Stream).Memory; CurPos := Stream.Position; Inc(Buffer, CurPos); Result := FindGraphicClass(Buffer^, Stream.Size - CurPos, GraphicClass); Exit; end; GetMem(Buffer, MinGraphicSize); try BytesRead := Stream.Read(Buffer^, MinGraphicSize); Stream.Seek(-BytesRead, soCurrent); Result := FindGraphicClass(Buffer^, BytesRead, GraphicClass); finally FreeMem(Buffer); end; end;
The graphic classes returned here are the ‘in the box’ set for recent Delphi versions; depending on which particular Delphi version and third party libraries you use, the ones that are most appropriate to yourself may differ of course.
With the FindGraphicClass function at hand, we can then write a further utility routine that loads a TPicture instance from a TBlobField like thus:
uses Consts, DB; procedure LoadPictureFromBlobField(Field: TBlobField; Dest: TPicture); var Graphic: TGraphic; GraphicClass: TGraphicClass; Stream: TMemoryStream; begin Graphic := nil; Stream := TMemoryStream.Create; try Field.SaveToStream(Stream); if Stream.Size = 0 then begin Dest.Assign(nil); Exit; end; if not FindGraphicClass(Stream.Memory^, Stream.Size, GraphicClass) then raise EInvalidGraphic.Create(SInvalidImage); Graphic := GraphicClass.Create; Stream.Position := 0; Graphic.LoadFromStream(Stream); Dest.Assign(Graphic); finally Stream.Free; Graphic.Free; end; end;
This last routine may then be used like this (where ‘cdsPicture’ and ‘Image1’ are a TBlobField and TImage respectively that are created at design time):
procedure TForm1.btnTestLoadPictureClick(Sender: TObject); begin LoadPictureFromBlobField(cdsPicture, Image1.Picture); end;