mirror of
https://github.com/HeidiSQL/HeidiSQL.git
synced 2025-08-14 10:02:10 +08:00

* Removes TNT Unicode controls, which are no longer required. All VCL controls now have native Unicode support. * Remove Delphi 11 packages, otherwise we would either need to keep TNT or break Unicode * PngComponents update from Uwe Raabe on http://cc.embarcadero.com/Item/26127 * Adjust auto build process * Since Delphi 2009, Strings are now UnicodeStrings, not AnsiStrings any longer. Fix a bunch of compiler errors which came along with this change. TODO: Project should compile but give tons of compiler warnings.
1270 lines
35 KiB
ObjectPascal
1270 lines
35 KiB
ObjectPascal
unit PngImageList;
|
|
|
|
{$I compilers.inc}
|
|
|
|
{$IF RTLVersion < 15.0 }
|
|
This unit only compiles with Delphi 7 and higher!
|
|
{$IFEND}
|
|
|
|
interface
|
|
|
|
uses
|
|
Windows, Classes, SysUtils, Controls, Graphics, ImgList, PngImage,
|
|
PngFunctions;
|
|
|
|
type
|
|
TPngImageCollection = class;
|
|
TPngImageCollectionItem = class;
|
|
TPngImageCollectionItems = class;
|
|
|
|
TPngImageList = class(TImageList)
|
|
private
|
|
FEnabledImages: Boolean;
|
|
FLocked: Integer;
|
|
FPngImages: TPngImageCollectionItems;
|
|
FPngOptions: TPngOptions;
|
|
function GetHeight: Integer;
|
|
function GetWidth: Integer;
|
|
procedure SetHeight(const Value: Integer);
|
|
procedure SetPngOptions(const Value: TPngOptions);
|
|
procedure SetWidth(const Value: Integer);
|
|
protected
|
|
procedure AssignTo(Dest: TPersistent); override;
|
|
procedure CopyPngs; virtual;
|
|
procedure DoDraw(Index: Integer; Canvas: TCanvas; X, Y: Integer; Style: Cardinal; Enabled: Boolean = True); override;
|
|
procedure InternalInsertPng(Index: Integer; const Png: TPngImage; Background:
|
|
TColor = clNone);
|
|
procedure InternalAddPng(const Png: TPngImage; Background: TColor = clNone);
|
|
function PngToIcon(const Png: TPngImage; Background: TColor = clNone): HICON;
|
|
procedure ReadData(Stream: TStream); override;
|
|
procedure SetEnabledImages(const Value: Boolean); virtual;
|
|
procedure SetPngImages(const Value: TPngImageCollectionItems); virtual;
|
|
procedure WriteData(Stream: TStream); override;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
//Patched methods
|
|
function Add(Image, Mask: TBitmap): Integer; virtual;
|
|
function AddIcon(Image: TIcon): Integer; virtual;
|
|
function AddPng(Image: TPngImage; Background: TColor = clNone): Integer;
|
|
function AddImage(Value: TCustomImageList; Index: Integer): Integer; virtual;
|
|
procedure AddImages(Value: TCustomImageList); virtual;
|
|
function AddMasked(Image: TBitmap; MaskColor: TColor): Integer; virtual;
|
|
procedure BeginUpdate;
|
|
procedure Clear; virtual;
|
|
procedure Delete(Index: Integer); virtual;
|
|
procedure EndUpdate(Update: Boolean = True);
|
|
procedure Insert(Index: Integer; Image, Mask: TBitmap); virtual;
|
|
procedure InsertIcon(Index: Integer; Image: TIcon); virtual;
|
|
procedure InsertPng(Index: Integer; Image: TPngImage; Background: TColor =
|
|
clNone);
|
|
procedure InsertMasked(Index: Integer; Image: TBitmap; MaskColor: TColor); virtual;
|
|
procedure Move(CurIndex, NewIndex: Integer); virtual;
|
|
procedure Replace(Index: Integer; Image, Mask: TBitmap); virtual;
|
|
procedure ReplaceIcon(Index: Integer; Image: TIcon); virtual;
|
|
procedure ReplaceMasked(Index: Integer; NewImage: TBitmap; MaskColor: TColor); virtual;
|
|
published
|
|
property EnabledImages: Boolean read FEnabledImages write SetEnabledImages default True;
|
|
property Height read GetHeight write SetHeight default 16;
|
|
property PngImages: TPngImageCollectionItems read FPngImages write SetPngImages;
|
|
property PngOptions: TPngOptions read FPngOptions write SetPngOptions default [pngBlendOnDisabled];
|
|
property Width read GetWidth write SetWidth default 16;
|
|
end;
|
|
|
|
TPngImageCollection = class(TComponent)
|
|
private
|
|
FItems: TPngImageCollectionItems;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
published
|
|
property Items: TPngImageCollectionItems read FItems write FItems;
|
|
end;
|
|
|
|
TPngImageCollectionItems = class(TCollection)
|
|
private
|
|
FOwner: TPersistent;
|
|
function GetItem(Index: Integer): TPngImageCollectionItem;
|
|
procedure SetItem(Index: Integer; const Value: TPngImageCollectionItem);
|
|
protected
|
|
function GetOwner: TPersistent; override;
|
|
procedure Update(Item: TCollectionItem); override;
|
|
public
|
|
constructor Create(AOwner: TPersistent);
|
|
function Add(DontCreatePNG: Boolean = False): TPngImageCollectionItem; reintroduce;
|
|
procedure Assign(Source: TPersistent); override;
|
|
function Insert(Index: Integer; DontCreatePNG: Boolean = False): TPngImageCollectionItem; reintroduce;
|
|
property Items[index: Integer]: TPngImageCollectionItem read GetItem write SetItem; default;
|
|
end;
|
|
|
|
TPngImageCollectionItem = class(TCollectionItem)
|
|
private
|
|
FBackground: TColor;
|
|
FName: string;
|
|
FPngImage: TPngImage;
|
|
procedure SetBackground(const Value: TColor);
|
|
procedure SetPngImage(const Value: TPngImage);
|
|
protected
|
|
procedure AssignTo(Dest: TPersistent); override;
|
|
function GetDisplayName: string; override;
|
|
public
|
|
constructor Create(Collection: TCollection); overload; override;
|
|
constructor Create(Collection: TCollection; DontCreatePNG: Boolean = False); reintroduce; overload;
|
|
destructor Destroy; override;
|
|
procedure Assign(Source: TPersistent); override;
|
|
function Duplicate: TPngImage;
|
|
published
|
|
property Background: TColor read FBackground write SetBackground default clBtnFace;
|
|
property Name: string read FName write FName;
|
|
property PngImage: TPngImage read FPngImage write SetPngImage;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
Math, Contnrs, CommCtrl, ComCtrls;
|
|
|
|
var
|
|
ImageListCount: Integer = 0;
|
|
MethodPatches: TObjectList = nil;
|
|
|
|
type
|
|
TMethodPatch = class
|
|
private
|
|
Name: string;
|
|
OldBody: array[0..5] of Byte;
|
|
OldPointer, NewPointer: Pointer;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
procedure BeginInvokeOldMethod;
|
|
procedure FinishInvokeOldMethod;
|
|
function PatchBack: Boolean;
|
|
end;
|
|
|
|
{ Global }
|
|
|
|
function FindMethodPatch(const Name: string): TMethodPatch;
|
|
var
|
|
I: Integer;
|
|
begin
|
|
Result := nil;
|
|
for I := 0 to MethodPatches.Count - 1 do begin
|
|
if TMethodPatch(MethodPatches[I]).Name = Name then begin
|
|
Result := TMethodPatch(MethodPatches[I]);
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function PatchPtr(OldPtr, NewPtr: Pointer; const Name: string; Patch: TMethodPatch): Boolean;
|
|
var
|
|
Access: Cardinal;
|
|
begin
|
|
Result := False;
|
|
Patch.Name := Name;
|
|
if OldPtr <> NewPtr then begin
|
|
Patch.OldPointer := OldPtr;
|
|
Patch.NewPointer := NewPtr;
|
|
Move(PByte(OldPtr)^, Patch.OldBody[0], SizeOf(Patch.OldBody));
|
|
if VirtualProtect(OldPtr, 16, PAGE_EXECUTE_READWRITE, @Access) then begin
|
|
PByte(OldPtr)^ := $E9; // Near jump
|
|
PCardinal(Cardinal(OldPtr) + 1)^ := Cardinal(NewPtr) - Cardinal(OldPtr) - 5;
|
|
VirtualProtect(OldPtr, 16, Access, @Access);
|
|
Result := True;
|
|
end;
|
|
end;
|
|
if not Result then
|
|
Patch.OldPointer := nil;
|
|
end;
|
|
|
|
procedure ApplyMethodPatches;
|
|
type
|
|
TPointerCombo = record
|
|
OldPtr, NewPtr: Pointer;
|
|
Name: string;
|
|
end;
|
|
|
|
function Combo(OldPtr, NewPtr: Pointer; const Name: string): TPointerCombo;
|
|
begin
|
|
Result.OldPtr := OldPtr;
|
|
Result.NewPtr := NewPtr;
|
|
Result.Name := Name;
|
|
end;
|
|
|
|
const
|
|
EmptyCombo: TPointerCombo = (OldPtr: nil; NewPtr: nil; Name: '');
|
|
var
|
|
Pointers: array of TPointerCombo;
|
|
Patch: TMethodPatch;
|
|
I: Integer;
|
|
begin
|
|
if ImageListCount = 0 then begin
|
|
SetLength(Pointers, 14);
|
|
Pointers[0] := Combo(@TCustomImageList.Add, @TPngImageList.Add, 'Add');
|
|
Pointers[1] := Combo(@TCustomImageList.AddIcon, @TPngImageList.AddIcon, 'AddIcon');
|
|
Pointers[2] := Combo(@TCustomImageList.AddImage, @TPngImageList.AddImage, 'AddImage');
|
|
Pointers[3] := Combo(@TCustomImageList.AddImages, @TPngImageList.AddImages, 'AddImages');
|
|
Pointers[4] := Combo(@TCustomImageList.AddMasked, @TPngImageList.AddMasked, 'AddMasked');
|
|
Pointers[5] := Combo(@TCustomImageList.Clear, @TPngImageList.Clear, 'Clear');
|
|
Pointers[6] := Combo(@TCustomImageList.Delete, @TPngImageList.Delete, 'Delete');
|
|
Pointers[7] := Combo(@TCustomImageList.Insert, @TPngImageList.Insert, 'Insert');
|
|
Pointers[8] := Combo(@TCustomImageList.InsertIcon, @TPngImageList.InsertIcon, 'InsertIcon');
|
|
Pointers[9] := Combo(@TCustomImageList.InsertMasked, @TPngImageList.InsertMasked, 'InsertMasked');
|
|
Pointers[10] := Combo(@TCustomImageList.Move, @TPngImageList.Move, 'Move');
|
|
Pointers[11] := Combo(@TCustomImageList.Replace, @TPngImageList.Replace, 'Replace');
|
|
Pointers[12] := Combo(@TCustomImageList.ReplaceIcon, @TPngImageList.ReplaceIcon, 'ReplaceIcon');
|
|
Pointers[13] := Combo(@TCustomImageList.ReplaceMasked, @TPngImageList.ReplaceMasked, 'ReplaceMasked');
|
|
|
|
MethodPatches := TObjectList.Create;
|
|
for I := Low(Pointers) to High(Pointers) do begin
|
|
if Pointers[I].OldPtr <> nil then begin
|
|
Patch := TMethodPatch.Create;
|
|
if PatchPtr(Pointers[I].OldPtr, Pointers[I].NewPtr, Pointers[I].Name, Patch) then
|
|
MethodPatches.Add(Patch)
|
|
else
|
|
Patch.Free;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure RevertPatchedMethods;
|
|
begin
|
|
if ImageListCount = 0 then
|
|
FreeAndNil(MethodPatches);
|
|
end;
|
|
|
|
{ TMethodPatch }
|
|
|
|
constructor TMethodPatch.Create;
|
|
begin
|
|
inherited Create;
|
|
OldPointer := nil;
|
|
end;
|
|
|
|
destructor TMethodPatch.Destroy;
|
|
begin
|
|
if OldPointer <> nil then
|
|
PatchBack;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TMethodPatch.BeginInvokeOldMethod;
|
|
begin
|
|
PatchBack;
|
|
end;
|
|
|
|
procedure TMethodPatch.FinishInvokeOldMethod;
|
|
begin
|
|
PatchPtr(OldPointer, NewPointer, Name, Self);
|
|
end;
|
|
|
|
function TMethodPatch.PatchBack: Boolean;
|
|
var
|
|
Access: Cardinal;
|
|
begin
|
|
Result := False;
|
|
if VirtualProtect(OldPointer, 16, PAGE_EXECUTE_READWRITE, @Access) then begin
|
|
Move(OldBody[0], OldPointer^, SizeOf(OldBody));
|
|
VirtualProtect(OldPointer, 16, Access, @Access);
|
|
Result := True;
|
|
end;
|
|
end;
|
|
|
|
constructor TPngImageList.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
if ImageListCount = 0 then
|
|
ApplyMethodPatches;
|
|
Inc(ImageListCount);
|
|
FEnabledImages := True;
|
|
FPngOptions := [pngBlendOnDisabled];
|
|
FPngImages := TPngImageCollectionItems.Create(Self);
|
|
FLocked := 0;
|
|
end;
|
|
|
|
destructor TPngImageList.Destroy;
|
|
begin
|
|
FPngImages.Free;
|
|
Dec(ImageListCount);
|
|
if ImageListCount = 0 then
|
|
RevertPatchedMethods;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
//--- Patched methods ---
|
|
|
|
function TPngImageList.Add(Image, Mask: TBitmap): Integer;
|
|
var
|
|
Patch: TMethodPatch;
|
|
Png: TPngImage;
|
|
begin
|
|
if TObject(Self) is TPngImageList then begin
|
|
Png := TPngImage.Create;
|
|
try
|
|
CreatePNG(Image, Mask, Png);
|
|
result := AddPng(Png);
|
|
finally
|
|
Png.Free;
|
|
end;
|
|
end
|
|
else begin
|
|
Patch := FindMethodPatch('Add');
|
|
if Patch <> nil then begin
|
|
Patch.BeginInvokeOldMethod;
|
|
try
|
|
Result := TCustomImageList(Self).Add(Image, Mask);
|
|
finally
|
|
Patch.FinishInvokeOldMethod;
|
|
end;
|
|
end
|
|
else
|
|
Result := -1;
|
|
end;
|
|
end;
|
|
|
|
function TPngImageList.AddIcon(Image: TIcon): Integer;
|
|
var
|
|
Patch: TMethodPatch;
|
|
Png: TPngImage;
|
|
begin
|
|
if TObject(Self) is TPngImageList then begin
|
|
Png := TPngImage.Create;
|
|
try
|
|
ConvertToPNG(Image, Png);
|
|
result := AddPng(Png);
|
|
finally
|
|
Png.Free;
|
|
end;
|
|
end
|
|
else begin
|
|
Patch := FindMethodPatch('AddIcon');
|
|
if Patch <> nil then begin
|
|
Patch.BeginInvokeOldMethod;
|
|
try
|
|
Result := TCustomImageList(Self).AddIcon(Image);
|
|
finally
|
|
Patch.FinishInvokeOldMethod;
|
|
end;
|
|
end
|
|
else
|
|
Result := -1;
|
|
end;
|
|
end;
|
|
|
|
function TPngImageList.AddPng(Image: TPngImage; Background: TColor = clNone):
|
|
Integer;
|
|
var
|
|
Item: TPngImageCollectionItem;
|
|
begin
|
|
Result := -1;
|
|
if Image = nil then Exit;
|
|
|
|
BeginUpdate;
|
|
try
|
|
Item := FPngImages.Add(True);
|
|
Item.PngImage := Image;
|
|
Item.Background := Background;
|
|
Result := Item.Index;
|
|
InternalAddPng(Item.PngImage, Item.Background);
|
|
Change;
|
|
finally
|
|
EndUpdate(false);
|
|
end;
|
|
end;
|
|
|
|
function TPngImageList.AddImage(Value: TCustomImageList; Index: Integer): Integer;
|
|
var
|
|
Patch: TMethodPatch;
|
|
Png: TPngImage;
|
|
begin
|
|
if TObject(Self) is TPngImageList then begin
|
|
Png := TPngImage.Create;
|
|
try
|
|
CopyImageFromImageList(Png, Value, Index);
|
|
result := AddPng(Png);
|
|
finally
|
|
Png.Free;
|
|
end;
|
|
end
|
|
else begin
|
|
Patch := FindMethodPatch('AddImage');
|
|
if Patch <> nil then begin
|
|
Patch.BeginInvokeOldMethod;
|
|
try
|
|
Result := TCustomImageList(Self).AddImage(Value, Index);
|
|
finally
|
|
Patch.FinishInvokeOldMethod;
|
|
end;
|
|
end
|
|
else
|
|
Result := -1;
|
|
end;
|
|
end;
|
|
|
|
procedure TPngImageList.AddImages(Value: TCustomImageList);
|
|
var
|
|
Patch: TMethodPatch;
|
|
I: Integer;
|
|
Png: TPngImage;
|
|
begin
|
|
if TObject(Self) is TPngImageList then begin
|
|
BeginUpdate;
|
|
try
|
|
//Copy every image from Value into this imagelist.
|
|
Png := TPngImage.Create;
|
|
try
|
|
for I := 0 to Value.Count - 1 do begin
|
|
CopyImageFromImageList(Png, Value, I);
|
|
AddPng(Png);
|
|
end;
|
|
finally
|
|
Png.Free;
|
|
end;
|
|
finally
|
|
EndUpdate;
|
|
end;
|
|
end
|
|
else begin
|
|
Patch := FindMethodPatch('AddImages');
|
|
if Patch <> nil then begin
|
|
Patch.BeginInvokeOldMethod;
|
|
try
|
|
TCustomImageList(Self).AddImages(Value);
|
|
finally
|
|
Patch.FinishInvokeOldMethod;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TPngImageList.AddMasked(Image: TBitmap; MaskColor: TColor): Integer;
|
|
var
|
|
Patch: TMethodPatch;
|
|
Png: TPngImage;
|
|
begin
|
|
if TObject(Self) is TPngImageList then begin
|
|
Png := TPngImage.Create;
|
|
try
|
|
CreatePNGMasked(Image, MaskColor, Png);
|
|
result := AddPng(Png);
|
|
finally
|
|
Png.Free;
|
|
end;
|
|
end
|
|
else begin
|
|
Patch := FindMethodPatch('AddMasked');
|
|
if Patch <> nil then begin
|
|
Patch.BeginInvokeOldMethod;
|
|
try
|
|
Result := TCustomImageList(Self).AddMasked(Image, MaskColor);
|
|
finally
|
|
Patch.FinishInvokeOldMethod;
|
|
end;
|
|
end
|
|
else
|
|
Result := -1;
|
|
end;
|
|
end;
|
|
|
|
procedure TPngImageList.AssignTo(Dest: TPersistent);
|
|
begin
|
|
inherited;
|
|
if Dest is TPngImageList then begin
|
|
TPngImageList(Dest).PngImages := FPngImages;
|
|
TPngImageList(Dest).EnabledImages := FEnabledImages;
|
|
end;
|
|
end;
|
|
|
|
procedure TPngImageList.BeginUpdate;
|
|
begin
|
|
Inc(FLocked);
|
|
end;
|
|
|
|
procedure TPngImageList.Clear;
|
|
var
|
|
Patch: TMethodPatch;
|
|
begin
|
|
if TObject(Self) is TPngImageList then begin
|
|
//Clear the PngImages collection and the internal imagelist.
|
|
BeginUpdate;
|
|
try
|
|
FPngImages.Clear;
|
|
ImageList_Remove(Handle, -1);
|
|
Change;
|
|
finally
|
|
EndUpdate(False);
|
|
end;
|
|
end
|
|
else begin
|
|
Patch := FindMethodPatch('Clear');
|
|
if Patch <> nil then begin
|
|
Patch.BeginInvokeOldMethod;
|
|
try
|
|
TCustomImageList(Self).Clear;
|
|
finally
|
|
Patch.FinishInvokeOldMethod;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TPngImageList.CopyPngs;
|
|
var
|
|
I: Integer;
|
|
Png: TPngImage;
|
|
Icon: HIcon;
|
|
item: TPngImageCollectionItem;
|
|
begin
|
|
//Are we adding a bunch of images?
|
|
if FLocked > 0 then
|
|
Exit;
|
|
|
|
//Copy PNG images to the imagelist. These images will not be stored in the DFM.
|
|
ImageList_Remove(Handle, -1);
|
|
Handle := ImageList_Create(Width, Height, ILC_COLOR32 or (Integer(Masked) *
|
|
ILC_MASK), 0, AllocBy);
|
|
|
|
Png := TPngImage.Create;
|
|
try
|
|
for I := 0 to FPngImages.Count - 1 do begin
|
|
item := FPngImages.Items[I];
|
|
if (item.PngImage = nil) or item.PngImage.Empty then
|
|
Continue;
|
|
if FEnabledImages or (FPngOptions = []) then begin
|
|
Icon := PngToIcon(item.PngImage, item.Background);
|
|
end
|
|
else begin
|
|
//Basically the same as in the DrawPNG function
|
|
Png.Assign(item.PngImage);
|
|
if pngBlendOnDisabled in FPngOptions then
|
|
MakeImageBlended(Png);
|
|
if pngGrayscaleOnDisabled in FPngOptions then
|
|
MakeImageGrayscale(Png);
|
|
Icon := PngToIcon(Png);
|
|
end;
|
|
ImageList_AddIcon(Handle, Icon);
|
|
DestroyIcon(Icon);
|
|
end;
|
|
finally
|
|
Png.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TPngImageList.Delete(Index: Integer);
|
|
var
|
|
Patch: TMethodPatch;
|
|
begin
|
|
if TObject(Self) is TPngImageList then begin
|
|
//Delete an image from the PngImages collection and from the internal imagelist.
|
|
if (Index >= 0) and (Index < Count) then begin
|
|
BeginUpdate;
|
|
try
|
|
FPngImages.Delete(Index);
|
|
ImageList_Remove(Handle, Index);
|
|
Change;
|
|
finally
|
|
EndUpdate(False);
|
|
end;
|
|
end;
|
|
end
|
|
else begin
|
|
Patch := FindMethodPatch('Delete');
|
|
if Patch <> nil then begin
|
|
Patch.BeginInvokeOldMethod;
|
|
try
|
|
TCustomImageList(Self).Delete(Index);
|
|
finally
|
|
Patch.FinishInvokeOldMethod;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
//--- End of patched methods ---
|
|
|
|
procedure TPngImageList.DoDraw(Index: Integer; Canvas: TCanvas; X, Y: Integer; Style: Cardinal; Enabled: Boolean);
|
|
var
|
|
PaintRect: TRect;
|
|
Options: TPngOptions;
|
|
Png: TPngImageCollectionItem;
|
|
begin
|
|
//Draw a PNG directly to the Canvas. This is the preferred method to call,
|
|
//because this omits the API calls that use a fixed background.
|
|
PaintRect := Bounds(X, Y, Width, Height);
|
|
if Enabled then
|
|
Options := []
|
|
else
|
|
Options := FPngOptions;
|
|
Png := FPngImages.Items[Index];
|
|
if Png <> nil then
|
|
DrawPNG(Png.PngImage, Canvas, PaintRect, Options);
|
|
end;
|
|
|
|
procedure TPngImageList.EndUpdate(Update: Boolean);
|
|
begin
|
|
Dec(FLocked);
|
|
if Update and (FLocked = 0) then
|
|
CopyPngs;
|
|
end;
|
|
|
|
function TPngImageList.GetHeight: Integer;
|
|
begin
|
|
Result := inherited Height;
|
|
end;
|
|
|
|
function TPngImageList.GetWidth: Integer;
|
|
begin
|
|
Result := inherited Width;
|
|
end;
|
|
|
|
procedure TPngImageList.Insert(Index: Integer; Image, Mask: TBitmap);
|
|
var
|
|
Patch: TMethodPatch;
|
|
Png: TPngImage;
|
|
begin
|
|
if TObject(Self) is TPngImageList then begin
|
|
//Insert a new PNG based on the image and its mask.
|
|
if Image <> nil then begin
|
|
Png := TPngImage.Create;
|
|
try
|
|
CreatePNG(Image, Mask, Png);
|
|
InsertPng(Index, Png);
|
|
finally
|
|
Png.Free;
|
|
end;
|
|
end;
|
|
end
|
|
else begin
|
|
Patch := FindMethodPatch('Insert');
|
|
if Patch <> nil then begin
|
|
Patch.BeginInvokeOldMethod;
|
|
try
|
|
TCustomImageList(Self).Insert(Index, Image, Mask);
|
|
finally
|
|
Patch.FinishInvokeOldMethod;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TPngImageList.InsertIcon(Index: Integer; Image: TIcon);
|
|
var
|
|
Patch: TMethodPatch;
|
|
Png: TPngImage;
|
|
begin
|
|
if TObject(Self) is TPngImageList then begin
|
|
//Insert a new PNG based on the image.
|
|
if Image <> nil then begin
|
|
Png := TPngImage.Create;
|
|
try
|
|
ConvertToPNG(Image, Png);
|
|
InsertPng(Index, Png);
|
|
finally
|
|
Png.Free;
|
|
end;
|
|
end;
|
|
end
|
|
else begin
|
|
Patch := FindMethodPatch('InsertIcon');
|
|
if Patch <> nil then begin
|
|
Patch.BeginInvokeOldMethod;
|
|
try
|
|
TCustomImageList(Self).InsertIcon(Index, Image);
|
|
finally
|
|
Patch.FinishInvokeOldMethod;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TPngImageList.InsertPng(Index: Integer; Image: TPngImage; Background:
|
|
TColor = clNone);
|
|
var
|
|
Item: TPngImageCollectionItem;
|
|
begin
|
|
if Image <> nil then begin
|
|
BeginUpdate;
|
|
try
|
|
Item := PngImages.Insert(Index, True);
|
|
Item.PngImage := Image;
|
|
Item.Background := Background;
|
|
InternalInsertPng(Index, Item.PngImage, Item.Background);
|
|
Change;
|
|
finally
|
|
EndUpdate(False);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TPngImageList.InsertMasked(Index: Integer; Image: TBitmap; MaskColor: TColor);
|
|
var
|
|
Patch: TMethodPatch;
|
|
Png: TPngImage;
|
|
begin
|
|
if TObject(Self) is TPngImageList then begin
|
|
//Insert a new PNG based on the image and a colored mask.
|
|
if Image <> nil then begin
|
|
Png := TPngImage.Create;
|
|
try
|
|
CreatePNGMasked(Image, MaskColor, Png);
|
|
InsertPng(Index, Png);
|
|
finally
|
|
Png.Free;
|
|
end;
|
|
end;
|
|
end
|
|
else begin
|
|
Patch := FindMethodPatch('InsertMasked');
|
|
if Patch <> nil then begin
|
|
Patch.BeginInvokeOldMethod;
|
|
try
|
|
TCustomImageList(Self).InsertMasked(Index, Image, MaskColor);
|
|
finally
|
|
Patch.FinishInvokeOldMethod;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TPngImageList.InternalInsertPng(Index: Integer; const Png: TPngImage;
|
|
Background: TColor);
|
|
var
|
|
I: Integer;
|
|
Icon: HICON;
|
|
TempList: TPngImageList;
|
|
begin
|
|
TempList := TPngImageList(TComponentClass(ClassType).Create(nil));
|
|
try
|
|
TempList.Assign(Self);
|
|
ImageList_RemoveAll(Handle);
|
|
for I := 0 to Index - 1 do begin
|
|
Icon := ImageList_GetIcon(TempList.Handle, I, ILD_NORMAL);
|
|
ImageList_AddIcon(Handle, Icon);
|
|
DestroyIcon(Icon);
|
|
end;
|
|
Icon := PngToIcon(Png, Background);
|
|
ImageList_AddIcon(Handle, Icon);
|
|
DestroyIcon(Icon);
|
|
for I := Index to TempList.Count - 1 do begin
|
|
Icon := ImageList_GetIcon(TempList.Handle, I, ILD_NORMAL);
|
|
ImageList_AddIcon(Handle, Icon);
|
|
DestroyIcon(Icon);
|
|
end;
|
|
finally
|
|
TempList.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TPngImageList.InternalAddPng(const Png: TPngImage; Background: TColor
|
|
= clNone);
|
|
var
|
|
Icon: HICON;
|
|
begin
|
|
Icon := PngToIcon(Png, Background);
|
|
try
|
|
ImageList_AddIcon(Handle, Icon);
|
|
finally
|
|
DestroyIcon(Icon);
|
|
end;
|
|
end;
|
|
|
|
procedure TPngImageList.Move(CurIndex, NewIndex: Integer);
|
|
var
|
|
Patch: TMethodPatch;
|
|
begin
|
|
if TObject(Self) is TPngImageList then begin
|
|
//Move an image from one position to another. Don't try doing so in the internal
|
|
//imagelist, just recreate it, since this method won't be called very often.
|
|
BeginUpdate;
|
|
try
|
|
ImageList_Remove(Handle, CurIndex);
|
|
InternalInsertPng(NewIndex, FPngImages[CurIndex].PngImage,
|
|
FPngImages[CurIndex].Background);
|
|
FPngImages[CurIndex].Index := NewIndex;
|
|
Change;
|
|
finally
|
|
EndUpdate(False);
|
|
end;
|
|
end
|
|
else begin
|
|
Patch := FindMethodPatch('Move');
|
|
if Patch <> nil then begin
|
|
Patch.BeginInvokeOldMethod;
|
|
try
|
|
TCustomImageList(Self).Move(CurIndex, NewIndex);
|
|
finally
|
|
Patch.FinishInvokeOldMethod;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TPngImageList.PngToIcon(const Png: TPngImage; Background: TColor): HICON;
|
|
const
|
|
MaxRGBQuads = MaxInt div SizeOf(TRGBQuad) - 1;
|
|
type
|
|
TRGBQuadArray = array[0..MaxRGBQuads] of TRGBQuad;
|
|
PRGBQuadArray = ^TRGBQuadArray;
|
|
TBitmapInfo4 = packed record
|
|
bmiHeader: TBitmapV4Header;
|
|
bmiColors: array[0..0] of TRGBQuad;
|
|
end;
|
|
|
|
function PngToIcon32(Png: TPngImage): HIcon;
|
|
var
|
|
ImageBits: PRGBQuadArray;
|
|
BitmapInfo: TBitmapInfo4;
|
|
IconInfo: TIconInfo;
|
|
AlphaBitmap: HBitmap;
|
|
MaskBitmap: TBitmap;
|
|
X, Y: Integer;
|
|
AlphaLine: PByteArray;
|
|
HasAlpha, HasBitmask: Boolean;
|
|
Color, TransparencyColor: TColor;
|
|
begin
|
|
//Convert a PNG object to an alpha-blended icon resource
|
|
ImageBits := nil;
|
|
|
|
//Allocate a DIB for the color data and alpha channel
|
|
with BitmapInfo.bmiHeader do begin
|
|
bV4Size := SizeOf(BitmapInfo.bmiHeader);
|
|
bV4Width := Png.Width;
|
|
bV4Height := Png.Height;
|
|
bV4Planes := 1;
|
|
bV4BitCount := 32;
|
|
bV4V4Compression := BI_BITFIELDS;
|
|
bV4SizeImage := 0;
|
|
bV4XPelsPerMeter := 0;
|
|
bV4YPelsPerMeter := 0;
|
|
bV4ClrUsed := 0;
|
|
bV4ClrImportant := 0;
|
|
bV4RedMask := $00FF0000;
|
|
bV4GreenMask := $0000FF00;
|
|
bV4BlueMask := $000000FF;
|
|
bV4AlphaMask := $FF000000;
|
|
end;
|
|
AlphaBitmap := CreateDIBSection(0, PBitmapInfo(@BitmapInfo)^,
|
|
DIB_RGB_COLORS, Pointer(ImageBits), 0, 0);
|
|
try
|
|
//Spin through and fill it with a wash of color and alpha.
|
|
AlphaLine := nil;
|
|
HasAlpha := Png.Header.ColorType in [COLOR_GRAYSCALEALPHA,
|
|
COLOR_RGBALPHA];
|
|
HasBitmask := Png.TransparencyMode = ptmBit;
|
|
TransparencyColor := Png.TransparentColor;
|
|
for Y := 0 to Png.Height - 1 do begin
|
|
if HasAlpha then
|
|
AlphaLine := Png.AlphaScanline[Png.Height - Y - 1];
|
|
for X := 0 to Png.Width - 1 do begin
|
|
Color := Png.Pixels[X, Png.Height - Y - 1];
|
|
ImageBits^[Y * Png.Width + X].rgbRed := Color and $FF;
|
|
ImageBits^[Y * Png.Width + X].rgbGreen := Color shr 8 and $FF;
|
|
ImageBits^[Y * Png.Width + X].rgbBlue := Color shr 16 and $FF;
|
|
if HasAlpha then
|
|
ImageBits^[Y * Png.Width + X].rgbReserved := AlphaLine^[X]
|
|
else if HasBitmask then
|
|
ImageBits^[Y * Png.Width + X].rgbReserved := Integer(Color <>
|
|
TransparencyColor) * 255;
|
|
end;
|
|
end;
|
|
|
|
//Create an empty mask
|
|
MaskBitmap := TBitmap.Create;
|
|
try
|
|
MaskBitmap.Width := Png.Width;
|
|
MaskBitmap.Height := Png.Height;
|
|
MaskBitmap.PixelFormat := pf1bit;
|
|
MaskBitmap.Canvas.Brush.Color := clBlack;
|
|
MaskBitmap.Canvas.FillRect(Rect(0, 0, MaskBitmap.Width,
|
|
MaskBitmap.Height));
|
|
|
|
//Create the alpha blended icon
|
|
IconInfo.fIcon := True;
|
|
IconInfo.hbmColor := AlphaBitmap;
|
|
IconInfo.hbmMask := MaskBitmap.Handle;
|
|
Result := CreateIconIndirect(IconInfo);
|
|
finally
|
|
MaskBitmap.Free;
|
|
end;
|
|
finally
|
|
DeleteObject(AlphaBitmap);
|
|
end;
|
|
end;
|
|
|
|
function PngToIcon24(Png: TPngImage; Background: TColor): HIcon;
|
|
var
|
|
ColorBitmap, MaskBitmap: TBitmap;
|
|
X, Y: Integer;
|
|
AlphaLine: PByteArray;
|
|
IconInfo: TIconInfo;
|
|
TransparencyColor: TColor;
|
|
begin
|
|
ColorBitmap := TBitmap.Create;
|
|
MaskBitmap := TBitmap.Create;
|
|
try
|
|
ColorBitmap.Width := Png.Width;
|
|
ColorBitmap.Height := Png.Height;
|
|
ColorBitmap.PixelFormat := pf32bit;
|
|
MaskBitmap.Width := Png.Width;
|
|
MaskBitmap.Height := Png.Height;
|
|
MaskBitmap.PixelFormat := pf32bit;
|
|
|
|
//Draw the color bitmap
|
|
ColorBitmap.Canvas.Brush.Color := Background;
|
|
ColorBitmap.Canvas.FillRect(Rect(0, 0, Png.Width, Png.Height));
|
|
Png.Draw(ColorBitmap.Canvas, Rect(0, 0, Png.Width, Png.Height));
|
|
|
|
//Create the mask bitmap
|
|
if Png.Header.ColorType in [COLOR_GRAYSCALEALPHA, COLOR_RGBALPHA] then
|
|
for Y := 0 to Png.Height - 1 do begin
|
|
AlphaLine := Png.AlphaScanline[Y];
|
|
for X := 0 to Png.Width - 1 do
|
|
if AlphaLine^[X] = 0 then
|
|
SetPixelV(MaskBitmap.Canvas.Handle, X, Y, clWhite)
|
|
else
|
|
SetPixelV(MaskBitmap.Canvas.Handle, X, Y, clBlack);
|
|
end
|
|
else if Png.TransparencyMode = ptmBit then begin
|
|
TransparencyColor := Png.TransparentColor;
|
|
for Y := 0 to Png.Height - 1 do
|
|
for X := 0 to Png.Width - 1 do
|
|
if Png.Pixels[X, Y] = TransparencyColor then
|
|
SetPixelV(MaskBitmap.Canvas.Handle, X, Y, clWhite)
|
|
else
|
|
SetPixelV(MaskBitmap.Canvas.Handle, X, Y, clBlack);
|
|
end;
|
|
|
|
//Create the icon
|
|
IconInfo.fIcon := True;
|
|
IconInfo.hbmColor := ColorBitmap.Handle;
|
|
IconInfo.hbmMask := MaskBitmap.Handle;
|
|
Result := CreateIconIndirect(IconInfo);
|
|
finally
|
|
ColorBitmap.Free;
|
|
MaskBitmap.Free;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
if GetComCtlVersion >= ComCtlVersionIE6 then begin
|
|
//Windows XP or later, using the modern method: convert every PNG to
|
|
//an icon resource with alpha channel
|
|
Result := PngToIcon32(Png);
|
|
end
|
|
else begin
|
|
//No Windows XP, using the legacy method: copy every PNG to a normal
|
|
//bitmap using a fixed background color
|
|
Result := PngToIcon24(Png, Background);
|
|
end;
|
|
end;
|
|
|
|
procedure TPngImageList.ReadData(Stream: TStream);
|
|
begin
|
|
//Make sure nothing gets read from the DFM
|
|
end;
|
|
|
|
procedure TPngImageList.Replace(Index: Integer; Image, Mask: TBitmap);
|
|
var
|
|
Item: TPngImageCollectionItem;
|
|
Patch: TMethodPatch;
|
|
Icon: HICON;
|
|
begin
|
|
if TObject(Self) is TPngImageList then begin
|
|
//Replace an existing PNG based with a new image and its mask.
|
|
if Image <> nil then begin
|
|
BeginUpdate;
|
|
try
|
|
Item := FPngImages[Index];
|
|
Item.FPngImage.Free;
|
|
CreatePNG(Image, Mask, Item.FPngImage);
|
|
Icon := PngToIcon(Item.PngImage, Item.Background);
|
|
ImageList_ReplaceIcon(Handle, Index, Icon);
|
|
DestroyIcon(Icon);
|
|
Change;
|
|
finally
|
|
EndUpdate(False);
|
|
end;
|
|
end;
|
|
end
|
|
else begin
|
|
Patch := FindMethodPatch('Replace');
|
|
if Patch <> nil then begin
|
|
Patch.BeginInvokeOldMethod;
|
|
try
|
|
TCustomImageList(Self).Replace(Index, Image, Mask);
|
|
finally
|
|
Patch.FinishInvokeOldMethod;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TPngImageList.ReplaceIcon(Index: Integer; Image: TIcon);
|
|
var
|
|
Item: TPngImageCollectionItem;
|
|
Patch: TMethodPatch;
|
|
Icon: HICON;
|
|
begin
|
|
if TObject(Self) is TPngImageList then begin
|
|
//Replace an existing PNG based with a new image.
|
|
if Image <> nil then begin
|
|
BeginUpdate;
|
|
try
|
|
Item := FPngImages[Index];
|
|
Item.FPngImage.Free;
|
|
ConvertToPNG(Image, Item.FPngImage);
|
|
Icon := PngToIcon(Item.PngImage, Item.Background);
|
|
ImageList_ReplaceIcon(Handle, Index, Icon);
|
|
DestroyIcon(Icon);
|
|
Change;
|
|
finally
|
|
EndUpdate(False);
|
|
end;
|
|
end
|
|
end
|
|
else begin
|
|
Patch := FindMethodPatch('ReplaceIcon');
|
|
if Patch <> nil then begin
|
|
Patch.BeginInvokeOldMethod;
|
|
try
|
|
TCustomImageList(Self).ReplaceIcon(Index, Image);
|
|
finally
|
|
Patch.FinishInvokeOldMethod;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TPngImageList.ReplaceMasked(Index: Integer; NewImage: TBitmap; MaskColor: TColor);
|
|
var
|
|
Item: TPngImageCollectionItem;
|
|
Patch: TMethodPatch;
|
|
Icon: HICON;
|
|
begin
|
|
if TObject(Self) is TPngImageList then begin
|
|
//Replace an existing PNG based with a new image and a colored mask.
|
|
if NewImage <> nil then begin
|
|
BeginUpdate;
|
|
try
|
|
Item := FPngImages[Index];
|
|
Item.FPngImage.Free;
|
|
CreatePNGMasked(NewImage, MaskColor, Item.FPngImage);
|
|
Icon := PngToIcon(Item.PngImage, Item.Background);
|
|
ImageList_ReplaceIcon(Handle, Index, Icon);
|
|
DestroyIcon(Icon);
|
|
Change;
|
|
finally
|
|
EndUpdate(False);
|
|
end;
|
|
end
|
|
end
|
|
else begin
|
|
Patch := FindMethodPatch('ReplaceMasked');
|
|
if Patch <> nil then begin
|
|
Patch.BeginInvokeOldMethod;
|
|
try
|
|
TCustomImageList(Self).ReplaceMasked(Index, NewImage, MaskColor);
|
|
finally
|
|
Patch.FinishInvokeOldMethod;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TPngImageList.SetEnabledImages(const Value: Boolean);
|
|
begin
|
|
if FEnabledImages xor Value then begin
|
|
FEnabledImages := Value;
|
|
CopyPngs;
|
|
end;
|
|
end;
|
|
|
|
procedure TPngImageList.SetHeight(const Value: Integer);
|
|
begin
|
|
if inherited Height <> Value then begin
|
|
inherited Height := Value;
|
|
Clear;
|
|
end;
|
|
end;
|
|
|
|
procedure TPngImageList.SetPngImages(const Value: TPngImageCollectionItems);
|
|
begin
|
|
if FPngImages <> Value then begin
|
|
FPngImages.Assign(Value);
|
|
Change;
|
|
end;
|
|
end;
|
|
|
|
procedure TPngImageList.SetPngOptions(const Value: TPngOptions);
|
|
begin
|
|
if FPngOptions <> Value then begin
|
|
FPngOptions := Value;
|
|
CopyPngs;
|
|
end;
|
|
end;
|
|
|
|
procedure TPngImageList.SetWidth(const Value: Integer);
|
|
begin
|
|
if inherited Width <> Value then begin
|
|
inherited Width := Value;
|
|
Clear;
|
|
end;
|
|
end;
|
|
|
|
procedure TPngImageList.WriteData(Stream: TStream);
|
|
begin
|
|
//Make sure nothing gets written to the DFM
|
|
end;
|
|
|
|
{ TPngImageCollection }
|
|
|
|
constructor TPngImageCollection.Create(AOwner: TComponent);
|
|
begin
|
|
inherited Create(AOwner);
|
|
FItems := TPngImageCollectionItems.Create(Self);
|
|
end;
|
|
|
|
destructor TPngImageCollection.Destroy;
|
|
begin
|
|
FItems.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
{ TPngImageCollectionItems }
|
|
|
|
constructor TPngImageCollectionItems.Create(AOwner: TPersistent);
|
|
begin
|
|
inherited Create(TPngImageCollectionItem);
|
|
FOwner := AOwner;
|
|
end;
|
|
|
|
function TPngImageCollectionItems.Add(DontCreatePNG: Boolean = False): TPngImageCollectionItem;
|
|
begin
|
|
{$WARN SYMBOL_DEPRECATED OFF}
|
|
Result := TPngImageCollectionItem.Create(Self, DontCreatePNG);
|
|
Added(TCollectionItem(Result));
|
|
end;
|
|
|
|
procedure TPngImageCollectionItems.Assign(Source: TPersistent);
|
|
begin
|
|
inherited Assign(Source);
|
|
Update(nil);
|
|
end;
|
|
|
|
function TPngImageCollectionItems.GetItem(Index: Integer): TPngImageCollectionItem;
|
|
begin
|
|
if (Index >= 0) and (Index < Count) then
|
|
Result := TPngImageCollectionItem(inherited Items[Index])
|
|
else
|
|
Result := nil;
|
|
end;
|
|
|
|
function TPngImageCollectionItems.GetOwner: TPersistent;
|
|
begin
|
|
Result := FOwner;
|
|
end;
|
|
|
|
function TPngImageCollectionItems.Insert(Index: Integer; DontCreatePNG: Boolean = False): TPngImageCollectionItem;
|
|
begin
|
|
Result := Add(DontCreatePNG);
|
|
Result.Index := Index;
|
|
end;
|
|
|
|
procedure TPngImageCollectionItems.SetItem(Index: Integer; const Value: TPngImageCollectionItem);
|
|
begin
|
|
if (Index >= 0) and (Index < Count) then
|
|
inherited Items[Index] := Value;
|
|
end;
|
|
|
|
procedure TPngImageCollectionItems.Update(Item: TCollectionItem);
|
|
begin
|
|
inherited Update(Item);
|
|
if FOwner is TPngImageList then
|
|
TPngImageList(FOwner).CopyPngs;
|
|
end;
|
|
|
|
constructor TPngImageCollectionItem.Create(Collection: TCollection);
|
|
begin
|
|
inherited Create(Collection);
|
|
FPngImage := TPngImage.Create;
|
|
FName := Format('PngImage%d', [Index]);
|
|
FBackground := clBtnFace;
|
|
end;
|
|
|
|
constructor TPngImageCollectionItem.Create(Collection: TCollection; DontCreatePNG: Boolean = False);
|
|
begin
|
|
inherited Create(Collection);
|
|
if DontCreatePng then
|
|
FPngImage := nil
|
|
else
|
|
FPngImage := TPngImage.Create;
|
|
FName := Format('PngImage%d', [Index]);
|
|
FBackground := clBtnFace;
|
|
end;
|
|
|
|
destructor TPngImageCollectionItem.Destroy;
|
|
begin
|
|
FPngImage.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TPngImageCollectionItem.Assign(Source: TPersistent);
|
|
begin
|
|
if Source is TPngImageCollectionItem then begin
|
|
PngImage.Assign(TPngImageCollectionItem(Source).PngImage);
|
|
Background := TPngImageCollectionItem(Source).Background;
|
|
Name := TPngImageCollectionItem(Source).Name;
|
|
end
|
|
else
|
|
inherited Assign(Source);
|
|
end;
|
|
|
|
{ TPngImageCollectionItem }
|
|
|
|
procedure TPngImageCollectionItem.AssignTo(Dest: TPersistent);
|
|
begin
|
|
inherited AssignTo(Dest);
|
|
if (Dest is TPngImageCollectionItem) then
|
|
TPngImageCollectionItem(Dest).PngImage := PngImage;
|
|
end;
|
|
|
|
function TPngImageCollectionItem.Duplicate: TPngImage;
|
|
begin
|
|
Result := TPngImage.Create;
|
|
Result.Assign(FPngImage);
|
|
end;
|
|
|
|
function TPngImageCollectionItem.GetDisplayName: string;
|
|
begin
|
|
if Length(FName) = 0 then
|
|
Result := inherited GetDisplayName
|
|
else
|
|
Result := FName;
|
|
end;
|
|
|
|
procedure TPngImageCollectionItem.SetBackground(const Value: TColor);
|
|
begin
|
|
if FBackground <> Value then begin
|
|
FBackground := Value;
|
|
Changed(False);
|
|
end;
|
|
end;
|
|
|
|
procedure TPngImageCollectionItem.SetPngImage(const Value: TPngImage);
|
|
begin
|
|
if FPngImage = nil then
|
|
FPngImage := TPngImage.Create;
|
|
FPngImage.Assign(Value);
|
|
Changed(False);
|
|
end;
|
|
|
|
initialization
|
|
|
|
finalization
|
|
MethodPatches.Free;
|
|
|
|
end.
|
|
|