📄 gif_myrxgif.~pas
字号:
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;
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;
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;
Corrupted := True;
Break;
end
else GifError(LoadStr(SGIFDecodeError));
end;
OutCode^[OutCount] := Suffix^[CurCode];
Inc(OutCount);
CurCode := Prefix^[CurCode];
end;
if Corrupted then Break;
FinalChar := CurCode and BitMask;
OutCode^[OutCount] := FinalChar;
Inc(OutCount);
for I := OutCount - 1 downto 0 do
Output(Byte(OutCode^[I]), OutCtxt);
OutCount := 0;
{ Update dictionary }
if not TableFull then begin
Prefix^[FreeCode] := OldCode;
Suffix^[FreeCode] := FinalChar;
{ Advance to next free slot }
Inc(FreeCode);
if (FreeCode >= MaxCode) then begin
if (ReadCtxt.CodeSize < 12) then begin
Inc(ReadCtxt.CodeSize);
MaxCode := MaxCode shl 1;
ReadCtxt.ReadMask := (1 shl ReadCtxt.CodeSize) - 1;
end
else TableFull := True;
end;
end;
OldCode := InCode;
end;
Code := ReadCode(Stream, ReadCtxt);
if Stream.Size > 0 then begin
Temp := Trunc(100.0 * (Stream.Position / Stream.Size));
if Assigned(ProgressProc) then ProgressProc(psRunning, Temp, '');
end;
end; { while }
if Code = $FFFF then GifError(ResStr(SReadError));
finally
if Assigned(ProgressProc) then begin
if ExceptObject = nil then ProgressProc(psEnding, 100, '')
else ProgressProc(psEnding, 0, Exception(ExceptObject).Message);
end;
end;
finally
FreeMem(OutCode, SizeOf(TIntCodeTable) + SizeOf(Word));
end;
finally
FreeMem(Suffix, SizeOf(TIntCodeTable));
end;
finally
FreeMem(Prefix, SizeOf(TIntCodeTable));
end;
end;
procedure WriteCode(Stream: TStream; Code: Longint;
var Context: TWriteContext);
var
BufIndex: Longint;
Bytes: Byte;
begin
BufIndex := Context.Inx shr 3;
Code := Code shl (Context.Inx and 7);
Context.Buf[BufIndex] := Context.Buf[BufIndex] or (Code);
Context.Buf[BufIndex + 1] := (Code shr 8);
Context.Buf[BufIndex + 2] := (Code shr 16);
Context.Inx := Context.Inx + Context.CodeSize;
if Context.Inx >= 255 * 8 then begin
{ Flush out full buffer }
Bytes := 255;
Stream.WriteBuffer(Bytes, 1);
Stream.WriteBuffer(Context.Buf, Bytes);
Move(Context.Buf[255], Context.Buf[0], 2);
FillChar(Context.Buf[2], 255, 0);
Context.Inx := Context.Inx - (255 * 8);
end;
end;
procedure FlushCode(Stream: TStream; var Context: TWriteContext);
var
Bytes: Byte;
begin
Bytes := (Context.Inx + 7) shr 3;
if Bytes > 0 then begin
Stream.WriteBuffer(Bytes, 1);
Stream.WriteBuffer(Context.Buf, Bytes);
end;
{ Data block terminator - a block of zero Size }
Bytes := 0;
Stream.WriteBuffer(Bytes, 1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -