mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-22 09:23:56 +02:00
693 lines
20 KiB
ObjectPascal
693 lines
20 KiB
ObjectPascal
{
|
|
*****************************************************************************
|
|
* *
|
|
* This file is part of the Lazarus Component Library (LCL) *
|
|
* *
|
|
* See the file COPYING.LCL, included in this distribution, *
|
|
* for details about the copyright. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
|
|
* *
|
|
*****************************************************************************
|
|
}
|
|
unit GtkFontCache;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, FPCAdds, LCLProc, LCLType, AvgLvlTree, gdk, gtkdef;
|
|
|
|
type
|
|
TResourceCache = class;
|
|
TResourceCacheDescriptor = class;
|
|
|
|
{ TResourceCacheItem }
|
|
|
|
TResourceCacheItem = class
|
|
private
|
|
FReferenceCount: integer;
|
|
public
|
|
Handle: THandle;
|
|
Cache: TResourceCache;
|
|
FirstDescriptor, LastDescriptor: TResourceCacheDescriptor;
|
|
Next, Prev: TResourceCacheItem;
|
|
constructor Create(TheCache: TResourceCache; TheHandle: THandle);
|
|
destructor Destroy; override;
|
|
procedure IncreaseRefCount;
|
|
procedure DecreaseRefCount;
|
|
procedure AddToList(var First, Last: TResourceCacheItem);
|
|
procedure RemoveFromList(var First, Last: TResourceCacheItem);
|
|
procedure WarnReferenceHigh; virtual;
|
|
public
|
|
property ReferenceCount: integer read FReferenceCount;
|
|
end;
|
|
TResourceCacheItemClass = class of TResourceCacheItem;
|
|
|
|
|
|
{ TResourceCacheDescriptor }
|
|
|
|
TResourceCacheDescriptor = class
|
|
public
|
|
Item: TResourceCacheItem;
|
|
Cache: TResourceCache;
|
|
Next, Prev: TResourceCacheDescriptor;
|
|
constructor Create(TheCache: TResourceCache; TheItem: TResourceCacheItem);
|
|
destructor Destroy; override;
|
|
procedure AddToList(var First, Last: TResourceCacheDescriptor);
|
|
procedure RemoveFromList(var First, Last: TResourceCacheDescriptor);
|
|
end;
|
|
TResourceCacheDescriptorClass = class of TResourceCacheDescriptor;
|
|
|
|
|
|
{ TResourceCache }
|
|
|
|
TResourceCache = class
|
|
protected
|
|
FItems: TAvgLvlTree;
|
|
FDescriptors: TAvgLvlTree;
|
|
FDestroying: boolean;
|
|
FResourceCacheDescriptorClass: TResourceCacheDescriptorClass;
|
|
FResourceCacheItemClass: TResourceCacheItemClass;
|
|
FMaxUnusedItem: integer; // how many freed resources to keep
|
|
FFirstUnusedItem, FLastUnusedItem: TResourceCacheItem;
|
|
FUnUsedItemCount: integer;
|
|
procedure RemoveItem(Item: TResourceCacheItem); virtual;
|
|
procedure RemoveDescriptor(Desc: TResourceCacheDescriptor); virtual;
|
|
procedure ItemUsed(Item: TResourceCacheItem);
|
|
procedure ItemUnused(Item: TResourceCacheItem);
|
|
function ItemIsUsed(Item: TResourceCacheItem): boolean;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
function CompareItems(Tree: TAvgLvlTree; Item1, Item2: Pointer): integer; virtual;
|
|
function CompareDescriptors(Tree: TAvgLvlTree; Desc1, Desc2: Pointer): integer; virtual; abstract;
|
|
procedure ConsistencyCheck;
|
|
public
|
|
property MaxUnusedItem: integer read FMaxUnusedItem
|
|
write FMaxUnusedItem;
|
|
property ResourceCacheItemClass: TResourceCacheItemClass
|
|
read FResourceCacheItemClass;
|
|
property ResourceCacheDescriptorClass: TResourceCacheDescriptorClass
|
|
read FResourceCacheDescriptorClass;
|
|
end;
|
|
|
|
|
|
{ THandleResourceCache }
|
|
|
|
THandleResourceCache = class(TResourceCache)
|
|
public
|
|
function FindItem(Handle: THandle): TResourceCacheItem;
|
|
end;
|
|
|
|
|
|
{ TBlockResourceCacheDescriptor }
|
|
|
|
TBlockResourceCacheDescriptor = class(TResourceCacheDescriptor)
|
|
public
|
|
Data: Pointer;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
|
|
{ TBlockResourceCache }
|
|
|
|
TBlockResourceCache = class(THandleResourceCache)
|
|
private
|
|
FDataSize: integer;
|
|
protected
|
|
FOnCompareDescPtrWithDescriptor: TListSortCompare;
|
|
public
|
|
constructor Create(TheDataSize: integer);
|
|
function FindDescriptor(DescPtr: Pointer): TBlockResourceCacheDescriptor;
|
|
function AddResource(Handle: THandle; DescPtr: Pointer
|
|
): TBlockResourceCacheDescriptor;
|
|
function CompareDescriptors(Tree: TAvgLvlTree;
|
|
Desc1, Desc2: Pointer): integer; override;
|
|
public
|
|
property DataSize: integer read FDataSize;
|
|
property OnCompareDescPtrWithDescriptor: TListSortCompare
|
|
read FOnCompareDescPtrWithDescriptor;
|
|
end;
|
|
|
|
function ComparePHandleWithResourceCacheItem(HandlePtr: PHandle;
|
|
Item: TResourceCacheItem): integer;
|
|
function CompareDescPtrWithBlockResDesc(DescPtr: Pointer;
|
|
Item: TBlockResourceCacheDescriptor): integer;
|
|
|
|
|
|
type
|
|
TGdkFontCacheDescriptor = class;
|
|
|
|
{ TGdkFontCacheItem }
|
|
|
|
TGdkFontCacheItem = class(TResourceCacheItem)
|
|
public
|
|
GdkFont: PGDKFont;
|
|
|
|
// metrics
|
|
MetricsValid: boolean;
|
|
lBearing: LongInt;
|
|
rBearing: LongInt;
|
|
TextMetric: TTextMetric;
|
|
IsDoubleByteChar: boolean;
|
|
procedure WarnReferenceHigh; override;
|
|
end;
|
|
|
|
|
|
{ TGdkFontCacheDescriptor }
|
|
|
|
TGdkFontCacheDescriptor = class(TResourceCacheDescriptor)
|
|
public
|
|
LogFont: TLogFont;
|
|
LongFontName: string;
|
|
xlfd: string;
|
|
end;
|
|
|
|
|
|
{ TGdkFontCache }
|
|
|
|
TGdkFontCache = class(TResourceCache)
|
|
protected
|
|
procedure RemoveItem(Item: TResourceCacheItem); override;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
function CompareItems(Tree: TAvgLvlTree; Item1, Item2: Pointer): integer; override;
|
|
function CompareDescriptors(Tree: TAvgLvlTree; Desc1, Desc2: Pointer): integer; override;
|
|
function FindGDKFont(TheGdkFont: PGDKFont): TGdkFontCacheItem;
|
|
function FindGDKFontDesc(const LogFont: TLogFont;
|
|
const LongFontName: string): TGdkFontCacheDescriptor;
|
|
function Add(TheGdkFont: PGDKFont; const LogFont: TLogFont;
|
|
const LongFontName: string): TGdkFontCacheDescriptor;
|
|
procedure Reference(TheGdkFont: PGDKFont);
|
|
procedure Unreference(TheGdkFont: PGDKFont);
|
|
procedure DumpDescriptors;
|
|
end;
|
|
|
|
function LogFontToString(const LogFont: TLogFont): string;
|
|
|
|
var
|
|
FontCache: TGdkFontCache;
|
|
|
|
implementation
|
|
|
|
type
|
|
TLogFontAndName = record
|
|
LogFont: TLogFont;
|
|
LongFontName: string;
|
|
end;
|
|
PLogFontAndName = ^TLogFontAndName;
|
|
|
|
function ComparePHandleWithResourceCacheItem(HandlePtr: PHandle;
|
|
Item: TResourceCacheItem): integer;
|
|
begin
|
|
Result:=CompareHandles(HandlePtr^,Item.Handle);
|
|
end;
|
|
|
|
function CompareDescPtrWithBlockResDesc(DescPtr: Pointer;
|
|
Item: TBlockResourceCacheDescriptor): integer;
|
|
begin
|
|
Result:=CompareMemRange(DescPtr,Item.Data,
|
|
TBlockResourceCache(Item.Cache).DataSize);
|
|
end;
|
|
|
|
function LogFontToString(const LogFont: TLogFont): string;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result:=''
|
|
+' lfFaceName="'+LogFont.lfFaceName+'" '
|
|
+' CharSet='+dbgs(LogFont.lfCharSet)
|
|
+' ClipPrecision='+dbgs(LogFont.lfClipPrecision)
|
|
+' Escapement='+dbgs(LogFont.lfEscapement)
|
|
+' Height='+dbgs(LogFont.lfHeight)
|
|
+' Italic='+dbgs(LogFont.lfItalic)
|
|
+' Orientation='+dbgs(LogFont.lfOrientation)
|
|
+' OutPrecision='+dbgs(LogFont.lfOutPrecision)
|
|
+' PitchAndFamily='+dbgs(LogFont.lfPitchAndFamily)
|
|
+' Quality='+dbgs(LogFont.lfQuality)
|
|
+' StrikeOut='+dbgs(LogFont.lfStrikeOut)
|
|
+' Underline='+dbgs(LogFont.lfUnderline)
|
|
+' Weight='+dbgs(LogFont.lfWeight)
|
|
+' Width='+dbgs(LogFont.lfWidth)
|
|
+#13#10;
|
|
for i:=0 to SizeOf(LogFont)-1 do
|
|
Result:=Result+hexstr(ord(PChar(@LogFont)[i]),2);
|
|
Result:=Result+#13#10;
|
|
end;
|
|
|
|
|
|
{ TResourceCacheItem }
|
|
|
|
constructor TResourceCacheItem.Create(TheCache: TResourceCache;
|
|
TheHandle: THandle);
|
|
begin
|
|
Cache:=TheCache;
|
|
Handle:=TheHandle;
|
|
end;
|
|
|
|
destructor TResourceCacheItem.Destroy;
|
|
begin
|
|
Cache.RemoveItem(Self);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TResourceCacheItem.IncreaseRefCount;
|
|
begin
|
|
inc(FReferenceCount);
|
|
if FReferenceCount=1 then
|
|
Cache.ItemUsed(Self);
|
|
if (FReferenceCount=100) or (FReferenceCount=1000) then
|
|
WarnReferenceHigh;
|
|
end;
|
|
|
|
procedure TResourceCacheItem.DecreaseRefCount;
|
|
begin
|
|
if FReferenceCount=0 then
|
|
RaiseGDBException('TResourceCacheItem.DecreaseRefCount=0');
|
|
dec(FReferenceCount);
|
|
if FReferenceCount=0 then
|
|
Cache.ItemUnused(Self);
|
|
end;
|
|
|
|
procedure TResourceCacheItem.AddToList(var First, Last: TResourceCacheItem
|
|
);
|
|
// add as last
|
|
begin
|
|
Next:=nil;
|
|
Prev:=Last;
|
|
Last:=Self;
|
|
if First=nil then First:=Self;
|
|
if Prev<>nil then Prev.Next:=Self;
|
|
end;
|
|
|
|
procedure TResourceCacheItem.RemoveFromList(var First,Last: TResourceCacheItem);
|
|
begin
|
|
if First=Self then First:=Next;
|
|
if Last=Self then Last:=Prev;
|
|
if Next<>nil then Next.Prev:=Prev;
|
|
if Prev<>nil then Prev.Next:=Next;
|
|
Next:=nil;
|
|
Prev:=nil;
|
|
end;
|
|
|
|
procedure TResourceCacheItem.WarnReferenceHigh;
|
|
begin
|
|
debugln('WARNING: TResourceCacheItem.IncreaseRefCount ',dbgs(FReferenceCount));
|
|
end;
|
|
|
|
{ TResourceCacheDescriptor }
|
|
|
|
constructor TResourceCacheDescriptor.Create(TheCache: TResourceCache;
|
|
TheItem: TResourceCacheItem);
|
|
begin
|
|
Cache:=TheCache;
|
|
Item:=TheItem;
|
|
Item.IncreaseRefCount;
|
|
AddToList(Item.FirstDescriptor,Item.LastDescriptor);
|
|
end;
|
|
|
|
destructor TResourceCacheDescriptor.Destroy;
|
|
begin
|
|
Cache.RemoveDescriptor(Self);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TResourceCacheDescriptor.AddToList(
|
|
var First, Last: TResourceCacheDescriptor);
|
|
// add as last
|
|
begin
|
|
Next:=nil;
|
|
Prev:=Last;
|
|
Last:=Self;
|
|
if First=nil then First:=Self;
|
|
if Prev<>nil then Prev.Next:=Self;
|
|
end;
|
|
|
|
procedure TResourceCacheDescriptor.RemoveFromList(
|
|
var First, Last: TResourceCacheDescriptor);
|
|
begin
|
|
if First=Self then First:=Next;
|
|
if Last=Self then Last:=Prev;
|
|
if Next<>nil then Next.Prev:=Prev;
|
|
if Prev<>nil then Prev.Next:=Next;
|
|
Next:=nil;
|
|
Prev:=nil;
|
|
end;
|
|
|
|
{ TResourceCache }
|
|
|
|
procedure TResourceCache.RemoveItem(Item: TResourceCacheItem);
|
|
begin
|
|
if FDestroying then exit;
|
|
while Item.FirstDescriptor<>nil do Item.FirstDescriptor.Free;
|
|
FItems.Remove(Item);
|
|
end;
|
|
|
|
procedure TResourceCache.RemoveDescriptor(Desc: TResourceCacheDescriptor);
|
|
begin
|
|
if FDestroying then exit;
|
|
Desc.RemoveFromList(Desc.Item.FirstDescriptor,Desc.Item.LastDescriptor);
|
|
FDescriptors.Remove(Desc);
|
|
if Desc.Item.FirstDescriptor=nil then
|
|
Desc.Item.Free;
|
|
end;
|
|
|
|
procedure TResourceCache.ItemUsed(Item: TResourceCacheItem);
|
|
// called after creation or when Item is used again
|
|
begin
|
|
if not ItemIsUsed(Item) then begin
|
|
Item.RemoveFromList(FFirstUnusedItem,FLastUnusedItem);
|
|
dec(FUnUsedItemCount);
|
|
end;
|
|
end;
|
|
|
|
procedure TResourceCache.ItemUnused(Item: TResourceCacheItem);
|
|
// called when Item is not used any more
|
|
begin
|
|
if not ItemIsUsed(Item) then
|
|
raise Exception.Create('TResourceCache.ItemUnused');
|
|
Item.AddToList(FFirstUnusedItem,FLastUnusedItem);
|
|
inc(FUnUsedItemCount);
|
|
if FUnUsedItemCount>FMaxUnusedItem then
|
|
// maximum unused resources reached -> free the oldest
|
|
FFirstUnusedItem.Free;
|
|
end;
|
|
|
|
function TResourceCache.ItemIsUsed(Item: TResourceCacheItem): boolean;
|
|
begin
|
|
Result:=(FFirstUnusedItem<>Item) and (Item.Next=nil)
|
|
and (Item.Prev=nil)
|
|
end;
|
|
|
|
constructor TResourceCache.Create;
|
|
begin
|
|
FMaxUnusedItem:=100;
|
|
FItems:=TAvgLvlTree.CreateObjectCompare(@CompareItems);
|
|
FDescriptors:=TAvgLvlTree.CreateObjectCompare(@CompareDescriptors);
|
|
FResourceCacheItemClass:=TResourceCacheItem;
|
|
FResourceCacheDescriptorClass:=TResourceCacheDescriptor;
|
|
end;
|
|
|
|
destructor TResourceCache.Destroy;
|
|
begin
|
|
FDestroying:=true;
|
|
FItems.FreeAndClear;
|
|
FItems.Free;
|
|
FItems:=nil;
|
|
FDescriptors.FreeAndClear;
|
|
FDescriptors.Free;
|
|
FDescriptors:=nil;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TResourceCache.CompareItems(Tree: TAvgLvlTree; Item1, Item2: Pointer
|
|
): integer;
|
|
begin
|
|
Result:=CompareHandles(TResourceCacheItem(Item1).Handle,
|
|
TResourceCacheItem(Item2).Handle);
|
|
end;
|
|
|
|
procedure TResourceCache.ConsistencyCheck;
|
|
var
|
|
ANode: TAvgLvlTreeNode;
|
|
Item: TResourceCacheItem;
|
|
begin
|
|
if (FFirstUnusedItem=nil) xor (FLastUnusedItem=nil) then
|
|
RaiseGDBException('');
|
|
|
|
// check items
|
|
ANode:=FItems.FindLowest;
|
|
while ANode<>nil do begin
|
|
Item:=TResourceCacheItem(ANode.Data);
|
|
if Item.FirstDescriptor=nil then
|
|
RaiseGDBException('');
|
|
if Item.LastDescriptor=nil then
|
|
RaiseGDBException('');
|
|
ANode:=FItems.FindSuccessor(ANode);
|
|
end;
|
|
end;
|
|
|
|
{ THandleResourceCache }
|
|
|
|
function THandleResourceCache.FindItem(Handle: THandle): TResourceCacheItem;
|
|
var
|
|
ANode: TAvgLvlTreeNode;
|
|
begin
|
|
ANode:=FItems.FindKey(@Handle,@ComparePHandleWithResourceCacheItem);
|
|
if ANode<>nil then
|
|
Result:=TResourceCacheItem(ANode.Data)
|
|
else
|
|
Result:=nil;
|
|
end;
|
|
|
|
{ TBlockResourceCache }
|
|
|
|
constructor TBlockResourceCache.Create(TheDataSize: integer);
|
|
begin
|
|
inherited Create;
|
|
FDataSize:=DataSize;
|
|
FResourceCacheDescriptorClass:=TBlockResourceCacheDescriptor;
|
|
FOnCompareDescPtrWithDescriptor:=@CompareDescPtrWithBlockResDesc;
|
|
end;
|
|
|
|
function TBlockResourceCache.FindDescriptor(DescPtr: Pointer
|
|
): TBlockResourceCacheDescriptor;
|
|
var
|
|
ANode: TAvgLvlTreeNode;
|
|
begin
|
|
ANode:=FDescriptors.FindKey(DescPtr,FOnCompareDescPtrWithDescriptor);
|
|
if ANode<>nil then
|
|
Result:=TBlockResourceCacheDescriptor(ANode.Data)
|
|
else
|
|
Result:=nil;
|
|
end;
|
|
|
|
function TBlockResourceCache.AddResource(Handle: THandle; DescPtr: Pointer
|
|
): TBlockResourceCacheDescriptor;
|
|
var
|
|
Item: TResourceCacheItem;
|
|
|
|
procedure RaiseDescriptorAlreadyAdded;
|
|
var
|
|
Msg: String;
|
|
i: Integer;
|
|
begin
|
|
Msg:='TBlockResourceCache.AddResource Descriptor Already Added '#13;
|
|
for i:=0 to DataSize-1 do
|
|
Msg:=Msg+hexstr(ord(PChar(DescPtr)[i]),2);
|
|
raise Exception.Create(Msg);
|
|
end;
|
|
|
|
begin
|
|
Result:=FindDescriptor(DescPtr);
|
|
if Result<>nil then
|
|
RaiseDescriptorAlreadyAdded;
|
|
|
|
Item:=FindItem(Handle);
|
|
if Item=nil then begin
|
|
Item:=FResourceCacheItemClass.Create(Self,Handle);
|
|
FItems.Add(Item);
|
|
end;
|
|
Result:=TBlockResourceCacheDescriptor(
|
|
FResourceCacheDescriptorClass.Create(Self,Item));
|
|
ReAllocMem(Result.Data,DataSize);
|
|
System.Move(DescPtr^,Result.Data^,DataSize);
|
|
FDescriptors.Add(Result);
|
|
end;
|
|
|
|
function TBlockResourceCache.CompareDescriptors(Tree: TAvgLvlTree; Desc1,
|
|
Desc2: Pointer): integer;
|
|
begin
|
|
Result:=CompareMemRange(TBlockResourceCacheDescriptor(Desc1).Data,
|
|
TBlockResourceCacheDescriptor(Desc2).Data,
|
|
DataSize);
|
|
end;
|
|
|
|
{ TBlockResourceCacheDescriptor }
|
|
|
|
destructor TBlockResourceCacheDescriptor.Destroy;
|
|
begin
|
|
ReAllocMem(Data,0);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
{ TGdkFontCache }
|
|
|
|
function CompareGdkFontWithResItem(Font: PGDKFont;
|
|
Item: TGdkFontCacheItem): integer;
|
|
begin
|
|
Result:=ComparePointers(Font,Item.GdkFont);
|
|
end;
|
|
|
|
function CompareLogFontAndNameWithResDesc(Key: PLogFontAndName;
|
|
Desc: TGdkFontCacheDescriptor): integer;
|
|
begin
|
|
Result:=CompareStr(Key^.LongFontName,Desc.LongFontName);
|
|
//writeln('CompareLogFontAndNameWithResDesc A ',Key^.LongFontName,' ',Desc.LongFontName,' ',HexStr(Cardinal(Desc),8),' Result=',Result);
|
|
if Result=0 then
|
|
Result:=CompareMemRange(@Key^.LogFont,@Desc.LogFont,SizeOf(Desc.LogFont));
|
|
//writeln('CompareLogFontAndNameWithResDesc END Result=',Result);
|
|
end;
|
|
|
|
procedure TGdkFontCache.RemoveItem(Item: TResourceCacheItem);
|
|
begin
|
|
gdk_font_unref(TGdkFontCacheItem(Item).GdkFont);
|
|
inherited RemoveItem(Item);
|
|
end;
|
|
|
|
constructor TGdkFontCache.Create;
|
|
begin
|
|
inherited Create;
|
|
FResourceCacheItemClass:=TGdkFontCacheItem;
|
|
FResourceCacheDescriptorClass:=TGdkFontCacheDescriptor;
|
|
end;
|
|
|
|
destructor TGdkFontCache.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TGdkFontCache.CompareItems(Tree: TAvgLvlTree; Item1, Item2: Pointer
|
|
): integer;
|
|
begin
|
|
Result:=ComparePointers(TGdkFontCacheItem(Item1).GdkFont,
|
|
TGdkFontCacheItem(Item2).GdkFont);
|
|
end;
|
|
|
|
function TGdkFontCache.CompareDescriptors(Tree: TAvgLvlTree; Desc1,
|
|
Desc2: Pointer): integer;
|
|
var
|
|
Descriptor1: TGdkFontCacheDescriptor;
|
|
Descriptor2: TGdkFontCacheDescriptor;
|
|
begin
|
|
Descriptor1:=TGdkFontCacheDescriptor(Desc1);
|
|
Descriptor2:=TGdkFontCacheDescriptor(Desc2);
|
|
Result:=CompareStr(Descriptor1.LongFontName,Descriptor2.LongFontName);
|
|
if Result<>0 then exit;
|
|
Result:=CompareMemRange(@Descriptor1.LogFont,@Descriptor2.LogFont,
|
|
SizeOf(Descriptor1.LogFont));
|
|
end;
|
|
|
|
function TGdkFontCache.FindGDKFont(TheGdkFont: PGDKFont): TGdkFontCacheItem;
|
|
var
|
|
ANode: TAvgLvlTreeNode;
|
|
begin
|
|
ANode:=FItems.Findkey(TheGdkFont,@CompareGdkFontWithResItem);
|
|
if ANode<>nil then
|
|
Result:=TGdkFontCacheItem(ANode.Data)
|
|
else
|
|
Result:=nil;
|
|
end;
|
|
|
|
function TGdkFontCache.FindGDKFontDesc(const LogFont: TLogFont;
|
|
const LongFontName: string): TGdkFontCacheDescriptor;
|
|
var
|
|
LogFontAndName: TLogFontAndName;
|
|
ANode: TAvgLvlTreeNode;
|
|
begin
|
|
LogFontAndName.LogFont:=LogFont;
|
|
LogFontAndName.LongFontName:=LongFontName;
|
|
ANode:=FDescriptors.Findkey(@LogFontAndName,
|
|
@CompareLogFontAndNameWithResDesc);
|
|
if ANode<>nil then
|
|
Result:=TGdkFontCacheDescriptor(ANode.Data)
|
|
else
|
|
Result:=nil;
|
|
end;
|
|
|
|
function TGdkFontCache.Add(TheGdkFont: PGDKFont; const LogFont: TLogFont;
|
|
const LongFontName: string): TGdkFontCacheDescriptor;
|
|
var
|
|
Item: TGdkFontCacheItem;
|
|
begin
|
|
if FindGDKFontDesc(LogFont,LongFontName)<>nil then
|
|
RaiseGDBException('TGdkFontCache.Add font desc added twice');
|
|
|
|
// find cache item with TheGdkFont
|
|
Item:=FindGDKFont(TheGdkFont);
|
|
if Item=nil then begin
|
|
// create new item
|
|
Item:=TGdkFontCacheItem.Create(Self,0);
|
|
Item.GdkFont:=TheGdkFont;
|
|
gdk_font_ref(TheGdkFont);
|
|
FItems.Add(Item);
|
|
end;
|
|
if FindGDKFont(TheGdkFont)=nil then
|
|
RaiseGDBException('');
|
|
|
|
// create descriptor
|
|
Result:=TGdkFontCacheDescriptor.Create(Self,Item);
|
|
Result.LongFontName:=LongFontName;
|
|
Result.LogFont:=LogFont;
|
|
FDescriptors.Add(Result);
|
|
if FindGDKFontDesc(LogFont,LongFontName)=nil then begin
|
|
debugln('TGdkFontCache.Add Added: ',HexStr(Cardinal(Result),8),' LongFontName=',Result.LongFontName,' ',LogFontToString(Result.LogFont));
|
|
DumpDescriptors;
|
|
RaiseGDBException('');
|
|
end;
|
|
end;
|
|
|
|
procedure TGdkFontCache.Reference(TheGdkFont: PGDKFont);
|
|
var
|
|
Item: TGdkFontCacheItem;
|
|
begin
|
|
Item:=FindGDKFont(TheGdkFont);
|
|
if Item=nil then
|
|
gdk_font_ref(TheGdkFont)
|
|
else
|
|
Item.IncreaseRefCount;
|
|
end;
|
|
|
|
procedure TGdkFontCache.Unreference(TheGdkFont: PGDKFont);
|
|
var
|
|
Item: TGdkFontCacheItem;
|
|
begin
|
|
Item:=FindGDKFont(TheGdkFont);
|
|
if Item=nil then
|
|
gdk_font_unref(TheGdkFont)
|
|
else
|
|
Item.DecreaseRefCount;
|
|
end;
|
|
|
|
procedure TGdkFontCache.DumpDescriptors;
|
|
var
|
|
ANode: TAvgLvlTreeNode;
|
|
Desc: TGdkFontCacheDescriptor;
|
|
i: Integer;
|
|
begin
|
|
ANode:=FDescriptors.FindLowest;
|
|
i:=1;
|
|
while ANode<>nil do begin
|
|
Desc:=TGdkFontCacheDescriptor(ANode.Data);
|
|
debugln('TGdkFontCache.DumpDescriptors ',dbgs(i),' ',HexStr(Cardinal(Desc),8),' ',Desc.LongFontName,' ',LogFontToString(Desc.LogFont));
|
|
ANode:=FDescriptors.FindSuccessor(ANode);
|
|
inc(i);
|
|
end;
|
|
end;
|
|
|
|
{ TGdkFontCacheItem }
|
|
|
|
procedure TGdkFontCacheItem.WarnReferenceHigh;
|
|
begin
|
|
inherited WarnReferenceHigh;
|
|
debugln(' GdkFont='+HexStr(Cardinal(GdkFont),8));
|
|
if FirstDescriptor<>nil then
|
|
debugln(' '+TGdkFontCacheDescriptor(FirstDescriptor).LongFontName
|
|
+' '+LogFontToString(TGdkFontCacheDescriptor(FirstDescriptor).LogFont));
|
|
end;
|
|
|
|
initialization
|
|
FontCache:=TGdkFontCache.Create;
|
|
|
|
finalization
|
|
FontCache.Free;
|
|
FontCache:=nil;
|
|
|
|
end.
|
|
|