123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544 |
- {
- This file is part of the Free Pascal run time library.
- Copyright (c) 2008 by the Free Pascal development team
- GIF reader for fpImage.
- See the file COPYING.FPC, included in this distribution,
- for details about the copyright.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- **********************************************************************
- ToDo: read further images
- }
- unit FPReadGif;
- {$mode objfpc}{$H+}
- interface
- uses
- Classes, SysUtils, FPimage;
- type
- TGifRGB = packed record
- Red, Green, Blue : Byte;
- end;
- TGIFHeader = packed record
- Signature:array[0..2] of Char; //* Header Signature (always "GIF") */
- Version:array[0..2] of Char; //* GIF format version("87a" or "89a") */
- // Logical Screen Descriptor
- ScreenWidth:word; //* Width of Display Screen in Pixels */
- ScreenHeight:word; //* Height of Display Screen in Pixels */
- Packedbit, //* Screen and Color Map Information */
- BackgroundColor, //* Background Color Index */
- AspectRatio:byte; //* Pixel Aspect Ratio */
- end;
- TGifImageDescriptor = packed record
- Left, //* X position of image on the display */
- Top, //* Y position of image on the display */
- Width, //* Width of the image in pixels */
- Height:word; //* Height of the image in pixels */
- Packedbit:byte; //* Image and Color Table Data Information */
- end;
- TGifGraphicsControlExtension = packed record
- BlockSize, //* Size of remaining fields (always 04h) */
- Packedbit:byte; //* Method of graphics disposal to use */
- DelayTime:word; //* Hundredths of seconds to wait */
- ColorIndex, //* Transparent Color Index */
- Terminator:byte; //* Block Terminator (always 0) */
- end;
- TFPReaderGif = class;
- TGifCreateCompatibleImgEvent = procedure(Sender: TFPReaderGif;
- var NewImage: TFPCustomImage) of object;
- { TFPReaderGif }
- TFPReaderGif = class(TFPCustomImageReader)
- protected
- FHeader: TGIFHeader;
- FDescriptor: TGifImageDescriptor;
- FGraphicsCtrlExt: TGifGraphicsControlExtension;
- FTransparent: Boolean;
- FGraphCtrlExt: Boolean;
- FScanLine: PByte;
- FLineSize: Integer;
- FPalette: TFPPalette;
- FWidth: integer;
- FHeight: Integer;
- FInterlace: boolean;
- FBitsPerPixel: byte;
- FBackground: byte;
- FResolution: byte;
- FOnCreateImage: TGifCreateCompatibleImgEvent;
- procedure ReadPalette(Stream: TStream; Size: integer);
- function AnalyzeHeader: Boolean;
- procedure InternalRead(Stream: TStream; Img: TFPCustomImage); override;
- function ReadScanLine(Stream: TStream): boolean; virtual;
- function WriteScanLine(Img: TFPCustomImage): Boolean; virtual;
- function InternalCheck (Stream: TStream) : boolean; override;
- function SkipBlock(Stream: TStream): byte;
- public
- constructor Create; override;
- destructor Destroy; override;
- property Header: TGIFHeader read FHeader;
- property Descriptor: TGifImageDescriptor read FDescriptor;
- property GraphCtrlExt: Boolean read FGraphCtrlExt;
- property GraphicsCtrlExt: TGifGraphicsControlExtension read FGraphicsCtrlExt;
- property Transparent: Boolean read FTransparent;
- property Palette: TFPPalette read FPalette;
- property Width: integer read FWidth;
- property Height: Integer read FHeight;
- property Interlace: boolean read FInterlace;
- property BitsPerPixel: byte read FBitsPerPixel;
- property Background: byte read FBackground;
- property Resolution: byte read FResolution;
- property OnCreateImage: TGifCreateCompatibleImgEvent read FOnCreateImage write FOnCreateImage;
- end;
- implementation
- { TFPReaderGif }
- procedure TFPReaderGif.ReadPalette(Stream: TStream; Size: integer);
- Var
- RGBEntry : TGifRGB;
- I : Integer;
- c : TFPColor;
- begin
- FPalette.count := 0;
- For I:=0 To Size-1 Do
- Begin
- Stream.Read(RGBEntry, SizeOf(RGBEntry));
- With c do
- begin
- Red:=RGBEntry.Red or (RGBEntry.Red shl 8);
- Green:=RGBEntry.Green or (RGBEntry.Green shl 8);
- Blue:=RGBEntry.Blue or (RGBEntry.Blue shl 8);
- Alpha:=alphaOpaque;
- end;
- FPalette.Add(C);
- End;
- end;
- function TFPReaderGif.AnalyzeHeader: Boolean;
- var
- C : TFPColor;
- begin
- Result:=false;
- With FHeader do
- begin
- if (Signature = 'GIF') and
- ((Version = '87a') or
- (Version = '89a')) then
- else
- Raise Exception.Create('Unknown/Unsupported GIF image type');
- FResolution := Packedbit and $70 shr 5 + 1;
- FBitsPerPixel:=Packedbit and 7 + 1;
- FBackground := BackgroundColor;
- With FDescriptor do
- begin
- fWidth:=Width;
- fHeight:=Height;
- FInterlace := (Packedbit and $40 = $40);
- end;
- FTransparent:= FBackground <> 0;
- if FGraphCtrlExt then
- begin
- FTransparent:=(FGraphicsCtrlExt.Packedbit and $01)<>0;
- If FTransparent then
- FBackground:=FGraphicsCtrlExt.ColorIndex;
- end;
- FLineSize:=FWidth*(FHeight+1);
- GetMem(FScanLine,FLineSize);
- If FTransparent then
- begin
- C:=FPalette.Color[FBackground];
- C.alpha:=alphaTransparent;
- FPalette.Color[FBackground]:=C;
- end;
- end;
- Result:=true;
- end;
- procedure TFPReaderGif.InternalRead(Stream: TStream; Img: TFPCustomImage);
- var
- Introducer:byte;
- ColorTableSize :Integer;
- ContProgress: Boolean;
- begin
- FPalette:=nil;
- FScanLine:=nil;
- try
- ContProgress:=true;
- Progress(psStarting, 0, False, Rect(0,0,0,0), '', ContProgress);
- if not ContProgress then exit;
- FPalette := TFPPalette.Create(0);
- Stream.Position:=0;
- // header
- Stream.Read(FHeader,SizeOf(FHeader));
- Progress(psRunning, trunc(100.0 * (Stream.position / Stream.size)), False, Rect(0,0,0,0), '', ContProgress);
- if not ContProgress then exit;
-
- // Endian Fix Mantis 8541. Gif is always little endian
- {$IFDEF ENDIAN_BIG}
- with FHeader do
- begin
- ScreenWidth := LEtoN(ScreenWidth);
- ScreenHeight := LEtoN(ScreenHeight);
- end;
- {$ENDIF}
- // global palette
- if (FHeader.Packedbit and $80) <> 0 then
- begin
- ColorTableSize := FHeader.Packedbit and 7 + 1;
- ReadPalette(stream, 1 shl ColorTableSize);
- end;
- // skip extensions
- Repeat
- Introducer:=SkipBlock(Stream);
- until (Introducer = $2C) or (Introducer = $3B);
- // descriptor
- Stream.Read(FDescriptor, SizeOf(FDescriptor));
- {$IFDEF ENDIAN_BIG}
- with FDescriptor do
- begin
- Left := LEtoN(Left);
- Top := LEtoN(Top);
- Width := LEtoN(Width);
- Height := LEtoN(Height);
- end;
- {$ENDIF}
- // local palette
- if (FDescriptor.Packedbit and $80) <> 0 then
- begin
- ColorTableSize := FDescriptor.Packedbit and 7 + 1;
- ReadPalette(stream, 1 shl ColorTableSize);
- end;
- // parse header
- if not AnalyzeHeader then exit;
- // create image
- if Assigned(OnCreateImage) then
- OnCreateImage(Self,Img);
- Img.SetSize(FWidth,FHeight);
- // read pixels
- if not ReadScanLine(Stream) then exit;
- if not WriteScanLine(Img) then exit;
- // ToDo: read further images
- finally
- FreeAndNil(FPalette);
- ReAllocMem(FScanLine,0);
- end;
- Progress(FPimage.psEnding, 100, false, Rect(0,0,FWidth,FHeight), '', ContProgress);
- end;
- function TFPReaderGif.ReadScanLine(Stream: TStream): Boolean;
- var
- OldPos,
- UnpackedSize,
- PackedSize:longint;
- I: Integer;
- Data,
- Bits,
- Code: Cardinal;
- SourcePtr: PByte;
- InCode: Cardinal;
- CodeSize: Cardinal;
- CodeMask: Cardinal;
- FreeCode: Cardinal;
- OldCode: Cardinal;
- Prefix: array[0..4095] of Cardinal;
- Suffix,
- Stack: array [0..4095] of Byte;
- StackPointer: PByte;
- DataComp,
- Target: PByte;
- B,
- FInitialCodeSize,
- FirstChar: Byte;
- ClearCode,
- EOICode: Word;
- ContProgress: Boolean;
- begin
- DataComp:=nil;
- ContProgress:=true;
- try
- // read dictionary size
- Stream.read(FInitialCodeSize, 1);
- // search end of compressor table
- OldPos:=Stream.Position;
- PackedSize := 0;
- Repeat
- Stream.read(B, 1);
- if B > 0 then
- begin
- inc(PackedSize, B);
- Stream.Seek(B, soFromCurrent);
- CodeMask := (1 shl CodeSize) - 1;
- end;
- until B = 0;
- Progress(psRunning, trunc(100.0 * (Stream.position / Stream.size)),
- False, Rect(0,0,0,0), '', ContProgress);
- if not ContProgress then exit(false);
- Getmem(DataComp, PackedSize);
- // read compressor table
- SourcePtr:=DataComp;
- Stream.Position:=OldPos;
- Repeat
- Stream.read(B, 1);
- if B > 0 then
- begin
- Stream.ReadBuffer(SourcePtr^, B);
- Inc(SourcePtr,B);
- end;
- until B = 0;
- Progress(psRunning, trunc(100.0 * (Stream.position / Stream.size)),
- False, Rect(0,0,0,0), '', ContProgress);
- if not ContProgress then exit(false);
- SourcePtr:=DataComp;
- Target := FScanLine;
- CodeSize := FInitialCodeSize + 1;
- ClearCode := 1 shl FInitialCodeSize;
- EOICode := ClearCode + 1;
- FreeCode := ClearCode + 2;
- OldCode := 4096;
- CodeMask := (1 shl CodeSize) - 1;
- UnpackedSize:=FWidth * FHeight;
- for I := 0 to ClearCode - 1 do
- begin
- Prefix[I] := 4096;
- Suffix[I] := I;
- end;
- StackPointer := @Stack;
- FirstChar := 0;
- Data := 0;
- Bits := 0;
- // LZW decompression gif
- while (UnpackedSize > 0) and (PackedSize > 0) do
- begin
- Inc(Data, SourcePtr^ shl Bits);
- Inc(Bits, 8);
- while Bits >= CodeSize do
- begin
- Code := Data and CodeMask;
- Data := Data shr CodeSize;
- Dec(Bits, CodeSize);
- if Code = EOICode then Break;
- if Code = ClearCode then
- begin
- CodeSize := FInitialCodeSize + 1;
- CodeMask := (1 shl CodeSize) - 1;
- FreeCode := ClearCode + 2;
- OldCode := 4096;
- Continue;
- end;
- if Code > FreeCode then Break;
- if OldCode = 4096 then
- begin
- FirstChar := Suffix[Code];
- Target^ := FirstChar;
- Inc(Target);
- Dec(UnpackedSize);
- OldCode := Code;
- Continue;
- end;
- InCode := Code;
- if Code = FreeCode then
- begin
- StackPointer^ := FirstChar;
- Inc(StackPointer);
- Code := OldCode;
- end;
- while Code > ClearCode do
- begin
- StackPointer^ := Suffix[Code];
- Inc(StackPointer);
- Code := Prefix[Code];
- end;
- FirstChar := Suffix[Code];
- StackPointer^ := FirstChar;
- Inc(StackPointer);
- Prefix[FreeCode] := OldCode;
- Suffix[FreeCode] := FirstChar;
- if (FreeCode = CodeMask) and
- (CodeSize < 12) then
- begin
- Inc(CodeSize);
- CodeMask := (1 shl CodeSize) - 1;
- end;
- if FreeCode < 4095 then Inc(FreeCode);
- OldCode := InCode;
- repeat
- Dec(StackPointer);
- Target^ := StackPointer^;
- Inc(Target);
- Dec(UnpackedSize);
- until StackPointer = @Stack;
- end;
- Inc(SourcePtr);
- Dec(PackedSize);
- end;
- Progress(psRunning, trunc(100.0 * (Stream.position / Stream.size)),
- False, Rect(0,0,0,0), '', ContProgress);
- if not ContProgress then exit(false);
- finally
- if DataComp<>nil then
- FreeMem(DataComp);
- end;
- Result:=true;
- end;
- function TFPReaderGif.WriteScanLine(Img: TFPCustomImage): Boolean;
- Var
- Row, Col : Integer;
- Pass, Every : byte;
- P : PByte;
- function IsMultiple(NumberA, NumberB: Integer): Boolean;
- begin
- Result := (NumberA >= NumberB) and
- (NumberB > 0) and
- (NumberA mod NumberB = 0);
- end;
- begin
- Result:=false;
- P:=FScanLine;
- If FInterlace then
- begin
- For Pass := 1 to 4 do
- begin
- Case Pass of
- 1 : begin
- Row := 0;
- Every := 8;
- end;
- 2 : begin
- Row := 4;
- Every := 8;
- end;
- 3 : begin
- Row := 2;
- Every := 4;
- end;
- 4 : begin
- Row := 1;
- Every := 2;
- end;
- end;
- Repeat
- for Col:=0 to Img.Width-1 do
- begin
- Img.Colors[Col,Row]:=FPalette[P^];
- Inc(P);
- end;
- Inc(Row, Every);
- until Row >= Img.Height;
- end;
- end
- else
- begin
- for Row:=0 to Img.Height-1 do
- for Col:=0 to Img.Width-1 do
- begin
- Img.Colors[Col,Row]:=FPalette[P^];
- Inc(P);
- end;
- end;
- Result:=true;
- end;
- function TFPReaderGif.InternalCheck(Stream: TStream): boolean;
- var
- OldPos: Int64;
- begin
- try
- OldPos:=Stream.Position;
- Stream.Read(FHeader,SizeOf(FHeader));
- Result:=(FHeader.Signature = 'GIF') and
- ((FHeader.Version = '87a') or (FHeader.Version = '89a'));
- Stream.Position:=OldPos;
- except
- Result:=False;
- end;
- end;
- function TFPReaderGif.SkipBlock(Stream: TStream): byte;
- var
- Introducer,
- Labels,
- SkipByte : byte;
- begin
- Stream.read(Introducer,1);
- if Introducer = $21 then
- begin
- Stream.read(Labels,1);
- Case Labels of
- $FE, $FF : // Comment Extension block or Application Extension block
- while true do
- begin
- Stream.Read(SkipByte, 1);
- if SkipByte = 0 then Break;
- Stream.Seek(SkipByte, soFromCurrent);
- end;
- $F9 : // Graphics Control Extension block
- begin
- Stream.Read(FGraphicsCtrlExt, SizeOf(FGraphicsCtrlExt));
- FGraphCtrlExt:=True;
- end;
- $01 : // Plain Text Extension block
- begin
- Stream.Read(SkipByte, 1);
- Stream.Seek(SkipByte, soFromCurrent);
- while true do
- begin
- Stream.Read(SkipByte, 1);
- if SkipByte = 0 then Break;
- Stream.Seek(SkipByte, soFromCurrent);
- end;
- end;
- end;
- end;
- Result:=Introducer;
- end;
- constructor TFPReaderGif.Create;
- begin
- inherited Create;
- end;
- destructor TFPReaderGif.Destroy;
- begin
- inherited Destroy;
- end;
- initialization
- ImageHandlers.RegisterImageReader ('GIF Graphics', 'gif', TFPReaderGif);
- end.
|