358 lines
8.4 KiB
ObjectPascal
358 lines
8.4 KiB
ObjectPascal
|
|
||
|
{******************************************}
|
||
|
{ }
|
||
|
{ FastReport v4.0 }
|
||
|
{ Picture Cache }
|
||
|
{ }
|
||
|
{ Copyright (c) 1998-2008 }
|
||
|
{ by Alexander Tzyganenko, }
|
||
|
{ Fast Reports Inc. }
|
||
|
{ }
|
||
|
{******************************************}
|
||
|
|
||
|
unit FMX.frxPictureCache;
|
||
|
|
||
|
interface
|
||
|
|
||
|
{$I fmx.inc}
|
||
|
{$I frx.inc}
|
||
|
|
||
|
uses
|
||
|
System.Classes, System.SysUtils, System.Variants,
|
||
|
FMX.frxClass, FMX.frxXML, FMX.frxUtils;
|
||
|
|
||
|
|
||
|
type
|
||
|
TfrxCacheItem = packed record
|
||
|
Segment: Longint;
|
||
|
Offset: Longint;
|
||
|
end;
|
||
|
|
||
|
PfrxCacheItem = ^TfrxCacheItem;
|
||
|
|
||
|
TfrxCacheList = class(TObject)
|
||
|
private
|
||
|
function Get(Index: Integer): PfrxCacheItem;
|
||
|
protected
|
||
|
FItems: TList;
|
||
|
protected
|
||
|
procedure Clear;
|
||
|
public
|
||
|
constructor Create;
|
||
|
destructor Destroy; override;
|
||
|
function Add: PfrxCacheItem;
|
||
|
function Count: Integer;
|
||
|
property Items[Index: Integer]: PfrxCacheItem read Get; default;
|
||
|
end;
|
||
|
|
||
|
TfrxFileStream = class(TFileStream)
|
||
|
private
|
||
|
FSz: LongWord;
|
||
|
public
|
||
|
function Seek(Offset: Longint; Origin: Word): Longint; overload; override;
|
||
|
function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; overload; override;
|
||
|
end;
|
||
|
|
||
|
TfrxMemoryStream = class(TMemoryStream)
|
||
|
private
|
||
|
FSz: LongWord;
|
||
|
public
|
||
|
function Seek(Offset: Longint; Origin: Word): Longint; overload; override;
|
||
|
function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; overload; override;
|
||
|
end;
|
||
|
|
||
|
|
||
|
TfrxPictureCache = class(TObject)
|
||
|
private
|
||
|
FItems: TfrxCacheList;
|
||
|
FCacheStreamList: TList;
|
||
|
FTempFile: TStringList;
|
||
|
FTempDir: String;
|
||
|
FUseFileCache: Boolean;
|
||
|
procedure Add;
|
||
|
procedure SetTempDir(const Value: String);
|
||
|
procedure SetUseFileCache(const Value: Boolean);
|
||
|
public
|
||
|
constructor Create;
|
||
|
destructor Destroy; override;
|
||
|
procedure Clear;
|
||
|
procedure AddPicture(Picture: TfrxPictureView);
|
||
|
procedure GetPicture(Picture: TfrxPictureView);
|
||
|
procedure SaveToXML(Item: TfrxXMLItem);
|
||
|
procedure LoadFromXML(Item: TfrxXMLItem);
|
||
|
procedure AddSegment;
|
||
|
property UseFileCache: Boolean read FUseFileCache write SetUseFileCache;
|
||
|
property TempDir: String read FTempDir write SetTempDir;
|
||
|
end;
|
||
|
|
||
|
|
||
|
implementation
|
||
|
|
||
|
{$IFDEF POSIX}
|
||
|
uses Posix.Unistd;
|
||
|
{$ENDIF}
|
||
|
|
||
|
|
||
|
function frxStreamToString(Stream: TStream; Size: Integer): String;
|
||
|
var
|
||
|
p: PChar;
|
||
|
begin
|
||
|
SetLength(Result, Size * 2);
|
||
|
GetMem(p, Size);
|
||
|
Stream.Read(p^, Size);
|
||
|
BinToHex(p, PChar(@Result[1]), Size);
|
||
|
FreeMem(p, Size);
|
||
|
end;
|
||
|
|
||
|
procedure frxStringToStream(const s: String; Stream: TStream);
|
||
|
var
|
||
|
Size: Integer;
|
||
|
p: PChar;
|
||
|
begin
|
||
|
Size := Length(s) div 2;
|
||
|
GetMem(p, Size);
|
||
|
HexToBin(PChar(@s[1]), p, Size * 2);
|
||
|
Stream.Write(p^, Size);
|
||
|
FreeMem(p, Size);
|
||
|
end;
|
||
|
|
||
|
|
||
|
{ TfrxPictureCache }
|
||
|
|
||
|
constructor TfrxPictureCache.Create;
|
||
|
begin
|
||
|
FItems := TfrxCacheList.Create;
|
||
|
FCacheStreamList := TList.Create;
|
||
|
FTempFile := TStringList.Create;
|
||
|
FUseFileCache := False;
|
||
|
end;
|
||
|
|
||
|
destructor TfrxPictureCache.Destroy;
|
||
|
begin
|
||
|
Clear;
|
||
|
FItems.Free;
|
||
|
FCacheStreamList.Free;
|
||
|
FTempFile.Free;
|
||
|
inherited;
|
||
|
end;
|
||
|
|
||
|
procedure TfrxPictureCache.Clear;
|
||
|
begin
|
||
|
while FCacheStreamList.Count > 0 do
|
||
|
begin
|
||
|
TObject(FCacheStreamList[0]).Free;
|
||
|
FCacheStreamList.Delete(0);
|
||
|
if FUseFileCache then
|
||
|
begin
|
||
|
DeleteFile(FTempFile[0]);
|
||
|
FTempFile.Delete(0);
|
||
|
end;
|
||
|
end;
|
||
|
FItems.Clear;
|
||
|
end;
|
||
|
|
||
|
procedure TfrxPictureCache.Add;
|
||
|
begin
|
||
|
if (FCacheStreamList.Count = 0) or (TStream(FCacheStreamList[FCacheStreamList.Count - 1]).Size >= Round(MaxInt - MaxInt/6)) then
|
||
|
AddSegment;
|
||
|
|
||
|
with FItems.Add^ do
|
||
|
begin
|
||
|
Segment := FCacheStreamList.Count - 1;
|
||
|
Offset := TStream(FCacheStreamList[FCacheStreamList.Count - 1]).Size;
|
||
|
TStream(FCacheStreamList[FCacheStreamList.Count - 1]).Position := Offset;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TfrxPictureCache.AddPicture(Picture: TfrxPictureView);
|
||
|
begin
|
||
|
if (Picture.Picture = nil) or (Picture.Picture.IsEmpty) then
|
||
|
Picture.ImageIndex := 0
|
||
|
else
|
||
|
begin
|
||
|
Picture.ImageIndex := FItems.Count + 1;
|
||
|
Add;
|
||
|
Picture.Picture.SaveToStream(TStream(FCacheStreamList[FItems[Picture.ImageIndex - 1]^.Segment]));
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TfrxPictureCache.GetPicture(Picture: TfrxPictureView);
|
||
|
var
|
||
|
Size, Offset, Segment: Longint;
|
||
|
ImageIndex: Integer;
|
||
|
Stream: TStream;
|
||
|
begin
|
||
|
if (Picture.ImageIndex <= 0) or (Picture.ImageIndex > FItems.Count) then
|
||
|
Picture.Picture.Assign(nil)
|
||
|
else
|
||
|
begin
|
||
|
if FCacheStreamList.Count = 0 then
|
||
|
Exit;
|
||
|
ImageIndex := Picture.ImageIndex ;
|
||
|
Segment := Fitems[ImageIndex - 1]^.Segment;
|
||
|
Offset := FItems[ImageIndex - 1]^.Offset;
|
||
|
Stream := TStream(FCacheStreamList[Segment]);
|
||
|
|
||
|
if (Picture.ImageIndex < FItems.Count) and (Fitems[ImageIndex]^.Segment = Segment) then
|
||
|
Size := FItems[ImageIndex]^.Offset - Offset
|
||
|
else
|
||
|
Size := Stream.Size - Offset;
|
||
|
|
||
|
Stream.Position := Offset;
|
||
|
|
||
|
if FUseFileCache then
|
||
|
TfrxFileStream(Stream).FSz := Offset + Size
|
||
|
else
|
||
|
TfrxMemoryStream(Stream).FSz := Offset + Size;
|
||
|
|
||
|
Picture.Picture.LoadFromStream(Stream);
|
||
|
|
||
|
if FUseFileCache then
|
||
|
TfrxFileStream(Stream).FSz := 0
|
||
|
else
|
||
|
TfrxMemoryStream(Stream).FSz := 0
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TfrxPictureCache.LoadFromXML(Item: TfrxXMLItem);
|
||
|
var
|
||
|
i: Integer;
|
||
|
xi: TfrxXMLItem;
|
||
|
begin
|
||
|
Clear;
|
||
|
for i := 0 to Item.Count - 1 do
|
||
|
begin
|
||
|
xi := Item[i];
|
||
|
Add;
|
||
|
frxStringToStream(xi.Prop['stream'], TStream(FCacheStreamList[FCacheStreamList.Count - 1]));
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TfrxPictureCache.SaveToXML(Item: TfrxXMLItem);
|
||
|
var
|
||
|
i, Size: Integer;
|
||
|
xi: TfrxXMLItem;
|
||
|
begin
|
||
|
Item.Clear;
|
||
|
for i := 0 to FCacheStreamList.Count - 1 do
|
||
|
TStream(FCacheStreamList[i]).Position := 0;
|
||
|
for i := 0 to FItems.Count - 1 do
|
||
|
begin
|
||
|
if (i + 1 < FItems.Count) and (Fitems[i]^.Segment = Fitems[i + 1]^.Segment) then
|
||
|
Size := frxInteger(FItems[i + 1]^.Offset) - frxInteger(FItems[i]^.Offset)
|
||
|
else
|
||
|
Size := TStream(FCacheStreamList[FItems[i]^.Segment]).Size - frxInteger(FItems[i]^.Offset);
|
||
|
xi := Item.Add;
|
||
|
xi.Name := 'item';
|
||
|
xi.Text := 'stream="' + frxStreamToString(TStream(FCacheStreamList[FItems[i]^.Segment]), Size) + '"';
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TfrxPictureCache.SetTempDir(const Value: String);
|
||
|
begin
|
||
|
if FCacheStreamList.Count = 0 then
|
||
|
FTempDir := Value;
|
||
|
end;
|
||
|
|
||
|
procedure TfrxPictureCache.SetUseFileCache(const Value: Boolean);
|
||
|
begin
|
||
|
if FCacheStreamList.Count = 0 then
|
||
|
FUseFileCache := Value;
|
||
|
end;
|
||
|
|
||
|
procedure TfrxPictureCache.AddSegment;
|
||
|
var
|
||
|
Stream: TStream;
|
||
|
Path: WideString;
|
||
|
//FileName: WideString;
|
||
|
begin
|
||
|
if FUseFileCache then
|
||
|
begin
|
||
|
Path := GetTempFile;
|
||
|
FTempFile.Add(String(Path));
|
||
|
Stream := TfrxFileStream.Create(String(Path), fmOpenReadWrite);
|
||
|
TfrxFileStream(Stream).FSz := 0;
|
||
|
end
|
||
|
else
|
||
|
begin
|
||
|
Stream := TfrxMemoryStream.Create;
|
||
|
TfrxMemoryStream(Stream).FSz := 0;
|
||
|
end;
|
||
|
FCacheStreamList.Add(Stream);
|
||
|
end;
|
||
|
|
||
|
function TfrxMemoryStream.Seek(Offset: Longint; Origin: Word): Longint;
|
||
|
begin
|
||
|
if (FSz <> 0) and (Offset = 0) and (Origin = soFromEnd) then
|
||
|
Result := FSz
|
||
|
else
|
||
|
Result := inherited Seek(Offset, Origin);
|
||
|
end;
|
||
|
|
||
|
function TfrxMemoryStream.Seek(const Offset: Int64;
|
||
|
Origin: TSeekOrigin): Int64;
|
||
|
begin
|
||
|
if (FSz <> 0) and (Offset = 0) and (Origin = soEnd) then
|
||
|
Result := FSz
|
||
|
else
|
||
|
Result := inherited Seek(Offset, Origin);
|
||
|
end;
|
||
|
|
||
|
function TfrxFileStream.Seek(Offset: Longint; Origin: Word): Longint;
|
||
|
begin
|
||
|
if (FSz <> 0) and (Offset = 0) and (Origin = soFromEnd) then
|
||
|
Result := FSz
|
||
|
else
|
||
|
Result := inherited Seek(Offset, Origin);
|
||
|
end;
|
||
|
|
||
|
function TfrxFileStream.Seek(const Offset: Int64;
|
||
|
Origin: TSeekOrigin): Int64;
|
||
|
begin
|
||
|
if (FSz <> 0) and (Offset = 0) and (Origin = soEnd) then
|
||
|
Result := FSz
|
||
|
else
|
||
|
Result := inherited Seek(Offset, Origin);
|
||
|
end;
|
||
|
|
||
|
{ TfrxCacheList }
|
||
|
|
||
|
function TfrxCacheList.Add: PfrxCacheItem;
|
||
|
begin
|
||
|
GetMem(Result, sizeof(TfrxCacheItem));
|
||
|
FItems.Add(Result);
|
||
|
end;
|
||
|
|
||
|
procedure TfrxCacheList.Clear;
|
||
|
var
|
||
|
idx: Integer;
|
||
|
begin
|
||
|
for idx := 0 to FItems.Count - 1 do
|
||
|
FreeMem(FItems[idx], sizeof(TfrxCacheItem));
|
||
|
FItems.Clear;
|
||
|
end;
|
||
|
|
||
|
function TfrxCacheList.Count: Integer;
|
||
|
begin
|
||
|
Result := FItems.Count;
|
||
|
end;
|
||
|
|
||
|
constructor TfrxCacheList.Create;
|
||
|
begin
|
||
|
FItems := TList.Create;
|
||
|
end;
|
||
|
|
||
|
destructor TfrxCacheList.Destroy;
|
||
|
begin
|
||
|
Clear;
|
||
|
FItems.Free;
|
||
|
inherited;
|
||
|
end;
|
||
|
|
||
|
function TfrxCacheList.Get(Index: Integer): PfrxCacheItem;
|
||
|
begin
|
||
|
Result := PfrxCacheItem(FItems[Index]);
|
||
|
end;
|
||
|
|
||
|
end.
|