📄 imglist.pas
字号:
if Image <> nil then
with Image do
if (Height < FHeight) or (Width < FWidth) then
raise EInvalidOperation.Create(SInvalidImageSize);
end;
procedure TCustomImageList.SetDrawingStyle(Value: TDrawingStyle);
begin
if Value <> DrawingStyle then
begin
FDrawingStyle := Value;
Change;
end;
end;
function TCustomImageList.GetHotSpot: TPoint;
begin
Result := Point(0, 0);
end;
function TCustomImageList.GetInstRes(Instance: THandle; ResType: TResType;
ResID: DWORD; Width: Integer; LoadFlags: TLoadResources;
MaskColor: TColor): Boolean;
begin
Result := InternalGetInstRes(Instance, ResType, PChar(ResID), Width,
LoadFlags, MaskColor);
end;
function TCustomImageList.GetInstRes(Instance: THandle; ResType: TResType;
const Name: string; Width: Integer; LoadFlags: TLoadResources;
MaskColor: TColor): Boolean;
begin
Result := InternalGetInstRes(Instance, ResType, PChar(Name), Width,
LoadFlags, MaskColor);
end;
function TCustomImageList.InternalGetInstRes(Instance: THandle;
ResType: TResType; Name: PChar; Width: Integer; LoadFlags: TLoadResources;
MaskColor: TColor): Boolean;
const
ResMap: array [TResType] of Integer = (IMAGE_BITMAP, IMAGE_CURSOR, IMAGE_ICON);
var
hImage: HImageList;
Flags: Integer;
begin
Flags := 0;
if lrDefaultColor in LoadFlags then Flags := Flags or LR_DEFAULTCOLOR;
if lrDefaultSize in LoadFlags then Flags := Flags or LR_DEFAULTSIZE;
if lrFromFile in LoadFlags then Flags := Flags or LR_LOADFROMFILE;
if lrMap3DColors in LoadFlags then Flags := Flags or LR_LOADMAP3DCOLORS;
if lrTransparent in LoadFlags then Flags := Flags or LR_LOADTRANSPARENT;
if lrMonoChrome in LoadFlags then Flags := Flags or LR_MONOCHROME;
hImage := ImageList_LoadImage(Instance, Name, Width, AllocBy, MaskColor,
ResMap[ResType], Flags);
if hImage <> 0 then
begin
CopyImages(hImage);
ImageList_Destroy(hImage);
Result := True;
end
else Result := False;
end;
function TCustomImageList.GetResource(ResType: TResType; const Name: string;
Width: Integer; LoadFlags: TLoadResources; MaskColor: TColor): Boolean;
begin
Result := GetInstRes(MainInstance, ResType, Name, Width, LoadFlags, MaskColor);
end;
function TCustomImageList.ResInstLoad(Instance: THandle; ResType: TResType;
const Name: string; MaskColor: TColor): Boolean;
begin
Result := GetInstRes(Instance, ResType, Name, Width, [], MaskColor);
end;
function TCustomImageList.ResourceLoad(ResType: TResType; const Name: string;
MaskColor: TColor): Boolean;
var
LibModule: PLibModule;
begin
Result := False;
if HInstance = MainInstance then
Result := GetInstRes(MainInstance, ResType, Name, Width, [], MaskColor)
else
begin
LibModule := LibModuleList;
while LibModule <> nil do
with LibModule^ do
begin
Result := GetInstRes(ResInstance, ResType, Name, Width, [], MaskColor);
if not Result and (Instance <> ResInstance) then
Result := GetInstRes(Instance, ResType, Name, Width, [], MaskColor);
if Result then Exit;
LibModule := LibModule.Next;
end;
end;
end;
function TCustomImageList.FileLoad(ResType: TResType; const Name: string;
MaskColor: TColor): Boolean;
begin
Result := GetResource(ResType, Name, Width, [lrFromFile], MaskColor);
end;
procedure TCustomImageList.Change;
var
I: Integer;
begin
FChanged := True;
if FUpdateCount > 0 then Exit;
if FClients <> nil then
for I := 0 to FClients.Count - 1 do
TChangeLink(FClients[I]).Change;
if Assigned(FOnChange) then FOnChange(Self);
end;
procedure TCustomImageList.UnRegisterChanges(Value: TChangeLink);
var
I: Integer;
begin
if FClients <> nil then
for I := 0 to FClients.Count - 1 do
if FClients[I] = Value then
begin
Value.Sender := nil;
FClients.Delete(I);
Break;
end;
end;
procedure TCustomImageList.RegisterChanges(Value: TChangeLink);
begin
Value.Sender := Self;
if FClients <> nil then FClients.Add(Value);
end;
function TCustomImageList.Equal(IL: TCustomImageList): Boolean;
function StreamsEqual(S1, S2: TMemoryStream): Boolean;
begin
Result := (S1.Size = S2.Size) and CompareMem(S1.Memory, S2.Memory, S1.Size);
end;
var
MyImage, OtherImage: TMemoryStream;
begin
if (IL = nil) or (Count <> IL.Count) then
begin
Result := False;
Exit;
end;
if (Count = 0) and (IL.Count = 0) then
begin
Result := True;
Exit;
end;
MyImage := TMemoryStream.Create;
try
WriteData(MyImage);
OtherImage := TMemoryStream.Create;
try
IL.WriteData(OtherImage);
Result := StreamsEqual(MyImage, OtherImage);
finally
OtherImage.Free;
end;
finally
MyImage.Free;
end;
end;
procedure TCustomImageList.DefineProperties(Filer: TFiler);
function DoWrite: Boolean;
begin
if Filer.Ancestor <> nil then
Result := not (Filer.Ancestor is TCustomImageList) or
not Equal(TCustomImageList(Filer.Ancestor))
else
Result := Count > 0;
end;
begin
inherited DefineProperties(Filer);
Filer.DefineBinaryProperty('Bitmap', ReadData, WriteData, DoWrite);
end;
procedure TCustomImageList.ReadD2Stream(Stream: TStream);
var
FullImage, Image, FullMask, Mask: TBitmap;
I, J, Size, Pos, Count: Integer;
SrcRect: TRect;
begin
Stream.ReadBuffer(Size, SizeOf(Size));
Stream.ReadBuffer(Count, SizeOf(Count));
FullImage := TBitmap.Create;
try
Pos := Stream.Position;
FullImage.LoadFromStream(Stream);
Stream.Position := Pos + Size;
FullMask := TBitmap.Create;
try
FullMask.LoadFromStream(Stream);
Image := TBitmap.Create;
Image.Width := Width;
Image.Height := Height;
Mask := TBitmap.Create;
Mask.Monochrome := True;
Mask.Width := Width;
Mask.Height := Height;
SrcRect := Rect(0, 0, Width, Height);
BeginUpdate;
try
for J := 0 to (FullImage.Height div Height) - 1 do
begin
if Count = 0 then Break;
for I := 0 to (FullImage.Width div Width) - 1 do
begin
if Count = 0 then Break;
Image.Canvas.CopyRect(SrcRect, FullImage.Canvas,
Bounds(I * Width, J * Height, Width, Height));
Mask.Canvas.CopyRect(SrcRect, FullMask.Canvas,
Bounds(I * Width, J * Height, Width, Height));
Add(Image, Mask);
Dec(Count);
end;
end;
finally
Image.Free;
Mask.Free;
EndUpdate;
end;
finally
FullMask.Free;
end;
finally
FullImage.Free;
end;
end;
procedure TCustomImageList.ReadD3Stream(Stream: TStream);
var
LAdapter: TStreamAdapter;
LTemp: TMemoryStream;
LRetry: Boolean;
LValue, LBitCount: Byte;
begin
// attempt a simple read
LAdapter := TStreamAdapter.Create(Stream);
try
Handle := ImageList_Read(LAdapter);
finally
LAdapter.Free;
end;
// if we were not successful then attempt to fix up the really old ComCtl stream
if not HandleAllocated then
begin
// make a temp copy of the stream
LRetry := False;
LTemp := TMemoryStream.Create;
try
Stream.Position := 0;
LTemp.LoadFromStream(Stream);
// find the bad value imagelist header info
LTemp.Position := 18;
if (LTemp.Read(LValue, 1) = 1) and (LValue = 1) then
begin
// find the bitcount data farther on into the BitmapInfoHeader
LTemp.Position := 56;
if LTemp.Read(LBitCount, 1) = 1 then
begin
// correct the original value
LValue := LValue or LBitCount;
// back to the imagelist header info and patch it
LTemp.Position := 18;
LRetry := LTemp.Write(LValue, 1) = 1;
end;
end;
// reattempt the load
if LRetry then
begin
LTemp.Position := 0;
LAdapter := TStreamAdapter.Create(LTemp);
try
Handle := ImageList_Read(LAdapter);
finally
LAdapter.Free;
end;
end;
finally
LTemp.Free;
end;
// if we still didn't succeed then fail
if not HandleAllocated then
raise EReadError.CreateRes(@SImageReadFail);
end;
end;
procedure TCustomImageList.ReadData(Stream: TStream);
var
CheckInt1, CheckInt2: Integer;
CheckByte1, CheckByte2: Byte;
StreamPos: Integer;
begin
FreeHandle;
StreamPos := Stream.Position; // check stream signature to
Stream.Read(CheckInt1, SizeOf(CheckInt1)); // determine a Delphi 2 or Delphi
Stream.Read(CheckInt2, SizeOf(CheckInt2)); // 3 imagelist stream. Delphi 2
CheckByte1 := Lo(LoWord(CheckInt1)); // streams can be read, but only
CheckByte2 := Hi(LoWord(CheckInt1)); // Delphi 3 streams will be written
Stream.Position := StreamPos;
if (CheckInt1 <> CheckInt2) and (CheckByte1 = $49) and (CheckByte2 = $4C) then
ReadD3Stream(Stream)
else
ReadD2Stream(Stream);
end;
const
ComCtlVersionIE6 = $00060000;
var
CachedComCtrlVer: Cardinal;
ImageListWriteExProc: function(ImageList: HIMAGELIST; Flags: DWORD; Stream: IStream): HRESULT; stdcall;
procedure TCustomImageList.WriteData(Stream: TStream);
var
SA: TStreamAdapter;
ComCtrlHandle: THandle;
const
ILP_DOWNLEVEL = 1;
begin
if CachedComCtrlVer = 0 then
begin
CachedComCtrlVer := GetFileVersion(comctl32);
if CachedComCtrlVer >= ComCtlVersionIE6 then
begin
ComCtrlHandle := GetModuleHandle(comctl32);
if ComCtrlHandle <> 0 then
ImageListWriteExProc := GetProcAddress(ComCtrlHandle, 'ImageList_WriteEx'); { Do not localize }
end;
end;
SA := TStreamAdapter.Create(Stream);
try
{ See if we should use the new API for writing image lists in the old
format. }
if Assigned(ImageListWriteExProc) then
begin
if ImageListWriteExProc(Handle, ILP_DOWNLEVEL, SA) <> S_OK then
raise EWriteError.CreateRes(@SImageWriteFail)
end
else if not ImageList_Write(Handle, SA) then
raise EWriteError.CreateRes(@SImageWriteFail);
finally
SA.Free;
end;
end;
(*
var
I: Integer;
DIB1, DIB2: TBitmap;
DC: HDC;
S: TMemoryStream;
procedure WriteDIB(BM: HBitmap);
{ The ImageList leaves its bitmap handle selected into a DC somewhere,
so we can't select it into our own DC to copy from it. The only safe
operation is GetDIB (GetDIBits), which extracts the pixel bits without
selecting the BM into a DC. This code builds our own bitmap from
those bits, then crops it to the minimum size before writing it out.}
var
BitsSize: DWORD;
Header, Bits: PChar;
DIBBits: Pointer;
R: TRect;
HeaderSize: DWORD;
GlyphsPerRow, Rows: Integer;
begin
if BM = 0 then Exit;
GetDIBSizes(BM, HeaderSize, BitsSize);
GetMem(Header, HeaderSize + BitsSize);
try
Bits := Header + HeaderSize;
GetDIB(BM, 0, Header^, Bits^);
DIB1.Handle := CreateDIBSection(DC, PBitmapInfo(Header)^, DIB_RGB_COLORS, DIBBits, 0, 0);
System.Move(Bits^, DIBBits^, BitsSize);
with PBitmapInfo(Header)^.bmiHeader do
begin
GlyphsPerRow := biWidth div Width;
if GlyphsPerRow = 0 then Inc(GlyphsPerRow);
if GlyphsPerRow > Count then GlyphsPerRow := Count;
biWidth := GlyphsPerRow * Width;
Rows := Count div GlyphsPerRow;
if Count > Rows * GlyphsPerRow then Inc(Rows);
biHeight := Rows * Height;
R := Rect(0, 0, biWidth, biHeight);
end;
DIB2.Handle := CreateDIBSection(DC, PBitmapInfo(Header)^, DIB_RGB_COLORS, DIBBits, 0, 0);
DIB2.Canvas.CopyRect(R, DIB1.Canvas, R);
DIB2.SaveToStream(S);
finally
FreeMem(Header);
end;
end;
begin
DIB1 := nil;
DIB2 := nil;
DC := 0;
S := TMemoryStream.Create;
try
DIB1 := TBitmap.Create;
DIB2 := TBitmap.Create;
DC := GetDC(0);
WriteDIB(GetImageBitmap);
I := S.Size;
WriteDIB(GetMaskBitmap);
Stream.WriteBuffer(I, sizeof(I));
I := Count;
Stream.WriteBuffer(I, sizeof(I));
Stream.WriteBuffer(S.Memory^, S.Size);
finally
ReleaseDC(0, DC);
DIB1.Free;
DIB2.Free;
S.Free;
end;
end;
*)
procedure TCustomImageList.BeginUpdate;
begin
Inc(FUpdateCount);
end;
procedure TCustomImageList.EndUpdate;
begin
if FUpdateCount > 0 then Dec(FUpdateCount);
if FChanged then
begin
FChanged := False;
Change;
end;
end;
{ TChangeLink }
destructor TChangeLink.Destroy;
begin
if Sender <> nil then Sender.UnRegisterChanges(Self);
inherited Destroy;
end;
procedure TChangeLink.Change;
begin
if Assigned(OnChange) then OnChange(Sender);
end;
end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -