1{ $Id: win32wsimglist.pp 57164 2018-01-27 18:12:35Z ondrej $} 2{ 3 ***************************************************************************** 4 * Win32WSImgList.pp * 5 * ----------------- * 6 * * 7 * * 8 ***************************************************************************** 9 10 ***************************************************************************** 11 This file is part of the Lazarus Component Library (LCL) 12 13 See the file COPYING.modifiedLGPL.txt, included in this distribution, 14 for details about the license. 15 ***************************************************************************** 16} 17unit Win32WSImgList; 18 19{$mode objfpc}{$H+} 20 21interface 22 23uses 24//////////////////////////////////////////////////// 25// I M P O R T A N T 26//////////////////////////////////////////////////// 27// To get as little as posible circles, 28// uncomment only when needed for registration 29//////////////////////////////////////////////////// 30// rtl 31 CommCtrl, Windows, SysUtils, Classes, 32// lcl 33 ImgList, GraphType, Graphics, LCLType, 34// ws 35 Win32Extra, Win32Int, Win32Proc, InterfaceBase, 36 WSImgList, WSLCLClasses, WSProc, WSReferences; 37 38type 39 40 { TWin32WSCustomImageList } 41 42 TWin32WSCustomImageListResolution = class(TWSCustomImageListResolution) 43 protected 44 class procedure AddData(AListHandle: TLCLIntfHandle; 45 ACount, AReplaceIndex, AWidth, AHeight: Integer; AData: PRGBAQuad); 46 published 47 class procedure Clear(AList: TCustomImageListResolution); override; 48 class function CreateReference(AList: TCustomImageListResolution; ACount, AGrow, AWidth, 49 AHeight: Integer; AData: PRGBAQuad): TWSCustomImageListReference; override; 50 class procedure Delete(AList: TCustomImageListResolution; AIndex: Integer); override; 51 class procedure DestroyReference(AComponent: TComponent); override; 52 class procedure Draw(AList: TCustomImageListResolution; AIndex: Integer; ACanvas: TCanvas; 53 ABounds: TRect; ABkColor, ABlendColor: TColor; ADrawEffect: TGraphicsDrawEffect; 54 AStyle: TDrawingStyle; AImageType: TImageType); override; 55 class procedure DrawToDC(AList: TCustomImageListResolution; AIndex: Integer; ADC: HDC; 56 ABounds: TRect; ABkColor, ABlendColor: TColor; ADrawEffect: TGraphicsDrawEffect; 57 AStyle: TDrawingStyle; AImageType: TImageType); 58 class procedure Insert(AList: TCustomImageListResolution; AIndex: Integer; AData: PRGBAQuad); override; 59 class procedure Move(AList: TCustomImageListResolution; ACurIndex, ANewIndex: Integer); override; 60 class procedure Replace(AList: TCustomImageListResolution; AIndex: Integer; AData: PRGBAQuad); override; 61 end; 62 63 64implementation 65 66uses 67 intfgraphics; 68 69const 70 DRAWINGSTYLEMAP: array[TDrawingStyle] of DWord = ( 71{ dsFocus } ILD_FOCUS, 72{ dsSelected } ILD_SELECTED, 73{ dsNormal } ILD_NORMAL, 74{ dsTransparent } ILD_TRANSPARENT 75 ); 76 77 IMAGETPYEMAP: array[TImageType] of DWord = ( 78{ itImage } ILD_NORMAL, 79{ itMask } ILD_MASK 80 ); 81 82function ColorToImagelistColor(AColor: TColor): TColorRef; 83begin 84 case AColor of 85 clNone: Result := CLR_NONE; 86 clDefault: Result := CLR_DEFAULT; 87 else 88 Result := ColorToRGB(AColor); 89 end; 90end; 91 92function GetColorDepth(ADC: HDC): Integer; inline; overload; 93begin 94 Result := GetDeviceCaps(ADC, BITSPIXEL) * GetDeviceCaps(ADC, PLANES); 95end; 96 97function GetColorDepth: Integer; inline; overload; 98var 99 DC: HDC; 100begin 101 DC := GetDC(0); 102 Result := GetColorDepth(DC); 103 ReleaseDC(0, DC); 104end; 105 106class procedure TWin32WSCustomImageListResolution.AddData(AListHandle: TLCLIntfHandle; 107 ACount, AReplaceIndex, AWidth, AHeight: Integer; AData: PRGBAQuad); 108 109 procedure DoAddAlpha; 110 var 111 Info: Windows.TBitmapInfo; 112 BitsPtr: Pointer; 113 bmp: HBITMAP; 114 DC: HDC; 115 DataCount, DataSize: Integer; 116 begin 117 FillChar(Info, SizeOf(Info), 0); 118 Info.bmiHeader.biSize := SizeOf(Info.bmiHeader); 119 Info.bmiHeader.biWidth := AWidth; 120 Info.bmiHeader.biHeight := -AHeight; // request top down 121 Info.bmiHeader.biPlanes := 1; 122 Info.bmiHeader.biBitCount := 32; 123 Info.bmiHeader.biCompression := BI_RGB; 124 125 BitsPtr := nil; 126 DC := GetDC(0); 127 bmp := Windows.CreateDIBSection(DC, Info, DIB_RGB_COLORS, BitsPtr, 0, 0); 128 ReleaseDC(0, DC); 129 130 if BitsPtr = nil 131 then begin 132 DeleteObject(bmp); 133 Exit; 134 end; 135 136 DataCount := AWidth * AHeight; 137 DataSize := DataCount * SizeOf(AData^); 138 while ACount > 0 do 139 begin 140 System.Move(AData^, BitsPtr^, DataSize); 141 if AReplaceIndex = -1 142 then ImageList_Add(AListHandle, bmp, 0) 143 else ImageList_Replace(AListHandle, AReplaceIndex, bmp, 0); 144 Inc(AData, DataCount); 145 Dec(ACount); 146 end; 147 148 DeleteObject(bmp); 149 end; 150 151 procedure DoAdd; 152 var 153 Info: Windows.TBitmapInfo; 154 BitsPtr, MaskPtr: Pointer; 155 P, LinePtr: PByte; 156 bmp, msk: HBITMAP; 157 DC: HDC; 158 DataCount, DataSize, x, y, MaskStride: Integer; 159 begin 160 FillChar(Info, SizeOf(Info), 0); 161 Info.bmiHeader.biSize := SizeOf(Info.bmiHeader); 162 Info.bmiHeader.biWidth := AWidth; 163 Info.bmiHeader.biHeight := -AHeight; // request top down 164 Info.bmiHeader.biPlanes := 1; 165 Info.bmiHeader.biBitCount := 32; 166 Info.bmiHeader.biCompression := BI_RGB; 167 168 BitsPtr := nil; 169 MaskPtr := nil; 170 msk := 0; 171 bmp := 0; 172 DC := GetDC(0); 173 bmp := Windows.CreateDIBSection(DC, Info, DIB_RGB_COLORS, BitsPtr, 0, 0); 174 ReleaseDC(0, DC); 175 176 if (bmp = 0) or (BitsPtr = nil) then 177 begin 178 DeleteObject(bmp); 179 Exit; 180 end; 181 182 DataCount := AWidth * AHeight; 183 DataSize := DataCount * SizeOf(AData^); 184 MaskStride := ((AWidth + 15) shr 4) shl 1; // align to Word 185 MaskPtr := AllocMem(AHeight * MaskStride); 186 while ACount > 0 do 187 begin 188 System.Move(AData^, BitsPtr^, DataSize); 189 190 // create mask 191 LinePtr := MaskPtr; 192 for y := 1 to AHeight do 193 begin 194 p := LinePtr; 195 for x := 1 to AWidth do 196 begin 197 P^ := (P^ and $FE) or ((not AData^.Alpha) shr 7); 198 if x and $7 = 0 199 then Inc(p) 200 else P^ := Byte(P^ shl 1); 201 Inc(AData); 202 end; 203 // finish mask shifting 204 if (AWidth and $7) <> 0 then 205 P^ := P^ shl (7 - (AWidth and 7)); 206 Inc(LinePtr, MaskStride); 207 end; 208 209 msk := CreateBitmap(AWidth, AHeight, 1, 1, MaskPtr); 210 if AReplaceIndex = -1 211 then ImageList_Add(AListHandle, bmp, msk) 212 else ImageList_Replace(AListHandle, AReplaceIndex, bmp, msk); 213 Dec(ACount); 214 DeleteObject(msk); 215 end; 216 FreeMem(MaskPtr); 217 218 DeleteObject(bmp); 219 end; 220 221begin 222 if Win32WidgetSet.CommonControlsVersion >= ComCtlVersionIE6 223 then DoAddAlpha 224 else DoAdd; 225end; 226 227class procedure TWin32WSCustomImageListResolution.Clear( 228 AList: TCustomImageListResolution); 229begin 230 if not WSCheckReferenceAllocated(AList, 'Clear') 231 then Exit; 232 ImageList_SetImageCount(AList.Reference._Handle, 0); 233end; 234 235class function TWin32WSCustomImageListResolution.CreateReference( 236 AList: TCustomImageListResolution; ACount, AGrow, AWidth, AHeight: Integer; 237 AData: PRGBAQuad): TWSCustomImageListReference; 238var 239 Flags: DWord; 240begin 241 if Win32WidgetSet.CommonControlsVersion >= ComCtlVersionIE6 242 then begin 243 Flags := ILC_COLOR32; 244 end 245 else begin 246 case GetColorDepth of 247 04: FLAGS := ILC_COLOR4 or ILC_MASK; 248 08: FLAGS := ILC_COLOR8 or ILC_MASK; 249 16: FLAGS := ILC_COLOR16 or ILC_MASK; 250 24: FLAGS := ILC_COLOR24 or ILC_MASK; 251 32: FLAGS := ILC_COLOR32 or ILC_MASK; 252 else 253 FLAGS := ILC_COLOR or ILC_MASK; 254 end; 255 end; 256 {$WARNINGS OFF} 257 Result._Init(ImageList_Create(AWidth, AHeight, Flags, ACount, AGrow)); 258 if Result.Allocated and (ACount > 0) then 259 AddData(Result._Handle, ACount, -1, AWidth, AHeight, AData); 260 {$WARNINGS ON} 261end; 262 263class procedure TWin32WSCustomImageListResolution.Delete( 264 AList: TCustomImageListResolution; AIndex: Integer); 265begin 266 if not WSCheckReferenceAllocated(AList, 'Delete') 267 then Exit; 268 ImageList_Remove(AList.Reference._Handle, AIndex); 269end; 270 271class procedure TWin32WSCustomImageListResolution.DestroyReference(AComponent: TComponent); 272begin 273 if not WSCheckReferenceAllocated(TCustomImageListResolution(AComponent), 'DestroyReference') 274 then Exit; 275 ImageList_Destroy(TCustomImageListResolution(AComponent).Reference._Handle); 276end; 277 278class procedure TWin32WSCustomImageListResolution.Draw(AList: TCustomImageListResolution; 279 AIndex: Integer; ACanvas: TCanvas; ABounds: TRect; ABkColor, 280 ABlendColor: TColor; ADrawEffect: TGraphicsDrawEffect; AStyle: TDrawingStyle; 281 AImageType: TImageType); 282begin 283 if not WSCheckReferenceAllocated(AList, 'Draw') 284 then Exit; 285 DrawToDC(AList, AIndex, ACanvas.Handle, ABounds, ABkColor, ABlendColor, ADrawEffect, AStyle, AImageType); 286end; 287 288class procedure TWin32WSCustomImageListResolution.DrawToDC( 289 AList: TCustomImageListResolution; AIndex: Integer; ADC: HDC; ABounds: TRect; 290 ABkColor, ABlendColor: TColor; ADrawEffect: TGraphicsDrawEffect; 291 AStyle: TDrawingStyle; AImageType: TImageType); 292var 293 DrawParams: TImageListDrawParams; 294 RawImg: TRawImage; 295 ListImg, DeviceImg: TLazIntfImage; 296 OldBmp, ImgHandle, MskHandle: HBitmap; 297 ImgDC: HDC; 298 HasComCtl6: Boolean; 299begin 300 HasComCtl6 := Win32WidgetSet.CommonControlsVersion >= ComCtlVersionIE6; 301 // If we are using comctl > 6 then COLOR_32 is supported and alpha bitmaps will 302 // be drawn correctly. If version is lower than our alpha bitmaps will be drawn 303 // with mask and with no alpha. But if we draw with effect different fron normal 304 // we will draw using another method with alpha even using comctl < 6. To prevent 305 // such inconsistency in drawing lets check whether we need alpha drawing first 306 // and whether imagelist has native alpha drawing. If it has then we will use 307 // ImageList_DrawEx in other case we will draw alpha bitmap ourself. 308 if (ADrawEffect = gdeNormal) and (HasComCtl6 or (GetColorDepth(ADC) < 32)) then 309 begin 310 ImageList_DrawEx(AList.Reference._Handle, AIndex, ADC, ABounds.Left, 311 ABounds.Top, ABounds.Right, ABounds.Bottom, ColorToImagelistColor(ABkColor), 312 ColorToImagelistColor(ABlendColor), DRAWINGSTYLEMAP[AStyle] or IMAGETPYEMAP[AImageType]); 313 end 314 else 315 if (ADrawEffect = gdeDisabled) and HasComCtl6 then 316 begin 317 // if it is manifested exe then use winXP algoriphm of gray painting 318 FillChar(DrawParams, SizeOf(DrawParams), 0); 319 DrawParams.cbSize := SizeOf(DrawParams); 320 DrawParams.himl := AList.Reference._Handle; 321 DrawParams.i := AIndex; 322 DrawParams.hdcDst := ADC; 323 DrawParams.x := ABounds.Left; 324 DrawParams.y := ABounds.Top; 325 DrawParams.cx := ABounds.Right; 326 DrawParams.cy := ABounds.Bottom; 327 DrawParams.rgbBk := ColorToImagelistColor(ABkColor); 328 DrawParams.rgbFg := ColorToImagelistColor(ABlendColor); 329 DrawParams.fStyle := DRAWINGSTYLEMAP[AStyle] or IMAGETPYEMAP[AImageType]; 330 DrawParams.fState := ILS_SATURATE; // draw greyed 331 ImageList_DrawIndirect(@DrawParams); 332 end 333 else 334 begin 335 if ABounds.Right = 0 then 336 ABounds.Right := AList.Width; 337 if ABounds.Bottom = 0 then 338 ABounds.Bottom := AList.Height; 339 // use RawImage_PerformEffect to perform drawing effect 340 AList.GetRawImage(AIndex, RawImg); 341 RawImg.PerformEffect(ADrawEffect, True); 342 343 if not Widgetset.RawImage_CreateBitmaps(RawImg, ImgHandle, MskHandle, True) 344 then begin 345 // bummer, the widgetset doesn't support our 32bit format, try device 346 ListImg := TLazIntfImage.Create(RawImg, False); 347 DeviceImg := TLazIntfImage.Create(0,0,[]); 348 DeviceImg.DataDescription := GetDescriptionFromDevice(0, AList.Width, AList.Height); 349 DeviceImg.CopyPixels(ListImg); 350 DeviceImg.GetRawImage(RawImg); 351 Widgetset.RawImage_CreateBitmaps(RawImg, ImgHandle, MskHandle); 352 DeviceImg.Free; 353 ListImg.Free; 354 end; 355 356 ImgDC := CreateCompatibleDC(ADC); 357 OldBmp := SelectObject(ImgDC, ImgHandle); 358 WidgetSet.StretchMaskBlt(ADC, ABounds.Left, ABounds.Top, ABounds.Right, ABounds.Bottom, 359 ImgDC, 0, 0, ABounds.Right, ABounds.Bottom, MskHandle, 0, 0, SRCCOPY); 360 RawImg.FreeData; 361 SelectObject(ImgDC, OldBmp); 362 if ImgHandle<>0 then DeleteObject(ImgHandle); 363 if MskHandle<>0 then DeleteObject(MskHandle); 364 DeleteDC(ImgDC); 365 end; 366end; 367 368class procedure TWin32WSCustomImageListResolution.Insert( 369 AList: TCustomImageListResolution; AIndex: Integer; AData: PRGBAQuad); 370var 371 ImageList: HImageList; 372 Count: Integer; 373begin 374 if not WSCheckReferenceAllocated(AList, 'Insert') 375 then Exit; 376 377 ImageList := AList.Reference._Handle; 378 Count := ImageList_GetImageCount(ImageList); 379 380 if (AIndex <= Count) and (AIndex >= 0) then 381 begin 382 AddData(ImageList, 1, -1, AList.Width, AList.Height, AData); 383 if AIndex <> Count 384 then Move(AList, Count, AIndex); 385 end; 386end; 387 388class procedure TWin32WSCustomImageListResolution.Move(AList: TCustomImageListResolution; 389 ACurIndex, ANewIndex: Integer); 390var 391 n: integer; 392 Handle: THandle; 393begin 394 if not WSCheckReferenceAllocated(AList, 'Move') 395 then Exit; 396 397 if ACurIndex = ANewIndex 398 then Exit; 399 400 Handle := AList.Reference._Handle; 401 if ACurIndex < ANewIndex 402 then begin 403 for n := ACurIndex to ANewIndex - 1 do 404 ImageList_Copy(Handle, n + 1, Handle, n, ILCF_SWAP); 405 end 406 else begin 407 for n := ACurIndex downto ANewIndex + 1 do 408 ImageList_Copy(Handle, n - 1, Handle, n, ILCF_SWAP); 409 end; 410end; 411 412class procedure TWin32WSCustomImageListResolution.Replace( 413 AList: TCustomImageListResolution; AIndex: Integer; AData: PRGBAQuad); 414var 415 ImageList: HImageList; 416 Count: Integer; 417begin 418 if not WSCheckReferenceAllocated(AList, 'Replace') 419 then Exit; 420 421 ImageList := AList.Reference._Handle; 422 Count := ImageList_GetImageCount(ImageList); 423 424 if (AIndex < Count) and (AIndex >= 0) 425 then AddData(ImageList, 1, AIndex, AList.Width, AList.Height, AData); 426end; 427 428end. 429