Anifile.pas
上传用户:hylc_2004
上传日期:2014-01-23
资源大小:46800k
文件大小:18k
- {*******************************************************}
- { }
- { Delphi VCL Extensions (RX) }
- { }
- { Copyright (c) 1997, 1998 Master-Bank }
- { }
- {*******************************************************}
- unit AniFile;
- {$I RX.INC}
- interface
- uses SysUtils, Windows, RTLConsts, Classes, Graphics;
- type
- TFourCC = array[0..3] of Char;
- PAniTag = ^TAniTag;
- TAniTag = packed record
- ckID: TFourCC;
- ckSize: Longint;
- end;
- TAniHeader = packed record
- cbSizeOf: Longint;
- cSteps: Longint;
- cFrames: Longint;
- cReserved: array[0..3] of Longint;
- jifRate: Longint; { 1 Jiffy = 1/60 sec }
- fl: Longint;
- end;
- const
- AF_ICON = $00000001;
- AF_SEQUENCE = $00000002;
- { TIconFrame }
- type
- TIconFrame = class(TPersistent)
- private
- FIcon: TIcon;
- FIsIcon: Boolean;
- FTag: TAniTag;
- FHotSpot: TPoint;
- FJiffRate: Longint;
- FSeq: Integer;
- public
- constructor Create(Index: Integer; Jiff: Longint);
- destructor Destroy; override;
- procedure Assign(Source: TPersistent); override;
- property JiffRate: Longint read FJiffRate;
- property Seq: Integer read FSeq;
- end;
- { TAnimatedCursorImage }
- TANINAME = array[0..255] of Char;
- TAnimatedCursorImage = class(TPersistent)
- private
- FHeader: TAniHeader;
- FTitle: TANINAME;
- FCreator: TANINAME;
- FIcons: TList;
- FOriginalColors: Word;
- procedure NewImage;
- procedure RiffReadError;
- function ReadCreateIcon(Stream: TStream; ASize: Longint;
- var HotSpot: TPoint; var IsIcon: Boolean): TIcon;
- function GetIconCount: Integer;
- function GetIcon(Index: Integer): TIcon;
- function GetFrame(Index: Integer): TIconFrame;
- function GetTitle: string;
- function GetCreator: string;
- function GetDefaultRate: Longint;
- procedure ReadAniStream(Stream: TStream);
- procedure ReadStream(Size: Longint; Stream: TStream);
- procedure WriteStream(Stream: TStream; WriteSize: Boolean);
- protected
- procedure AssignTo(Dest: TPersistent); override;
- procedure Draw(ACanvas: TCanvas; const ARect: TRect);
- public
- constructor Create;
- destructor Destroy; override;
- procedure Assign(Source: TPersistent); override;
- procedure Clear;
- procedure LoadFromStream(Stream: TStream); virtual;
- procedure SaveToStream(Stream: TStream); virtual;
- procedure LoadFromFile(const Filename: string); virtual;
- procedure AssignToBitmap(Bitmap: TBitmap; BackColor: TColor;
- DecreaseColors, Vertical: Boolean);
- property DefaultRate: Longint read GetDefaultRate;
- property IconCount: Integer read GetIconCount;
- property Icons[Index: Integer]: TIcon read GetIcon;
- property Frames[Index: Integer]: TIconFrame read GetFrame;
- property Title: string read GetTitle;
- property Creator: string read GetCreator;
- property OriginalColors: Word read FOriginalColors;
- end;
- implementation
- { This implementation based on animated cursor editor source code
- (ANIEDIT.C, copyright (C) Microsoft Corp., 1993-1996) }
- uses Consts, VCLUtils, MaxMin, RxGraph, IcoList, ClipIcon;
- const
- FOURCC_ACON = 'ACON';
- FOURCC_RIFF = 'RIFF';
- FOURCC_INFO = 'INFO';
- FOURCC_INAM = 'INAM';
- FOURCC_IART = 'IART';
- FOURCC_LIST = 'LIST';
- FOURCC_anih = 'anih';
- FOURCC_rate = 'rate';
- FOURCC_seq = 'seq ';
- FOURCC_fram = 'fram';
- FOURCC_icon = 'icon';
- function PadUp(Value: Longint): Longint;
- { Up Value to nearest word boundary }
- begin
- Result := Value + (Value mod 2);
- end;
- procedure DecreaseBMPColors(Bmp: TBitmap; Colors: Integer);
- var
- Stream: TStream;
- begin
- if (Bmp <> nil) and (Colors > 0) then begin
- Stream := BitmapToMemory(Bmp, Colors);
- try
- Bmp.LoadFromStream(Stream);
- finally
- Stream.Free;
- end;
- end;
- end;
- function GetDInColors(BitCount: Word): Integer;
- begin
- case BitCount of
- 1, 4, 8: Result := 1 shl BitCount;
- else Result := 0;
- end;
- end;
- { ReadTag, ReadChunk, SkipChunk. Some handy functions for reading RIFF files. }
- function ReadTag(S: TStream; pTag: PAniTag): Boolean;
- begin
- pTag^.ckID := #0#0#0#0;
- pTag^.ckSize := 0;
- Result := S.Read(pTag^, SizeOf(TAniTag)) = SizeOf(TAniTag);
- end;
- function ReadChunk(S: TStream; pTag: PAniTag; Data: Pointer): Boolean;
- begin
- Result := S.Read(Data^, pTag^.ckSize) = pTag^.ckSize;
- if Result then
- Result := S.Seek(pTag^.ckSize mod 2, soFromCurrent) <> -1;
- end;
- function ReadChunkN(S: TStream; pTag: PAniTag; Data: Pointer;
- cbMax: Longint): Boolean;
- var
- cbRead: Longint;
- begin
- cbRead := pTag^.ckSize;
- if cbMax < cbRead then cbRead := cbMax;
- Result := S.Read(Data^, cbRead) = cbRead;
- if Result then begin
- cbRead := PadUp(pTag^.ckSize) - cbRead;
- Result := S.Seek(cbRead, soFromCurrent) <> -1;
- end;
- end;
- function SkipChunk(S: TStream; pTag: PAniTag): Boolean;
- begin
- { Round pTag^.ckSize up to nearest word boundary to maintain alignment }
- Result := S.Seek(PadUp(pTag^.ckSize), soFromCurrent) <> -1;
- end;
- { Icon and cursor types }
- const
- rc3_StockIcon = 0;
- rc3_Icon = 1;
- rc3_Cursor = 2;
- type
- PCursorOrIcon = ^TCursorOrIcon;
- TCursorOrIcon = packed record
- Reserved: Word;
- wType: Word;
- Count: Word;
- end;
- PIconRec = ^TIconRec;
- TIconRec = packed record
- Width: Byte;
- Height: Byte;
- Colors: Word;
- xHotspot: Word;
- yHotspot: Word;
- DIBSize: Longint;
- DIBOffset: Longint;
- end;
- { TIconFrame }
- constructor TIconFrame.Create(Index: Integer; Jiff: Longint);
- begin
- inherited Create;
- FSeq := Index;
- FJiffRate := Jiff;
- end;
- destructor TIconFrame.Destroy;
- begin
- if FIcon <> nil then FIcon.Free;
- inherited Destroy;
- end;
- procedure TIconFrame.Assign(Source: TPersistent);
- begin
- if Source is TIconFrame then begin
- with TIconFrame(Source) do begin
- if Self.FIcon = nil then Self.FIcon := TIcon.Create;
- Self.FIcon.Assign(FIcon);
- Self.FIsIcon := FIsIcon;
- Move(FTag, Self.FTag, SizeOf(TAniTag));
- Self.FHotSpot.X := FHotSpot.X;
- Self.FHotSpot.Y := FHotSpot.Y;
- Self.FJiffRate := FJiffRate;
- Self.FSeq := FSeq;
- end;
- end
- else inherited Assign(Source);
- end;
- { TAnimatedCursorImage }
- constructor TAnimatedCursorImage.Create;
- begin
- inherited Create;
- FIcons := TList.Create;
- end;
- destructor TAnimatedCursorImage.Destroy;
- begin
- NewImage;
- FIcons.Free;
- inherited Destroy;
- end;
- procedure TAnimatedCursorImage.Clear;
- begin
- NewImage;
- end;
- procedure TAnimatedCursorImage.NewImage;
- var
- I: Integer;
- begin
- for I := 0 to FIcons.Count - 1 do TIconFrame(FIcons[I]).Free;
- FIcons.Clear;
- FillChar(FTitle, SizeOf(FTitle), 0);
- FillChar(FCreator, SizeOf(FCreator), 0);
- FillChar(FHeader, SizeOf(FHeader), 0);
- FOriginalColors := 0;
- end;
- procedure TAnimatedCursorImage.RiffReadError;
- begin
- raise EReadError.Create(ResStr(SReadError));
- end;
- function TAnimatedCursorImage.GetTitle: string;
- begin
- Result := StrPas(FTitle);
- end;
- function TAnimatedCursorImage.GetCreator: string;
- begin
- Result := StrPas(FCreator);
- end;
- function TAnimatedCursorImage.GetIconCount: Integer;
- begin
- Result := FIcons.Count;
- end;
- function TAnimatedCursorImage.GetIcon(Index: Integer): TIcon;
- begin
- Result := TIconFrame(FIcons[Index]).FIcon;
- end;
- function TAnimatedCursorImage.GetFrame(Index: Integer): TIconFrame;
- begin
- Result := TIconFrame(FIcons[Index]);
- end;
- function TAnimatedCursorImage.GetDefaultRate: Longint;
- begin
- Result := Max(0, Min((FHeader.jifRate * 100) div 6, High(Result)));
- end;
- procedure TAnimatedCursorImage.Assign(Source: TPersistent);
- var
- I: Integer;
- Frame: TIconFrame;
- begin
- if Source = nil then begin
- Clear;
- end
- else if Source is TAnimatedCursorImage then begin
- NewImage;
- try
- with TAnimatedCursorImage(Source) do begin
- Move(FHeader, Self.FHeader, SizeOf(FHeader));
- Self.FTitle := FTitle;
- Self.FCreator := FCreator;
- Self.FOriginalColors := FOriginalColors;
- for I := 0 to FIcons.Count - 1 do begin
- Frame := TIconFrame.Create(-1, FHeader.jifRate);
- try
- Frame.Assign(TIconFrame(FIcons[I]));
- Self.FIcons.Add(Frame);
- except
- Frame.Free;
- raise;
- end;
- end;
- end;
- except
- NewImage;
- raise;
- end;
- end
- else inherited Assign(Source);
- end;
- procedure TAnimatedCursorImage.AssignTo(Dest: TPersistent);
- var
- I: Integer;
- begin
- if Dest is TIcon then begin
- if IconCount > 0 then Dest.Assign(Icons[0])
- else Dest.Assign(nil);
- end
- else if Dest is TBitmap then begin
- if IconCount > 0 then
- AssignToBitmap(TBitmap(Dest), TBitmap(Dest).Canvas.Brush.Color,
- True, False)
- else Dest.Assign(nil);
- end
- else if Dest is TIconList then begin
- TIconList(Dest).BeginUpdate;
- try
- TIconList(Dest).Clear;
- for I := 0 to IconCount - 1 do TIconList(Dest).Add(Icons[I]);
- finally
- TIconList(Dest).EndUpdate;
- end;
- end
- else inherited AssignTo(Dest);
- end;
- function TAnimatedCursorImage.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, Mem.Size);
- 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 animatied cursor from a RIFF file. The RIFF file format for
- animated cursors looks like this:
- RIFF('ACON'
- LIST('INFO'
- INAM(<name>)
- IART(<artist>))
- anih(<anihdr>)
- [rate(<rateinfo>)]
- ['seq '( <seq_info>)]
- LIST('fram' icon(<icon_file>)))
- }
- procedure TAnimatedCursorImage.ReadAniStream(Stream: TStream);
- var
- iFrame, iRate, iSeq, I: Integer;
- Tag: TAniTag;
- Frame: TIconFrame;
- cbChunk, cbRead, Temp: Longint;
- Icon: TIcon;
- bFound, IsIcon: Boolean;
- HotSpot: TPoint;
- begin
- iFrame := 0; iRate := 0; iSeq := 0;
- { 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;
- NewImage;
- { 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.fl and AF_ICON) <> AF_ICON) or
- (FHeader.cFrames = 0) then RiffReadError;
- for I := 0 to FHeader.cSteps - 1 do begin
- Frame := TIconFrame.Create(I, FHeader.jifRate);
- FIcons.Add(Frame);
- end;
- end
- else if Tag.ckID = FOURCC_rate then begin
- { If we find a rate chunk, read it into its preallocated space }
- if not ReadChunkN(Stream, @Tag, @Temp, SizeOf(Longint)) then
- Break;
- if iRate < FIcons.Count then
- TIconFrame(FIcons[iRate]).FJiffRate := Temp;
- Inc(iRate);
- end
- else if Tag.ckID = FOURCC_seq then begin
- { If we find a seq chunk, read it into its preallocated space }
- if not ReadChunkN(Stream, @Tag, @Temp, SizeOf(Longint)) then
- Break;
- if iSeq < FIcons.Count then
- TIconFrame(FIcons[iSeq]).FSeq := Temp;
- Inc(iSeq);
- 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;
- bFound := False;
- for I := 0 to FIcons.Count - 1 do begin
- if TIconFrame(FIcons[I]).FSeq = iFrame then begin
- TIconFrame(FIcons[I]).FIcon := Icon;
- TIconFrame(FIcons[I]).FTag := Tag;
- TIconFrame(FIcons[I]).FHotSpot := HotSpot;
- TIconFrame(FIcons[I]).FIsIcon := IsIcon;
- bFound := True;
- end;
- end;
- if not bFound then begin
- Frame := TIconFrame.Create(-1, FHeader.jifRate);
- Frame.FIcon := Icon;
- Frame.FIsIcon := IsIcon;
- Frame.FTag := Tag;
- Frame.FHotSpot := HotSpot;
- FIcons.Add(Frame);
- end;
- Inc(iFrame);
- end
- else begin
- { Unknown chunk in fram list, just ignore it }
- SkipChunk(Stream, @Tag);
- end;
- 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, @FTitle, SizeOf(TANINAME) - 1) then
- Break;
- Dec(cbChunk, PadUp(Tag.ckSize));
- end
- else if Tag.ckID = FOURCC_IART then begin
- if (cbChunk < Tag.ckSize) or not
- ReadChunkN(Stream, @Tag, @FCreator, SizeOf(TANINAME) - 1) then
- Break;
- Dec(cbChunk, PadUp(Tag.ckSize));
- 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. (Don't forget that we have already skipped one dword) }
- Tag.ckSize := cbChunk;
- SkipChunk(Stream, @Tag);
- end;
- end
- else begin { We're not interested in this chunk, skip it. }
- if not SkipChunk(Stream, @Tag) then Break;
- end;
- end; { while }
- { Update the frame count incase we coalesced some frames while reading
- in the file. }
- for I := FIcons.Count - 1 downto 0 do begin
- if TIconFrame(FIcons[I]).FIcon = nil then begin
- TIconFrame(FIcons[I]).Free;
- FIcons.Delete(I);
- end;
- end;
- FHeader.cFrames := FIcons.Count;
- if FHeader.cFrames = 0 then RiffReadError;
- end;
- procedure TAnimatedCursorImage.ReadStream(Size: Longint; Stream: TStream);
- var
- Data: TMemoryStream;
- begin
- 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;
- end;
- procedure TAnimatedCursorImage.WriteStream(Stream: TStream;
- WriteSize: Boolean);
- begin
- NotImplemented;
- end;
- procedure TAnimatedCursorImage.LoadFromStream(Stream: TStream);
- begin
- ReadStream(Stream.Size - Stream.Position, Stream);
- end;
- procedure TAnimatedCursorImage.SaveToStream(Stream: TStream);
- begin
- WriteStream(Stream, False);
- end;
- procedure TAnimatedCursorImage.LoadFromFile(const Filename: string);
- var
- Stream: TStream;
- begin
- Stream := TFileStream.Create(Filename, fmOpenRead + fmShareDenyNone);
- try
- try
- LoadFromStream(Stream);
- except
- NewImage;
- raise;
- end;
- finally
- Stream.Free;
- end;
- end;
- procedure TAnimatedCursorImage.Draw(ACanvas: TCanvas; const ARect: TRect);
- begin
- if FIcons.Count > 0 then
- DrawRealSizeIcon(ACanvas, Icons[0], ARect.Left, ARect.Top);
- end;
- procedure TAnimatedCursorImage.AssignToBitmap(Bitmap: TBitmap; BackColor: TColor;
- DecreaseColors, Vertical: Boolean);
- var
- I: Integer;
- Temp: TBitmap;
- 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 * FIcons.Count;
- end
- else begin
- Width := Icons[0].Width * FIcons.Count;
- Height := Icons[0].Height;
- end;
- Canvas.FillRect(Bounds(0, 0, Width, Height));
- for I := 0 to FIcons.Count - 1 do begin
- if Icons[I] <> nil then
- Canvas.Draw(Icons[I].Width * I * Ord(not Vertical),
- Icons[I].Height * I * Ord(Vertical), Icons[I]);
- end;
- end;
- if DecreaseColors then
- DecreaseBMPColors(Temp, Max(OriginalColors, 16));
- end;
- Bitmap.Assign(Temp);
- finally
- Temp.Free;
- end;
- end;
- end.