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