📄 rxgif.pas
字号:
type
TGIFHeader = packed record
Signature: array[0..2] of Char; { contains 'GIF' }
Version: array[0..2] of Char; { '87a' or '89a' }
end;
TScreenDescriptor = packed record
ScreenWidth: Word; { logical screen width }
ScreenHeight: Word; { logical screen height }
PackedFields: Byte;
BackgroundColorIndex: Byte; { Index to global color table }
AspectRatio: Byte; { actual ratio = (AspectRatio + 15) / 64 }
end;
TImageDescriptor = packed record
ImageLeftPos: Word; { column in pixels in respect to left of logical screen }
ImageTopPos: Word; { row in pixels in respect to top of logical screen }
ImageWidth: Word; { width of image in pixels }
ImageHeight: Word; { height of image in pixels }
PackedFields: Byte;
end;
{ GIF Extensions support }
type
TExtensionType = (etGraphic, etPlainText, etApplication, etComment);
const
ExtLabels: array[TExtensionType] of Byte = ($F9, $01, $FF, $FE);
LoopExtNS: string[11] = 'NETSCAPE2.0';
LoopExtAN: string[11] = 'ANIMEXTS1.0';
type
TGraphicControlExtension = packed record
BlockSize: Byte; { should be 4 }
PackedFields: Byte;
DelayTime: Word; { in centiseconds }
TransparentColorIndex: Byte;
Terminator: Byte;
end;
TPlainTextExtension = packed record
BlockSize: Byte; { should be 12 }
Left, Top, Width, Height: Word;
CellWidth, CellHeight: Byte;
FGColorIndex, BGColorIndex: Byte;
end;
TAppExtension = packed record
BlockSize: Byte; { should be 11 }
AppId: array[1..8] of Byte;
Authentication: array[1..3] of Byte;
end;
TExtensionRecord = packed record
case ExtensionType: TExtensionType of
etGraphic: (GCE: TGraphicControlExtension);
etPlainText: (PTE: TPlainTextExtension);
etApplication: (APPE: TAppExtension);
end;
{ TExtension }
TExtension = class(TPersistent)
private
FExtType: TExtensionType;
FData: TStrings;
FExtRec: TExtensionRecord;
public
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
function IsLoopExtension: Boolean;
end;
destructor TExtension.Destroy;
begin
FData.Free;
inherited Destroy;
end;
procedure TExtension.Assign(Source: TPersistent);
begin
if (Source <> nil) and (Source is TExtension) then begin
FExtType := TExtension(Source).FExtType;
FExtRec := TExtension(Source).FExtRec;
if TExtension(Source).FData <> nil then begin
if FData = nil then FData := TStringList.Create;
FData.Assign(TExtension(Source).FData);
end;
end
else inherited Assign(Source);
end;
function TExtension.IsLoopExtension: Boolean;
begin
Result := (FExtType = etApplication) and (FData.Count > 0) and
(CompareMem(@FExtRec.APPE.AppId, @LoopExtNS[1], FExtRec.APPE.BlockSize) or
CompareMem(@FExtRec.APPE.AppId, @LoopExtAN[1], FExtRec.APPE.BlockSize)) and
(Length(FData[0]) >= 3) and (Byte(FData[0][1]) = AE_LOOPING);
end;
procedure FreeExtensions(Extensions: TList); near;
begin
if Extensions <> nil then begin
while Extensions.Count > 0 do begin
TObject(Extensions[0]).Free;
Extensions.Delete(0);
end;
Extensions.Free;
end;
end;
function FindExtension(Extensions: TList; ExtType: TExtensionType): TExtension;
var
I: Integer;
begin
if Extensions <> nil then
for I := Extensions.Count - 1 downto 0 do begin
Result := TExtension(Extensions[I]);
if (Result <> nil) and (Result.FExtType = ExtType) then Exit;
end;
Result := nil;
end;
{
function CopyExtensions(Source: TList): TList; near;
var
I: Integer;
Ext: TExtension;
begin
Result := TList.Create;
try
for I := 0 to Source.Count - 1 do
if (Source[I] <> nil) and (TObject(Source[I]) is TExtension) then begin
Ext := TExtension.Create;
try
Ext.Assign(Source[I]);
Result.Add(Ext);
except
Ext.Free;
raise;
end;
end;
except
Result.Free;
raise;
end;
end;
}
type
TProgressProc = procedure (Stage: TProgressStage; PercentDone: Byte;
const Msg: string) of object;
{ GIF reading/writing routines
Procedures to read and write GIF files, GIF-decoding and encoding
based on freeware C source code of GBM package by Andy Key
(nyangau@interalpha.co.uk). The home page of GBM author is
at http://www.interalpha.net/customer/nyangau/. }
type
PIntCodeTable = ^TIntCodeTable;
TIntCodeTable = array[0..CODE_TABLE_SIZE - 1] of Word;
PReadContext = ^TReadContext;
TReadContext = record
Inx, Size: Longint;
Buf: array[0..255 + 4] of Byte;
CodeSize: Longint;
ReadMask: Longint;
end;
PWriteContext = ^TWriteContext;
TWriteContext = record
Inx: Longint;
CodeSize: Longint;
Buf: array[0..255 + 4] of Byte;
end;
TOutputContext = record
W, H, X, Y: Longint;
BitsPerPixel, Pass: Integer;
Interlace: Boolean;
LineIdent: Longint;
Data, CurrLineData: Pointer;
end;
PImageDict = ^TImageDict;
TImageDict = record
Tail, Index: Word;
Col: Byte;
end;
PDictTable = ^TDictTable;
TDictTable = array[0..CODE_TABLE_SIZE - 1] of TImageDict;
PRGBPalette = ^TRGBPalette;
TRGBPalette = array [Byte] of TRGBQuad;
function InitHash(P: Longint): Longint;
begin
Result := (P + 3) * 301;
end;
function InterlaceStep(Y, Height: Integer; var Pass: Integer): Integer;
begin
Result := Y;
case Pass of
0, 1: Inc(Result, 8);
2: Inc(Result, 4);
3: Inc(Result, 2);
end;
if Result >= Height then begin
if Pass = 0 then begin
Pass := 1; Result := 4;
if (Result < Height) then Exit;
end;
if Pass = 1 then begin
Pass := 2; Result := 2;
if (Result < Height) then Exit;
end;
if Pass = 2 then begin
Pass := 3; Result := 1;
end;
end;
end;
procedure ReadImageStream(Stream, Dest: TStream; var Desc: TImageDescriptor;
var Interlaced, LocalColors, Corrupted: Boolean; var BitsPerPixel: Byte;
var ColorTable: TGIFColorTable);
var
CodeSize, BlockSize: Byte;
begin
Corrupted := False;
Stream.ReadBuffer(Desc, SizeOf(TImageDescriptor));
Interlaced := (Desc.PackedFields and ID_INTERLACED) <> 0;
if (Desc.PackedFields and ID_LOCAL_COLOR_TABLE) <> 0 then
begin
{ Local colors table follows }
BitsPerPixel := 1 + Desc.PackedFields and ID_COLOR_TABLE_SIZE;
LocalColors := True;
ColorTable.Count := 1 shl BitsPerPixel;
Stream.ReadBuffer(ColorTable.Colors[0],
ColorTable.Count * SizeOf(TGIFColorItem));
end
else begin
LocalColors := False;
FillChar(ColorTable, SizeOf(ColorTable), 0);
end;
Stream.ReadBuffer(CodeSize, 1);
Dest.Write(CodeSize, 1);
repeat
Stream.Read(BlockSize, 1);
if (Stream.Position + BlockSize) > Stream.Size then begin
Corrupted := True;
Stream.Position := Stream.Size;
Exit;
end;
Dest.Write(BlockSize, 1);
if (Stream.Position + BlockSize) > Stream.Size then begin
BlockSize := Stream.Size - Stream.Position;
Corrupted := True;
end;
if BlockSize > 0 then Dest.CopyFrom(Stream, BlockSize);
until (BlockSize = 0) or (Stream.Position >= Stream.Size);
end;
procedure FillRGBPalette(const ColorTable: TGIFColorTable;
var Colors: TRGBPalette);
var
I: Byte;
begin
FillChar(Colors, SizeOf(Colors), $80);
for I := 0 to ColorTable.Count - 1 do begin
Colors[I].rgbRed := ColorTable.Colors[I].Red;
Colors[I].rgbGreen := ColorTable.Colors[I].Green;
Colors[I].rgbBlue := ColorTable.Colors[I].Blue;
Colors[I].rgbReserved := 0;
end;
end;
function ReadCode(Stream: TStream; var Context: TReadContext): Longint;
var
RawCode: Longint;
ByteIndex: Longint;
Bytes: Byte;
BytesToLose: Longint;
begin
while (Context.Inx + Context.CodeSize > Context.Size) and
(Stream.Position < Stream.Size) do
begin
{ not enough bits in buffer - refill it }
{ Not very efficient, but infrequently called }
BytesToLose := Context.Inx shr 3;
{ Note biggest Code Size is 12 bits. And this can at worst span 3 Bytes }
Move(Context.Buf[Word(BytesToLose)], Context.Buf[0], 3);
Context.Inx := Context.Inx and 7;
Context.Size := Context.Size - (BytesToLose shl 3);
Stream.ReadBuffer(Bytes, 1);
if Bytes > 0 then
Stream.ReadBuffer(Context.Buf[Word(Context.Size shr 3)], Bytes);
Context.Size := Context.Size + (Bytes shl 3);
end;
ByteIndex := Context.Inx shr 3;
RawCode := Context.Buf[Word(ByteIndex)] +
(Word(Context.Buf[Word(ByteIndex + 1)]) shl 8);
if Context.CodeSize > 8 then
RawCode := RawCode + (Longint(Context.Buf[ByteIndex + 2]) shl 16);
RawCode := RawCode shr (Context.Inx and 7);
Context.Inx := Context.Inx + Byte(Context.CodeSize);
Result := RawCode and Context.ReadMask;
end;
procedure Output(Value: Byte; var Context: TOutputContext);
var
P: PByte;
begin
if (Context.Y >= Context.H) then Exit;
case Context.BitsPerPixel of
1: begin
P := HugeOffset(Context.CurrLineData, Context.X shr 3);
if (Context.X and $07 <> 0) then
P^ := P^ or Word(value shl (7 - (Word(Context.X and 7))))
else P^ := Byte(value shl 7);
end;
4: begin
P := HugeOffset(Context.CurrLineData, Context.X shr 1);
if (Context.X and 1 <> 0) then P^ := P^ or Value
else P^ := Byte(value shl 4);
end;
8: begin
P := HugeOffset(Context.CurrLineData, Context.X);
P^ := Value;
end;
end;
Inc(Context.X);
if Context.X < Context.W then Exit;
Context.X := 0;
if Context.Interlace then
Context.Y := InterlaceStep(Context.Y, Context.H, Context.Pass)
else Inc(Context.Y);
Context.CurrLineData := HugeOffset(Context.Data,
(Context.H - 1 - Context.Y) * Context.LineIdent);
end;
procedure ReadGIFData(Stream: TStream; const Header: TBitmapInfoHeader;
Interlaced, LoadCorrupt: Boolean; IntBitPerPixel: Byte; Data: Pointer;
var Corrupted: Boolean; ProgressProc: TProgressProc);
var
MinCodeSize, Temp: Byte;
MaxCode, BitMask, InitCodeSize: Longint;
ClearCode, EndingCode, FirstFreeCode, FreeCode: Word;
I, OutCount, Code: Longint;
CurCode, OldCode, InCode, FinalChar: Word;
Prefix, Suffix, OutCode: PIntCodeTable;
ReadCtxt: TReadContext;
OutCtxt: TOutputContext;
TableFull: Boolean;
begin
Corrupted := False;
OutCount := 0; OldCode := 0; FinalChar := 0;
TableFull := False;
Prefix := AllocMem(SizeOf(TIntCodeTable));
try
Suffix := AllocMem(SizeOf(TIntCodeTable));
try
OutCode := AllocMem(SizeOf(TIntCodeTable) + SizeOf(Word));
try
if Assigned(ProgressProc) then ProgressProc(psStarting, 0, '');
try
Stream.ReadBuffer(MinCodeSize, 1);
if (MinCodeSize < 2) or (MinCodeSize > 9) then begin
if LoadCorrupt then begin
Corrupted := True;
MinCodeSize := Max(2, Min(MinCodeSize, 9));
end
else GifError(LoadStr(SBadGIFCodeSize));
end;
{ Initial read context }
ReadCtxt.Inx := 0;
ReadCtxt.Size := 0;
ReadCtxt.CodeSize := MinCodeSize + 1;
ReadCtxt.ReadMask := (1 shl ReadCtxt.CodeSize) - 1;
{ Initialise pixel-output context }
OutCtxt.X := 0; OutCtxt.Y := 0;
OutCtxt.Pass := 0;
OutCtxt.W := Header.biWidth;
OutCtxt.H := Header.biHeight;
OutCtxt.BitsPerPixel := Header.biBitCount;
OutCtxt.Interlace := Interlaced;
OutCtxt.LineIdent := ((Header.biWidth * Header.biBitCount + 31)
div 32) * 4;
OutCtxt.Data := Data;
OutCtxt.CurrLineData := HugeOffset(Data, (Header.biHeight - 1) *
OutCtxt.LineIdent);
BitMask := (1 shl IntBitPerPixel) - 1;
{ 2 ^ MinCodeSize accounts for all colours in file }
ClearCode := 1 shl MinCodeSize;
EndingCode := ClearCode + 1;
FreeCode := ClearCode + 2;
FirstFreeCode := FreeCode;
{ 2^ (MinCodeSize + 1) includes clear and eoi Code and space too }
InitCodeSize := ReadCtxt.CodeSize;
MaxCode := 1 shl ReadCtxt.CodeSize;
Code := ReadCode(Stream, ReadCtxt);
while (Code <> EndingCode) and (Code <> $FFFF) and
(OutCtxt.Y < OutCtxt.H) do
begin
if (Code = ClearCode) then begin
ReadCtxt.CodeSize := InitCodeSize;
MaxCode := 1 shl ReadCtxt.CodeSize;
ReadCtxt.ReadMask := MaxCode - 1;
FreeCode := FirstFreeCode;
Code := ReadCode(Stream, ReadCtxt);
CurCode := Code; OldCode := Code;
if (Code = $FFFF) then Break;
FinalChar := (CurCode and BitMask);
Output(Byte(FinalChar), OutCtxt);
TableFull := False;
end
else begin
CurCode := Code;
InCode := Code;
if CurCode >= FreeCode then begin
CurCode := OldCode;
OutCode^[OutCount] := FinalChar;
Inc(OutCount);
end;
while (CurCode > BitMask) do begin
if (OutCount > CODE_TABLE_SIZE) then begin
if LoadCorrupt then begin
CurCode := BitMask;
OutCount := 1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -