📄 jvqani.pas
字号:
begin
if (Index >= 0) and (Index < FrameCount) then
begin
if Length(FSequence) <> 0 then
N := FSequence[Index]
else
N := Index;
FFrameResult.FIcon := TJvIconFrame(FIcons[N]).FIcon;
FFrameResult.FIsIcon := TJvIconFrame(FIcons[N]).FIsIcon;
FFrameResult.FHotSpot := TJvIconFrame(FIcons[N]).FHotSpot;
if Length(FRates) <> 0 then
FFrameResult.FRate := FRates[Index]
else
FFrameResult.FRate := FHeader.dwJIFRate;
Result := FFrameResult;
end
else
Result := nil;
end;
function TJvAni.ReadCreateIcon(Stream: TStream; ASize: Longint;
var HotSpot: TPoint; var IsIcon: Boolean): TIcon;
type
PIconRecArray = ^TIconRecArray;
TIconRecArray = array [0..300] of TIconRec;
var
List: PIconRecArray;
Mem: TMemoryStream;
HeaderLen, I: Integer;
BI: PBitmapInfoHeader;
begin
Result := nil;
Mem := TMemoryStream.Create;
try
Mem.SetSize(ASize);
Mem.CopyFrom(Stream, ASize);
HotSpot := Point(0, 0);
IsIcon := PCursorOrIcon(Mem.Memory)^.wType = RC3_ICON;
if PCursorOrIcon(Mem.Memory)^.wType = RC3_CURSOR then
PCursorOrIcon(Mem.Memory)^.wType := RC3_ICON;
if PCursorOrIcon(Mem.Memory)^.wType = RC3_ICON then
begin
{ determinate original icon color }
HeaderLen := PCursorOrIcon(Mem.Memory)^.Count * SizeOf(TIconRec);
GetMem(List, HeaderLen);
try
Mem.Position := SizeOf(TCursorOrIcon);
Mem.Read(List^, HeaderLen);
for I := 0 to PCursorOrIcon(Mem.Memory)^.Count - 1 do
with List^[I] do
begin
GetMem(BI, DIBSize);
try
Mem.Seek(DIBOffset, soFromBeginning);
Mem.Read(BI^, DIBSize);
FOriginalColors := Max(GetDInColors(BI^.biBitCount), FOriginalColors);
HotSpot := Point(xHotspot, yHotspot);
finally
FreeMem(BI, DIBSize);
end;
end;
finally
FreeMem(List, HeaderLen);
end;
{ return to start of stream }
Mem.Position := 0;
Result := TIcon.Create;
try
Result.LoadFromStream(Mem);
if IsIcon then
HotSpot := Point(Result.Width div 2, Result.Height div 2);
except
Result.Free;
Result := nil;
end;
end;
finally
Mem.Free;
end;
end;
{ Loads an animated cursor from a RIFF file. The RIFF file format for
animated cursors looks like this:
"RIFF" [Length of File]
"ACON"
"LIST" [Length of List]
"INAM" [Length of Title] [Data]
"IART" [Length of Author] [Data]
"fram"
"icon" [Length of Icon][Data] ; 1st in list
...
"icon" [Length of Icon] [Data] ; Last in list (1 to cFrames)
"anih" [Length of ANI header (36 bytes)] [Data] ; (see ANI Header TypeDef)
"rate" [Length of rate block] [Data] ; ea. rate is a long (length is 1 to cSteps)
"seq " [Length of sequence block] [Data] ; ea. seq is a long (length is 1 to cSteps)
}
procedure TJvAni.ReadAniStream(Stream: TStream);
var
I: Integer;
Tag: TJvAniTag;
Frame: TJvIconFrame;
cbChunk, cbRead: Longint;
Icon: TIcon;
IsIcon: Boolean;
HotSpot: TPoint;
Buffer: array [0..255] of Char;
begin
{ Make sure it's a RIFF ANI file }
if not ReadTag(Stream, Tag) or (Tag.ckID <> FOURCC_RIFF) then
RiffReadError;
if (Stream.Read(Tag.ckID, SizeOf(Tag.ckID)) < SizeOf(Tag.ckID)) or
(Tag.ckID <> FOURCC_ACON) then
RiffReadError;
Clear;
{ look for 'anih', 'rate', 'seq ', and 'icon' chunks }
while ReadTag(Stream, Tag) do
begin
if Tag.ckID = FOURCC_anih then
begin
if not ReadChunk(Stream, Tag, FHeader) then
Break;
if ((FHeader.dwFlags and AF_ICON) <> AF_ICON) or
(FHeader.dwFrames = 0) then
RiffReadError;
end
else
if Tag.ckID = FOURCC_rate then
begin
{ If we find a rate chunk, read it into its preallocated space }
SetLength(FRates, Tag.ckSize div SizeOf(Longint));
if not ReadChunkN(Stream, Tag, FRates[0], Tag.ckSize) then
Break;
end
else
if Tag.ckID = FOURCC_seq then
begin
{ If we find a seq chunk, read it into its preallocated space }
FFrameCount := Tag.ckSize div SizeOf(Longint);
SetLength(FSequence, FFrameCount);
if not ReadChunkN(Stream, Tag, FSequence[0], Tag.ckSize) then
Break;
end
else
if Tag.ckID = FOURCC_LIST then
begin
cbChunk := PadUp(Tag.ckSize);
{ See if this list is the 'fram' list of icon chunks }
cbRead := Stream.Read(Tag.ckID, SizeOf(Tag.ckID));
if cbRead < SizeOf(Tag.ckID) then
Break;
Dec(cbChunk, cbRead);
if Tag.ckID = FOURCC_fram then
begin
while cbChunk >= SizeOf(Tag) do
begin
if not ReadTag(Stream, Tag) then
Break;
Dec(cbChunk, SizeOf(Tag));
if Tag.ckID = FOURCC_icon then
begin
{ Ok, load the icon/cursor bits }
Icon := ReadCreateIcon(Stream, Tag.ckSize, HotSpot, IsIcon);
if Icon = nil then
Break;
Frame := TJvIconFrame.Create(FHeader.dwJIFRate);
Frame.FIcon := Icon;
Frame.FHotSpot := HotSpot;
Frame.FIsIcon := IsIcon;
FIcons.Add(Frame);
end
else
{ Unknown chunk in fram list, just ignore it }
SkipChunk(Stream, Tag);
Dec(cbChunk, PadUp(Tag.ckSize));
end;
end
else
if Tag.ckID = FOURCC_INFO then
begin
{ now look for INAM and IART chunks }
while cbChunk >= SizeOf(Tag) do
begin
if not ReadTag(Stream, Tag) then
Break;
Dec(cbChunk, SizeOf(Tag));
if Tag.ckID = FOURCC_INAM then
begin
if (cbChunk < Tag.ckSize) or
not ReadChunkN(Stream, Tag, Buffer[0], SizeOf(Buffer) - 1) then
Break;
Dec(cbChunk, PadUp(Tag.ckSize));
FTitle := Buffer;
end
else
if Tag.ckID = FOURCC_IART then
begin
if (cbChunk < Tag.ckSize) or
not ReadChunkN(Stream, Tag, Buffer[0], SizeOf(Buffer) - 1) then
Break;
Dec(cbChunk, PadUp(Tag.ckSize));
FAuthor := Buffer;
end
else
begin
if not SkipChunk(Stream, Tag) then
Break;
Dec(cbChunk, PadUp(Tag.ckSize));
end;
end;
end
else
begin
{ Not the fram list or the INFO list. Skip the rest of this
chunk. (Do not forget that we have already skipped one dword) }
Tag.ckSize := cbChunk;
SkipChunk(Stream, Tag);
end;
end
else
begin
{ We are not interested in this chunk, skip it. }
if not SkipChunk(Stream, Tag) then
Break;
end;
end;
{ Update the frame count in case we coalesced some frames while reading
in the file. }
for I := FIcons.Count - 1 downto 0 do
begin
if TJvIconFrame(FIcons[I]).FIcon = nil then
begin
TJvIconFrame(FIcons[I]).Free;
FIcons.Delete(I);
end;
end;
if FrameCount = 0 then
FFrameCount := FIcons.Count;
FHeader.dwFrames := FIcons.Count;
if FHeader.dwFrames = 0 then
RiffReadError;
end;
procedure SetFOURCC(var FourCC: TJvFourCC; ID: string);
begin
FourCC[0] := ID[1];
FourCC[1] := ID[2];
FourCC[2] := ID[3];
FourCC[3] := ID[4];
end;
procedure StartWriteChunk(Stream: TStream; var Tag: TJvAniTag; ID: string);
begin
SetFOURCC(Tag.ckID, ID);
Tag.ckSize := Stream.Position;
Stream.Write(Tag, SizeOf(Tag));
end;
procedure EndWriteChunk(Stream: TStream; var Tag: TJvAniTag; AddSize: Integer);
var
Pos: Int64;
B: Byte;
begin
Pos := Stream.Position;
Tag.ckSize := Pos - Tag.ckSize;
Stream.Seek(-Tag.ckSize, soFromCurrent);
Dec(Tag.ckSize, SizeOf(TJvAniTag));
Inc(Tag.ckSize, AddSize);
Stream.Write(Tag, SizeOf(Tag));
Stream.Seek(Pos, soFromBeginning);
if Odd(Tag.ckSize) then
begin
B := 0;
Stream.Write(B, 1);
end;
end;
procedure TJvAni.WriteAniStream(Stream: TStream);
var
I: Integer;
MemStream: TMemoryStream;
TagRIFF, TagLIST, Tag: TJvAniTag;
ID: TJvFourCC;
begin
MemStream := TMemoryStream.Create;
try
StartWriteChunk(MemStream, TagRIFF, FOURCC_RIFF);
SetFOURCC(ID, FOURCC_ACON);
MemStream.Write(ID, SizeOf(TJvFourCC));
if (Title <> '') or (Author <> '') then
begin
StartWriteChunk(MemStream, TagLIST, FOURCC_LIST);
SetFOURCC(ID, FOURCC_INFO);
MemStream.Write(ID, SizeOf(TJvFourCC));
if Title <> '' then
begin
StartWriteChunk(MemStream, Tag, FOURCC_INAM);
MemStream.Write(PChar(Title)^, Length(Title) + 1);
EndWriteChunk(MemStream, Tag, 0);
end;
if Author <> '' then
begin
StartWriteChunk(MemStream, Tag, FOURCC_IART);
MemStream.Write(PChar(Author)^, Length(Author) + 1);
EndWriteChunk(MemStream, Tag, 0);
end;
EndWriteChunk(MemStream, TagLIST, 0);
end;
StartWriteChunk(MemStream, Tag, FOURCC_anih);
FHeader.dwFrames := IconCount;
MemStream.Write(FHeader, SizeOf(TJvAniHeader));
EndWriteChunk(MemStream, Tag, 0);
if Length(FRates) <> 0 then
begin
StartWriteChunk(MemStream, Tag, FOURCC_rate);
MemStream.Write(FRates, Length(FRates) * SizeOf(Longint));
EndWriteChunk(MemStream, Tag, 0);
end;
if Length(FSequence) <> 0 then
begin
StartWriteChunk(MemStream, Tag, FOURCC_seq);
MemStream.Write(FSequence[0], Length(FSequence) * SizeOf(Longint));
EndWriteChunk(MemStream, Tag, 0);
end;
StartWriteChunk(MemStream, TagLIST, FOURCC_LIST);
SetFOURCC(ID, FOURCC_fram);
MemStream.Write(ID, SizeOf(TJvFourCC));
for I := 0 to IconCount - 1 do
begin
StartWriteChunk(MemStream, Tag, FOURCC_icon);
Icons[I].SaveToStream(MemStream);
EndWriteChunk(MemStream, Tag, 0);
end;
EndWriteChunk(MemStream, TagLIST, 0);
EndWriteChunk(MemStream, TagRIFF, SizeOf(TJvAniTag));
Stream.CopyFrom(MemStream, 0);
finally
MemStream.Free;
end;
end;
procedure TJvAni.LoadFromStream(Stream: TStream);
var
Data: TMemoryStream;
Size: Longint;
begin
Size := Stream.Size - Stream.Position;
Data := TMemoryStream.Create;
try
Data.SetSize(Size);
Stream.ReadBuffer(Data.Memory^, Size);
if Size > 0 then
begin
Data.Position := 0;
ReadAniStream(Data);
end;
finally
Data.Free;
end;
if FrameCount > 0 then
Index := 0;
end;
procedure TJvAni.SaveToStream(Stream: TStream);
begin
if IconCount = 0 then
raise EInvalidGraphicOperation.CreateRes(@SInvalidImage);
WriteAniStream(Stream);
end;
procedure TJvAni.LoadFromFile(const FileName: string);
var
Stream: TStream;
begin
Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone);
try
try
LoadFromStream(Stream);
except
NewImage;
raise;
end;
finally
Stream.Free;
end;
end;
procedure TJvAni.SaveToFile(const FileName: string);
var
Stream: TStream;
begin
Stream := TFileStream.Create(FileName, fmCreate or fmShareExclusive);
try
SaveToStream(Stream);
finally
Stream.Free;
end;
end;
procedure TJvAni.Draw(ACanvas: TCanvas; const ARect: TRect);
begin
if Assigned(FIcons) and (FIcons.Count > 0) then
if (Frames[Index] <> nil) and not Frames[Index].Icon.Empty then
ACanvas.Draw(ARect.Left, ARect.Top, Frames[Index].Icon);
end;
procedure TJvAni.AssignToBitmap(Bitmap: TBitmap; BackColor: TColor;
DecreaseColors, Vertical: Boolean);
var
I: Integer;
Temp: TBitmap;
Idx: Integer;
R: TRect;
begin
Temp := TBitmap.Create;
try
if FIcons.Count > 0 then
begin
with Temp do
begin
Monochrome := False;
Canvas.Brush.Color := BackColor;
if Vertical then
begin
Width := Icons[0].Width;
Height := Icons[0].Height * FrameCount;
end
else
begin
Width := Icons[0].Width * FrameCount;
Height := Icons[0].Height;
end;
Canvas.FillRect(Bounds(0, 0, Width, Height));
Idx := Index;
for I := 0 to FrameCount - 1 do
begin
Index := I;
R := Rect(Frames[I].Icon.Width * I * Ord(not Vertical),
Frames[I].Icon.Height * I * Ord(Vertical), 0, 0);
Draw(Canvas, R);
end;
Index := Idx;
end;
if DecreaseColors then
DecreaseBMPColors(Temp, Max(OriginalColors, 16));
end;
Bitmap.Assign(Temp);
finally
Temp.Free;
end;
end;
procedure TJvAni.AssignIconsToBitmap(Bitmap: TBitmap; BackColor: TColor;
DecreaseColors, Vertical: Boolean);
var
I: Integer;
Temp: TBitmap;
Idx: Integer;
R: TRect;
begin
Temp := TBitmap.Create;
try
if FIcons.Count > 0 then
begin
with Temp do
begin
Monochrome := False;
Canvas.Brush.Color := BackColor;
if Vertical then
begin
Width := Icons[0].Width;
Height := Icons[0].Height * IconCount;
end
else
begin
Width := Icons[0].Width * IconCount;
Height := Icons[0].Height;
end;
Canvas.FillRect(Bounds(0, 0, Width, Height));
Idx := Index;
for I := 0 to IconCount - 1 do
begin
Index := I;
R := Rect(Icons[I].Width * I * Ord(not Vertical),
Icons[I].Height * I * Ord(Vertical), 0, 0);
Draw(Canvas, R);
end;
Index := Idx;
end;
if DecreaseColors then
DecreaseBMPColors(Temp, Max(OriginalColors, 16));
end;
Bitmap.Assign(Temp);
finally
Temp.Free;
end;
end;
procedure TJvAni.LoadFromMimeSource(MimeSource: TMimeSource);
begin
raise EInvalidGraphicOperation.CreateRes(@RsENotSupported);
end;
procedure TJvAni.SaveToMimeSource(MimeSource: TClxMimeSource);
begin
raise EInvalidGraphicOperation.CreateRes(@RsENotSupported);
end;
{$IFDEF UNITVERSIONING}
const
UnitVersioning: TUnitVersionInfo = (
RCSfile: '$RCSfile: JvQAni.pas,v $';
Revision: '$Revision: 1.21 $';
Date: '$Date: 2004/09/08 10:51:02 $';
LogPath: 'JVCL\run'
);
{$ENDIF UNITVERSIONING}
initialization
{$IFDEF UNITVERSIONING}
RegisterUnitVersion(HInstance, UnitVersioning);
{$ENDIF UNITVERSIONING}
TPicture.RegisterFileFormat(RsAniExtension, RsAniFilterName, TJvAni);
finalization
{$IFDEF UNITVERSIONING}
UnregisterUnitVersion(HInstance);
{$ENDIF UNITVERSIONING}
TPicture.UnregisterGraphicClass(TJvAni);
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -