mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-26 06:02:39 +02:00
430 lines
14 KiB
ObjectPascal
430 lines
14 KiB
ObjectPascal
{ $Id$}
|
|
{
|
|
*****************************************************************************
|
|
* Win32WSImgList.pp *
|
|
* ----------------- *
|
|
* *
|
|
* *
|
|
*****************************************************************************
|
|
|
|
*****************************************************************************
|
|
* *
|
|
* This file is part of the Lazarus Component Library (LCL) *
|
|
* *
|
|
* See the file COPYING.modifiedLGPL.txt, 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 Win32WSImgList;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
////////////////////////////////////////////////////
|
|
// I M P O R T A N T
|
|
////////////////////////////////////////////////////
|
|
// To get as little as posible circles,
|
|
// uncomment only when needed for registration
|
|
////////////////////////////////////////////////////
|
|
// rtl
|
|
CommCtrl, Windows, SysUtils, Classes,
|
|
// lcl
|
|
ImgList, GraphType, Graphics, LCLType,
|
|
// ws
|
|
Win32Extra, Win32Int, Win32Proc, InterfaceBase,
|
|
WSImgList, WSLCLClasses, WSProc, WSReferences;
|
|
|
|
type
|
|
|
|
{ TWin32WSCustomImageList }
|
|
|
|
TWin32WSCustomImageList = class(TWSCustomImageList)
|
|
protected
|
|
class procedure AddData(AListHandle: TLCLIntfHandle;
|
|
ACount, AReplaceIndex, AWidth, AHeight: Integer; AData: PRGBAQuad);
|
|
published
|
|
class procedure Clear(AList: TCustomImageList); override;
|
|
class function CreateReference(AList: TCustomImageList; ACount, AGrow, AWidth,
|
|
AHeight: Integer; AData: PRGBAQuad): TWSCustomImageListReference; override;
|
|
class procedure Delete(AList: TCustomImageList; AIndex: Integer); override;
|
|
class procedure DestroyReference(AComponent: TComponent); override;
|
|
class procedure Draw(AList: TCustomImageList; AIndex: Integer; ACanvas: TCanvas;
|
|
ABounds: TRect; ABkColor, ABlendColor: TColor; ADrawEffect: TGraphicsDrawEffect;
|
|
AStyle: TDrawingStyle; AImageType: TImageType); override;
|
|
class procedure DrawToDC(AList: TCustomImageList; AIndex: Integer; ADC: HDC;
|
|
ABounds: TRect; ABkColor, ABlendColor: TColor; ADrawEffect: TGraphicsDrawEffect;
|
|
AStyle: TDrawingStyle; AImageType: TImageType);
|
|
class procedure Insert(AList: TCustomImageList; AIndex: Integer; AData: PRGBAQuad); override;
|
|
class procedure Move(AList: TCustomImageList; ACurIndex, ANewIndex: Integer); override;
|
|
class procedure Replace(AList: TCustomImageList; AIndex: Integer; AData: PRGBAQuad); override;
|
|
end;
|
|
|
|
|
|
implementation
|
|
|
|
uses
|
|
intfgraphics;
|
|
|
|
const
|
|
DRAWINGSTYLEMAP: array[TDrawingStyle] of DWord = (
|
|
{ dsFocus } ILD_FOCUS,
|
|
{ dsSelected } ILD_SELECTED,
|
|
{ dsNormal } ILD_NORMAL,
|
|
{ dsTransparent } ILD_TRANSPARENT
|
|
);
|
|
|
|
IMAGETPYEMAP: array[TImageType] of DWord = (
|
|
{ itImage } ILD_NORMAL,
|
|
{ itMask } ILD_MASK
|
|
);
|
|
|
|
function ColorToImagelistColor(AColor: TColor): TColorRef;
|
|
begin
|
|
case AColor of
|
|
clNone: Result := CLR_NONE;
|
|
clDefault: Result := CLR_DEFAULT;
|
|
else
|
|
Result := ColorToRGB(AColor);
|
|
end;
|
|
end;
|
|
|
|
function GetColorDepth(ADC: HDC): Integer; inline; overload;
|
|
begin
|
|
Result := GetDeviceCaps(ADC, BITSPIXEL) * GetDeviceCaps(ADC, PLANES);
|
|
end;
|
|
|
|
function GetColorDepth: Integer; inline; overload;
|
|
var
|
|
DC: HDC;
|
|
begin
|
|
DC := GetDC(0);
|
|
Result := GetColorDepth(DC);
|
|
ReleaseDC(0, DC);
|
|
end;
|
|
|
|
class procedure TWin32WSCustomImageList.AddData(AListHandle: TLCLIntfHandle;
|
|
ACount, AReplaceIndex, AWidth, AHeight: Integer; AData: PRGBAQuad);
|
|
|
|
procedure DoAddAlpha;
|
|
var
|
|
Info: Windows.TBitmapInfo;
|
|
BitsPtr: Pointer;
|
|
bmp: HBITMAP;
|
|
DC: HDC;
|
|
DataCount, DataSize: Integer;
|
|
begin
|
|
FillChar(Info, SizeOf(Info), 0);
|
|
Info.bmiHeader.biSize := SizeOf(Info.bmiHeader);
|
|
Info.bmiHeader.biWidth := AWidth;
|
|
Info.bmiHeader.biHeight := -AHeight; // request top down
|
|
Info.bmiHeader.biPlanes := 1;
|
|
Info.bmiHeader.biBitCount := 32;
|
|
Info.bmiHeader.biCompression := BI_RGB;
|
|
|
|
BitsPtr := nil;
|
|
DC := GetDC(0);
|
|
bmp := Windows.CreateDIBSection(DC, Info, DIB_RGB_COLORS, BitsPtr, 0, 0);
|
|
ReleaseDC(0, DC);
|
|
|
|
if BitsPtr = nil
|
|
then begin
|
|
DeleteObject(bmp);
|
|
Exit;
|
|
end;
|
|
|
|
DataCount := AWidth * AHeight;
|
|
DataSize := DataCount * SizeOf(AData^);
|
|
while ACount > 0 do
|
|
begin
|
|
System.Move(AData^, BitsPtr^, DataSize);
|
|
if AReplaceIndex = -1
|
|
then ImageList_Add(AListHandle, bmp, 0)
|
|
else ImageList_Replace(AListHandle, AReplaceIndex, bmp, 0);
|
|
Inc(AData, DataCount);
|
|
Dec(ACount);
|
|
end;
|
|
|
|
DeleteObject(bmp);
|
|
end;
|
|
|
|
procedure DoAdd;
|
|
var
|
|
Info: Windows.TBitmapInfo;
|
|
BitsPtr, MaskPtr: Pointer;
|
|
P, LinePtr: PByte;
|
|
bmp, msk: HBITMAP;
|
|
DC: HDC;
|
|
DataCount, DataSize, x, y, MaskStride: Integer;
|
|
begin
|
|
FillChar(Info, SizeOf(Info), 0);
|
|
Info.bmiHeader.biSize := SizeOf(Info.bmiHeader);
|
|
Info.bmiHeader.biWidth := AWidth;
|
|
Info.bmiHeader.biHeight := -AHeight; // request top down
|
|
Info.bmiHeader.biPlanes := 1;
|
|
Info.bmiHeader.biBitCount := 32;
|
|
Info.bmiHeader.biCompression := BI_RGB;
|
|
|
|
BitsPtr := nil;
|
|
MaskPtr := nil;
|
|
msk := 0;
|
|
bmp := 0;
|
|
DC := GetDC(0);
|
|
bmp := Windows.CreateDIBSection(DC, Info, DIB_RGB_COLORS, BitsPtr, 0, 0);
|
|
ReleaseDC(0, DC);
|
|
|
|
if (bmp = 0) or (BitsPtr = nil) then
|
|
begin
|
|
DeleteObject(bmp);
|
|
Exit;
|
|
end;
|
|
|
|
DataCount := AWidth * AHeight;
|
|
DataSize := DataCount * SizeOf(AData^);
|
|
MaskStride := ((AWidth + 15) shr 4) shl 1; // align to Word
|
|
MaskPtr := AllocMem(AHeight * MaskStride);
|
|
while ACount > 0 do
|
|
begin
|
|
System.Move(AData^, BitsPtr^, DataSize);
|
|
|
|
// create mask
|
|
LinePtr := MaskPtr;
|
|
for y := 1 to AHeight do
|
|
begin
|
|
p := LinePtr;
|
|
for x := 1 to AWidth do
|
|
begin
|
|
P^ := (P^ and $FE) or ((not AData^.Alpha) shr 7);
|
|
if x and $7 = 0
|
|
then Inc(p)
|
|
else P^ := Byte(P^ shl 1);
|
|
Inc(AData);
|
|
end;
|
|
// finish mask shifting
|
|
if (AWidth and $7) <> 0 then
|
|
P^ := P^ shl (7 - (AWidth and 7));
|
|
Inc(LinePtr, MaskStride);
|
|
end;
|
|
|
|
msk := CreateBitmap(AWidth, AHeight, 1, 1, MaskPtr);
|
|
if AReplaceIndex = -1
|
|
then ImageList_Add(AListHandle, bmp, msk)
|
|
else ImageList_Replace(AListHandle, AReplaceIndex, bmp, msk);
|
|
Dec(ACount);
|
|
DeleteObject(msk);
|
|
end;
|
|
FreeMem(MaskPtr);
|
|
|
|
DeleteObject(bmp);
|
|
end;
|
|
|
|
begin
|
|
if Win32WidgetSet.CommonControlsVersion >= ComCtlVersionIE6
|
|
then DoAddAlpha
|
|
else DoAdd;
|
|
end;
|
|
|
|
class procedure TWin32WSCustomImageList.Clear(AList: TCustomImageList);
|
|
begin
|
|
if not WSCheckReferenceAllocated(AList, 'Clear')
|
|
then Exit;
|
|
ImageList_SetImageCount(AList.Reference._Handle, 0);
|
|
end;
|
|
|
|
class function TWin32WSCustomImageList.CreateReference(AList: TCustomImageList;
|
|
ACount, AGrow, AWidth, AHeight: Integer; AData: PRGBAQuad): TWSCustomImageListReference;
|
|
var
|
|
Flags: DWord;
|
|
begin
|
|
if Win32WidgetSet.CommonControlsVersion >= ComCtlVersionIE6
|
|
then begin
|
|
Flags := ILC_COLOR32;
|
|
end
|
|
else begin
|
|
case GetColorDepth of
|
|
04: FLAGS := ILC_COLOR4 or ILC_MASK;
|
|
08: FLAGS := ILC_COLOR8 or ILC_MASK;
|
|
16: FLAGS := ILC_COLOR16 or ILC_MASK;
|
|
24: FLAGS := ILC_COLOR24 or ILC_MASK;
|
|
32: FLAGS := ILC_COLOR32 or ILC_MASK;
|
|
else
|
|
FLAGS := ILC_COLOR or ILC_MASK;
|
|
end;
|
|
end;
|
|
Result._Init(ImageList_Create(AWidth, AHeight, Flags, ACount, AGrow));
|
|
if Result.Allocated and (ACount > 0) then
|
|
AddData(Result._Handle, ACount, -1, AWidth, AHeight, AData);
|
|
end;
|
|
|
|
class procedure TWin32WSCustomImageList.Delete(AList: TCustomImageList;
|
|
AIndex: Integer);
|
|
begin
|
|
if not WSCheckReferenceAllocated(AList, 'Delete')
|
|
then Exit;
|
|
ImageList_Remove(AList.Reference._Handle, AIndex);
|
|
end;
|
|
|
|
class procedure TWin32WSCustomImageList.DestroyReference(AComponent: TComponent);
|
|
begin
|
|
if not WSCheckReferenceAllocated(TCustomImageList(AComponent), 'DestroyReference')
|
|
then Exit;
|
|
ImageList_Destroy(TCustomImageList(AComponent).Reference._Handle);
|
|
end;
|
|
|
|
class procedure TWin32WSCustomImageList.Draw(AList: TCustomImageList; AIndex: Integer;
|
|
ACanvas: TCanvas; ABounds: TRect; ABkColor, ABlendColor: TColor;
|
|
ADrawEffect: TGraphicsDrawEffect; AStyle: TDrawingStyle; AImageType: TImageType);
|
|
begin
|
|
if not WSCheckReferenceAllocated(AList, 'Draw')
|
|
then Exit;
|
|
DrawToDC(AList, AIndex, ACanvas.Handle, ABounds, ABkColor, ABlendColor, ADrawEffect, AStyle, AImageType);
|
|
end;
|
|
|
|
class procedure TWin32WSCustomImageList.DrawToDC(AList: TCustomImageList;
|
|
AIndex: Integer; ADC: HDC; ABounds: TRect; ABkColor, ABlendColor: TColor;
|
|
ADrawEffect: TGraphicsDrawEffect; AStyle: TDrawingStyle;
|
|
AImageType: TImageType);
|
|
var
|
|
DrawParams: TImageListDrawParams;
|
|
RawImg: TRawImage;
|
|
ListImg, DeviceImg: TLazIntfImage;
|
|
OldBmp, ImgHandle, MskHandle: HBitmap;
|
|
ImgDC: HDC;
|
|
HasComCtl6: Boolean;
|
|
begin
|
|
HasComCtl6 := Win32WidgetSet.CommonControlsVersion >= ComCtlVersionIE6;
|
|
// If we are using comctl > 6 then COLOR_32 is supported and alpha bitmaps will
|
|
// be drawn correctly. If version is lower than our alpha bitmaps will be drawn
|
|
// with mask and with no alpha. But if we draw with effect different fron normal
|
|
// we will draw using another method with alpha even using comctl < 6. To prevent
|
|
// such inconsistency in drawing lets check whether we need alpha drawing first
|
|
// and whether imagelist has native alpha drawing. If it has then we will use
|
|
// ImageList_DrawEx in other case we will draw alpha bitmap ourself.
|
|
if (ADrawEffect = gdeNormal) and (HasComCtl6 or (GetColorDepth(ADC) < 32)) then
|
|
begin
|
|
ImageList_DrawEx(AList.Reference._Handle, AIndex, ADC, ABounds.Left,
|
|
ABounds.Top, ABounds.Right, ABounds.Bottom, ColorToImagelistColor(ABkColor),
|
|
ColorToImagelistColor(ABlendColor), DRAWINGSTYLEMAP[AStyle] or IMAGETPYEMAP[AImageType]);
|
|
end
|
|
else
|
|
if (ADrawEffect = gdeDisabled) and HasComCtl6 then
|
|
begin
|
|
// if it is manifested exe then use winXP algoriphm of gray painting
|
|
FillChar(DrawParams, SizeOf(DrawParams), 0);
|
|
DrawParams.cbSize := SizeOf(DrawParams);
|
|
DrawParams.himl := AList.Reference._Handle;
|
|
DrawParams.i := AIndex;
|
|
DrawParams.hdcDst := ADC;
|
|
DrawParams.x := ABounds.Left;
|
|
DrawParams.y := ABounds.Top;
|
|
DrawParams.cx := ABounds.Right;
|
|
DrawParams.cy := ABounds.Bottom;
|
|
DrawParams.rgbBk := ColorToImagelistColor(ABkColor);
|
|
DrawParams.rgbFg := ColorToImagelistColor(ABlendColor);
|
|
DrawParams.fStyle := DRAWINGSTYLEMAP[AStyle] or IMAGETPYEMAP[AImageType];
|
|
DrawParams.fState := ILS_SATURATE; // draw greyed
|
|
ImageList_DrawIndirect(@DrawParams);
|
|
end
|
|
else
|
|
begin
|
|
if ABounds.Right = 0 then
|
|
ABounds.Right := AList.Width;
|
|
if ABounds.Bottom = 0 then
|
|
ABounds.Bottom := AList.Height;
|
|
// use RawImage_PerformEffect to perform drawing effect
|
|
AList.GetRawImage(AIndex, RawImg);
|
|
RawImg.PerformEffect(ADrawEffect, True);
|
|
|
|
if not Widgetset.RawImage_CreateBitmaps(RawImg, ImgHandle, MskHandle, True)
|
|
then begin
|
|
// bummer, the widgetset doesn't support our 32bit format, try device
|
|
ListImg := TLazIntfImage.Create(RawImg, False);
|
|
DeviceImg := TLazIntfImage.Create(0,0,[]);
|
|
DeviceImg.DataDescription := GetDescriptionFromDevice(0, AList.Width, AList.Height);
|
|
DeviceImg.CopyPixels(ListImg);
|
|
DeviceImg.GetRawImage(RawImg);
|
|
Widgetset.RawImage_CreateBitmaps(RawImg, ImgHandle, MskHandle);
|
|
DeviceImg.Free;
|
|
ListImg.Free;
|
|
end;
|
|
|
|
ImgDC := CreateCompatibleDC(ADC);
|
|
OldBmp := SelectObject(ImgDC, ImgHandle);
|
|
WidgetSet.StretchMaskBlt(ADC, ABounds.Left, ABounds.Top, ABounds.Right, ABounds.Bottom,
|
|
ImgDC, 0, 0, ABounds.Right, ABounds.Bottom, MskHandle, 0, 0, SRCCOPY);
|
|
RawImg.FreeData;
|
|
SelectObject(ImgDC, OldBmp);
|
|
if ImgHandle<>0 then DeleteObject(ImgHandle);
|
|
if MskHandle<>0 then DeleteObject(MskHandle);
|
|
DeleteDC(ImgDC);
|
|
end;
|
|
end;
|
|
|
|
class procedure TWin32WSCustomImageList.Insert(AList: TCustomImageList;
|
|
AIndex: Integer; AData: PRGBAQuad);
|
|
var
|
|
ImageList: HImageList;
|
|
Count: Integer;
|
|
begin
|
|
if not WSCheckReferenceAllocated(AList, 'Insert')
|
|
then Exit;
|
|
|
|
ImageList := AList.Reference._Handle;
|
|
Count := ImageList_GetImageCount(ImageList);
|
|
|
|
if (AIndex <= Count) and (AIndex >= 0) then
|
|
begin
|
|
AddData(ImageList, 1, -1, AList.Width, AList.Height, AData);
|
|
if AIndex <> Count
|
|
then Move(AList, Count, AIndex);
|
|
end;
|
|
end;
|
|
|
|
class procedure TWin32WSCustomImageList.Move(AList: TCustomImageList;
|
|
ACurIndex, ANewIndex: Integer);
|
|
var
|
|
n: integer;
|
|
Handle: THandle;
|
|
begin
|
|
if not WSCheckReferenceAllocated(AList, 'Move')
|
|
then Exit;
|
|
|
|
if ACurIndex = ANewIndex
|
|
then Exit;
|
|
|
|
Handle := AList.Reference._Handle;
|
|
if ACurIndex < ANewIndex
|
|
then begin
|
|
for n := ACurIndex to ANewIndex - 1 do
|
|
ImageList_Copy(Handle, n + 1, Handle, n, ILCF_SWAP);
|
|
end
|
|
else begin
|
|
for n := ACurIndex downto ANewIndex + 1 do
|
|
ImageList_Copy(Handle, n - 1, Handle, n, ILCF_SWAP);
|
|
end;
|
|
end;
|
|
|
|
class procedure TWin32WSCustomImageList.Replace(AList: TCustomImageList;
|
|
AIndex: Integer; AData: PRGBAQuad);
|
|
var
|
|
ImageList: HImageList;
|
|
Count: Integer;
|
|
begin
|
|
if not WSCheckReferenceAllocated(AList, 'Replace')
|
|
then Exit;
|
|
|
|
ImageList := AList.Reference._Handle;
|
|
Count := ImageList_GetImageCount(ImageList);
|
|
|
|
if (AIndex < Count) and (AIndex >= 0)
|
|
then AddData(ImageList, 1, AIndex, AList.Width, AList.Height, AData);
|
|
end;
|
|
|
|
end.
|