TBXUtils.pas 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099
  1. unit TBXUtils;
  2. // TBX Package
  3. // Copyright 2001-2004 Alex A. Denisov. All Rights Reserved
  4. // See TBX.chm for license and installation instructions
  5. //
  6. // Id: TBXUtils.pas 11 2004-04-01 07:22:56Z Alex@ZEISS
  7. interface
  8. {$I TB2Ver.inc}
  9. uses
  10. Windows, Messages, Classes, SysUtils, Graphics, Controls, Forms, ImgList;
  11. function MixColors(C1, C2: TColor; W1: Integer): TColor;
  12. function ColorIntensity(C: TColor): Integer;
  13. function IsDarkColor(C: TColor; Threshold: Integer = 100): Boolean;
  14. function Blend(C1, C2: TColor; W1: Integer): TColor;
  15. procedure SetContrast(var Color: TColor; BkgndColor: TColor; Threshold: Integer);
  16. function GetBGR(C: TColorRef): Cardinal;
  17. function TBXScaleByTextHeightRunTime(Canvas: TCanvas; Dimension: Integer): Integer;
  18. { A few drawing functions }
  19. { these functions recognize clNone value of TColor }
  20. function FillRectEx(DC: HDC; const Rect: TRect; Color: TColor): Boolean;
  21. function FrameRectEx(DC: HDC; var Rect: TRect; Color: TColor; Adjust: Boolean): Boolean;
  22. procedure DrawLineEx(DC: HDC; X1, Y1, X2, Y2: Integer; Color: TColor);
  23. function PolyLineEx(DC: HDC; const Points: array of TPoint; Color: TColor): Boolean;
  24. procedure PolygonEx(DC: HDC; const Points: array of TPoint; OutlineColor, FillColor: TColor);
  25. procedure RoundRectEx(DC: HDC; Left, Top, Right, Bottom: Integer; EllipseWidth, EllipseHeight, OutlineColor, FillColor: TColor);
  26. procedure EllipseEx(DC: HDC; Left, Top, Right, Bottom: Integer; OutlineColor, FillColor: TColor);
  27. { extended icon painting routines }
  28. procedure DrawTBXIcon(Canvas: TCanvas; const R: TRect;
  29. ImageList: TCustomImageList; ImageIndex: Integer);
  30. procedure DrawTBXIconShadow(Canvas: TCanvas; const R: TRect;
  31. ImageList: TCustomImageList; ImageIndex: Integer; Density: Integer);
  32. procedure DrawTBXIconFlatShadow(Canvas: TCanvas; const R: TRect;
  33. ImageList: TCustomImageList; ImageIndex: Integer; ShadowColor: TColor);
  34. procedure DrawGlyph(DC: HDC; X, Y: Integer; ImageList: TCustomImageList; ImageIndex: Integer; Color: TColor); overload;
  35. procedure DrawGlyph(DC: HDC; const R: TRect; Width, Height: Integer; const Bits; Color: TColor); overload;
  36. { Stock Objects }
  37. var
  38. StockBitmap1, StockBitmap2: TBitmap;
  39. StockMonoBitmap, StockCompatibleBitmap: TBitmap;
  40. SmCaptionFont: TFont;
  41. const
  42. ROP_DSPDxax = $00E20746;
  43. { Support for window shadows }
  44. type
  45. TShadowEdges = set of (seTopLeft, seBottomRight);
  46. TShadowStyle = (ssFlat, ssLayered);
  47. TShadow = class(TCustomControl)
  48. protected
  49. FOpacity: Byte;
  50. FBuffer: TBitmap;
  51. FClearRect: TRect;
  52. FEdges: TShadowEdges;
  53. FStyle: TShadowStyle;
  54. FSaveBits: Boolean;
  55. procedure GradR(const R: TRect);
  56. procedure GradB(const R: TRect);
  57. procedure GradBR(const R: TRect);
  58. procedure GradTR(const R: TRect);
  59. procedure GradBL(const R: TRect);
  60. procedure CreateParams(var Params: TCreateParams); override;
  61. procedure FillBuffer; virtual; abstract;
  62. procedure WMNCHitTest(var Message: TMessage); message WM_NCHITTEST;
  63. public
  64. constructor Create(const Bounds: TRect; Opacity: Byte; LoColor: Boolean; Edges: TShadowEdges); reintroduce;
  65. procedure Clear(const R: TRect);
  66. procedure Render;
  67. procedure Show(ParentHandle: HWND);
  68. end;
  69. THorzShadow = class(TShadow)
  70. protected
  71. procedure FillBuffer; override;
  72. end;
  73. TVertShadow = class(TShadow)
  74. protected
  75. procedure FillBuffer; override;
  76. end;
  77. TShadows = class
  78. private
  79. FSaveBits: Boolean;
  80. procedure SetSaveBits(Value: Boolean);
  81. protected
  82. V1: TShadow;
  83. H1: TShadow;
  84. V2: TShadow;
  85. H2: TShadow;
  86. V3: TShadow;
  87. H3: TShadow;
  88. public
  89. constructor Create(R1, R2: TRect; TheSize: Integer; Opacity: Byte; LoColor: Boolean);
  90. destructor Destroy; override;
  91. procedure Show(ParentHandle: HWND);
  92. property SaveBits: Boolean read FSaveBits write SetSaveBits;
  93. end;
  94. procedure RecreateStock;
  95. implementation
  96. {$R-}{$Q-}
  97. uses TB2Common, Math, Types;
  98. type
  99. PPoints = ^TPoints;
  100. TPoints = array [0..0] of TPoint;
  101. const
  102. WeightR: single = 0.764706;
  103. WeightG: single = 1.52941;
  104. WeightB: single = 0.254902;
  105. function MixColors(C1, C2: TColor; W1: Integer): TColor;
  106. var
  107. W2: Cardinal;
  108. begin
  109. Assert(W1 in [0..255]);
  110. W2 := W1 xor 255;
  111. if Integer(C1) < 0 then C1 := GetSysColor(C1 and $000000FF);
  112. if Integer(C2) < 0 then C2 := GetSysColor(C2 and $000000FF);
  113. Result := Integer(
  114. ((Cardinal(C1) and $FF00FF) * Cardinal(W1) +
  115. (Cardinal(C2) and $FF00FF) * W2) and $FF00FF00 +
  116. ((Cardinal(C1) and $00FF00) * Cardinal(W1) +
  117. (Cardinal(C2) and $00FF00) * W2) and $00FF0000) shr 8;
  118. end;
  119. function ColorIntensity(C: TColor): Integer;
  120. begin
  121. if C < 0 then C := GetSysColor(C and $FF);
  122. Result := ((C shr 16 and $FF) * 30 + (C shr 8 and $FF) * 150 + (C and $FF) * 76) shr 8;
  123. end;
  124. function IsDarkColor(C: TColor; Threshold: Integer = 100): Boolean;
  125. begin
  126. if C < 0 then C := GetSysColor(C and $FF);
  127. Threshold := Threshold shl 8;
  128. Result := ((C and $FF) * 76 + (C shr 8 and $FF) * 150 + (C shr 16 and $FF) * 30 ) < Threshold;
  129. end;
  130. function Blend(C1, C2: TColor; W1: Integer): TColor;
  131. var
  132. W2, A1, A2, D, F, G: Integer;
  133. begin
  134. if C1 < 0 then C1 := GetSysColor(C1 and $FF);
  135. if C2 < 0 then C2 := GetSysColor(C2 and $FF);
  136. if W1 >= 100 then D := 1000
  137. else D := 100;
  138. W2 := D - W1;
  139. F := D div 2;
  140. A2 := C2 shr 16 * W2;
  141. A1 := C1 shr 16 * W1;
  142. G := (A1 + A2 + F) div D and $FF;
  143. Result := G shl 16;
  144. A2 := (C2 shr 8 and $FF) * W2;
  145. A1 := (C1 shr 8 and $FF) * W1;
  146. G := (A1 + A2 + F) div D and $FF;
  147. Result := Result or G shl 8;
  148. A2 := (C2 and $FF) * W2;
  149. A1 := (C1 and $FF) * W1;
  150. G := (A1 + A2 + F) div D and $FF;
  151. Result := Result or G;
  152. end;
  153. function ColorDistance(C1, C2: Integer): Single;
  154. var
  155. DR, DG, DB: Integer;
  156. begin
  157. DR := (C1 and $FF) - (C2 and $FF);
  158. Result := Sqr(DR * WeightR);
  159. DG := (C1 shr 8 and $FF) - (C2 shr 8 and $FF);
  160. Result := Result + Sqr(DG * WeightG);
  161. DB := (C1 shr 16) - (C2 shr 16);
  162. Result := Result + Sqr(DB * WeightB);
  163. Result := SqRt(Result);
  164. end;
  165. function GetAdjustedThreshold(BkgndIntensity, Threshold: Single): Single;
  166. begin
  167. if BkgndIntensity < 220 then Result := (2 - BkgndIntensity / 220) * Threshold
  168. else Result := Threshold;
  169. end;
  170. function IsContrastEnough(AColor, ABkgndColor: Integer;
  171. DoAdjustThreshold: Boolean; Threshold: Single): Boolean;
  172. begin
  173. if DoAdjustThreshold then
  174. Threshold := GetAdjustedThreshold(ColorDistance(ABkgndColor, $000000), Threshold);
  175. Result := ColorDistance(ABkgndColor, AColor) > Threshold;
  176. end;
  177. procedure AdjustContrast(var AColor: Integer; ABkgndColor: Integer; Threshold: Single);
  178. var
  179. x, y, z: Single;
  180. r, g, b: Single;
  181. RR, GG, BB: Integer;
  182. i1, i2, s, q, w: Single;
  183. DoInvert: Boolean;
  184. begin
  185. i1 := ColorDistance(AColor, $000000);
  186. i2 := ColorDistance(ABkgndColor, $000000);
  187. Threshold := GetAdjustedThreshold(i2, Threshold);
  188. if i1 > i2 then DoInvert := i2 < 442 - Threshold
  189. else DoInvert := i2 < Threshold;
  190. x := (ABkgndColor and $FF) * WeightR;
  191. y := (ABkgndColor shr 8 and $FF) * WeightG;
  192. z := (ABkgndColor shr 16) * WeightB;
  193. r := (AColor and $FF) * WeightR;
  194. g := (AColor shr 8 and $FF) * WeightG;
  195. b := (AColor shr 16) * WeightB;
  196. if DoInvert then
  197. begin
  198. r := 195 - r;
  199. g := 390 - g;
  200. b := 65 - b;
  201. x := 195 - x;
  202. y := 390 - y;
  203. z := 65 - z;
  204. end;
  205. s := Sqrt(Sqr(b) + Sqr(g) + Sqr(r));
  206. if s < 0.01 then s := 0.01;
  207. q := (r * x + g * y + b * z) / S;
  208. x := Q / S * r - x;
  209. y := Q / S * g - y;
  210. z := Q / S * b - z;
  211. w := Sqrt(Sqr(Threshold) - Sqr(x) - Sqr(y) - Sqr(z));
  212. r := (q - w) * r / s;
  213. g := (q - w) * g / s;
  214. b := (q - w) * b / s;
  215. if DoInvert then
  216. begin
  217. r := 195 - r;
  218. g := 390 - g;
  219. b := 65 - b;
  220. end;
  221. if r < 0 then r := 0 else if r > 195 then r := 195;
  222. if g < 0 then g := 0 else if g > 390 then g := 390;
  223. if b < 0 then b := 0 else if b > 65 then b := 65;
  224. RR := Trunc(r * (1 / WeightR) + 0.5);
  225. GG := Trunc(g * (1 / WeightG) + 0.5);
  226. BB := Trunc(b * (1 / WeightB) + 0.5);
  227. if RR > $FF then RR := $FF else if RR < 0 then RR := 0;
  228. if GG > $FF then GG := $FF else if GG < 0 then GG := 0;
  229. if BB > $FF then BB := $FF else if BB < 0 then BB := 0;
  230. AColor := (BB and $FF) shl 16 or (GG and $FF) shl 8 or (RR and $FF);
  231. end;
  232. procedure SetContrast(var Color: TColor; BkgndColor: TColor; Threshold: Integer);
  233. var
  234. t: Single;
  235. begin
  236. if Color < 0 then Color := GetSysColor(Color and $FF);
  237. if BkgndColor < 0 then BkgndColor := GetSysColor(BkgndColor and $FF);
  238. t := Threshold;
  239. if not IsContrastEnough(Color, BkgndColor, True, t) then
  240. AdjustContrast(Integer(Color), BkgndColor, t);
  241. end;
  242. const
  243. // This differs from PasTools as we use larger menu fonts
  244. OurDesignTimeTextHeight = 15;
  245. var
  246. LastFontName: string = '';
  247. LastFontHeight: Integer = -1;
  248. LastTextHeight: Integer = -1;
  249. function TBXScaleByTextHeightRunTime(Canvas: TCanvas; Dimension: Integer): Integer;
  250. begin
  251. // This should be called from the GUI thread only.
  252. // See ScaleByTextHeightRunTime in PasTools.
  253. if (LastTextHeight < 0) or
  254. (LastFontName <> Canvas.Font.Name) or
  255. (LastFontHeight <> Canvas.Font.Height) then
  256. begin
  257. LastTextHeight := Canvas.TextHeight('0');
  258. LastFontName := Canvas.Font.Name;
  259. LastFontHeight := Canvas.Font.Height;
  260. end;
  261. if LastTextHeight <> OurDesignTimeTextHeight then
  262. begin
  263. Dimension := MulDiv(Dimension, LastTextHeight, OurDesignTimeTextHeight);
  264. end;
  265. Result := Dimension;
  266. end;
  267. { Drawing routines }
  268. function GetBGR(C: TColorRef): Cardinal;
  269. asm
  270. MOV ECX,EAX // this function swaps R and B bytes in ABGR
  271. SHR EAX,16
  272. XCHG AL,CL
  273. MOV AH,$00 // and writes $FF into A component
  274. SHL EAX,16
  275. MOV AX,CX
  276. end;
  277. function CreatePenEx(Color: TColor): HPen;
  278. begin
  279. if Color = clNone then Result := CreatePen(PS_NULL, 1, 0)
  280. else if Color < 0 then Result := CreatePen(PS_SOLID, 1, GetSysColor(Color and $000000FF))
  281. else Result := CreatePen(PS_SOLID, 1, Color);
  282. end;
  283. function CreateBrushEx(Color: TColor): HBrush;
  284. var
  285. LB: TLogBrush;
  286. begin
  287. if Color = clNone then
  288. begin
  289. LB.lbStyle := BS_HOLLOW;
  290. Result := CreateBrushIndirect(LB);
  291. end
  292. else begin
  293. if Color < 0 then Color := GetSysColor(Color and $000000FF);
  294. Result := CreateSolidBrush(Color);
  295. end;
  296. end;
  297. function FillRectEx(DC: HDC; const Rect: TRect; Color: TColor): Boolean;
  298. var
  299. Brush: HBRUSH;
  300. begin
  301. Result := Color <> clNone;
  302. if Result then
  303. begin
  304. if Color < 0 then Brush := GetSysColorBrush(Color and $000000FF)
  305. else Brush := CreateSolidBrush(Color);
  306. Windows.FillRect(DC, Rect, Brush);
  307. if Color >= 0 then DeleteObject(Brush);
  308. end;
  309. end;
  310. function FrameRectEx(DC: HDC; var Rect: TRect; Color: TColor; Adjust: Boolean): Boolean;
  311. var
  312. Brush: HBRUSH;
  313. begin
  314. Result := Color <> clNone;
  315. if Result then
  316. begin
  317. if Color < 0 then Brush := GetSysColorBrush(Color and $000000FF)
  318. else Brush := CreateSolidBrush(Color);
  319. Windows.FrameRect(DC, Rect, Brush);
  320. if Color >= 0 then DeleteObject(Brush);
  321. end;
  322. if Adjust then with Rect do
  323. begin
  324. Inc(Left); Dec(Right);
  325. Inc(Top); Dec(Bottom);
  326. end;
  327. end;
  328. procedure DrawLineEx(DC: HDC; X1, Y1, X2, Y2: Integer; Color: TColor);
  329. var
  330. OldPen, Pen: HPen;
  331. begin
  332. Pen := CreatePen(PS_SOLID, 1, ColorToRGB(Color));
  333. OldPen := SelectObject(DC, Pen);
  334. Windows.MoveToEx(DC, X1, Y1, nil);
  335. Windows.LineTo(DC, X2, Y2);
  336. SelectObject(DC, OldPen);
  337. DeleteObject(Pen);
  338. end;
  339. function PolyLineEx(DC: HDC; const Points: array of TPoint; Color: TColor): Boolean; overload;
  340. var
  341. Pen, OldPen: HPEN;
  342. begin
  343. Result := Color <> clNone;
  344. if Result then
  345. begin
  346. if Color < 0 then Color := GetSysColor(Color and $FF);
  347. Pen := CreatePen(PS_SOLID, 1, Color);
  348. OldPen := SelectObject(DC, Pen);
  349. Windows.Polyline(DC, PPoints(@Points[0])^, Length(Points));
  350. SelectObject(DC, OldPen);
  351. DeleteObject(Pen);
  352. end;
  353. end;
  354. procedure PolygonEx(DC: HDC; const Points: array of TPoint; OutlineColor, FillColor: TColor);
  355. var
  356. OldBrush, Brush: HBrush;
  357. OldPen, Pen: HPen;
  358. begin
  359. if (OutlineColor = clNone) and (FillColor = clNone) then Exit;
  360. Pen := CreatePenEx(OutlineColor);
  361. Brush := CreateBrushEx(FillColor);
  362. OldPen := SelectObject(DC, Pen);
  363. OldBrush := SelectObject(DC, Brush);
  364. Windows.Polygon(DC, PPoints(@Points[0])^, Length(Points));
  365. SelectObject(DC, OldBrush);
  366. SelectObject(DC, OldPen);
  367. DeleteObject(Brush);
  368. DeleteObject(Pen);
  369. end;
  370. procedure RoundRectEx(DC: HDC; Left, Top, Right, Bottom: Integer;
  371. EllipseWidth, EllipseHeight, OutlineColor, FillColor: TColor);
  372. var
  373. OldBrush, Brush: HBrush;
  374. OldPen, Pen: HPen;
  375. begin
  376. if (OutlineColor = clNone) and (FillColor = clNone) then Exit;
  377. Pen := CreatePenEx(OutlineColor);
  378. Brush := CreateBrushEx(FillColor);
  379. OldPen := SelectObject(DC, Pen);
  380. OldBrush := SelectObject(DC, Brush);
  381. Windows.RoundRect(DC, Left, Top, Right, Bottom, EllipseWidth, EllipseHeight);
  382. SelectObject(DC, OldBrush);
  383. SelectObject(DC, OldPen);
  384. DeleteObject(Brush);
  385. DeleteObject(Pen);
  386. end;
  387. procedure EllipseEx(DC: HDC; Left, Top, Right, Bottom: Integer; OutlineColor, FillColor: TColor);
  388. var
  389. OldBrush, Brush: HBrush;
  390. OldPen, Pen: HPen;
  391. begin
  392. Pen := CreatePenEx(OutlineColor);
  393. Brush := CreateBrushEx(FillColor);
  394. OldPen := SelectObject(DC, Pen);
  395. OldBrush := SelectObject(DC, Brush);
  396. Windows.Ellipse(DC, Left, Top, Right, Bottom);
  397. SelectObject(DC, OldBrush);
  398. SelectObject(DC, OldPen);
  399. DeleteObject(Brush);
  400. DeleteObject(Pen);
  401. end;
  402. procedure FillLongword(var X; Count: Integer; Value: Longword);
  403. asm
  404. // EAX = X; EDX = Count; ECX = Value
  405. PUSH EDI
  406. MOV EDI,EAX // Point EDI to destination
  407. MOV EAX,ECX
  408. MOV ECX,EDX
  409. TEST ECX,ECX
  410. JS @exit
  411. REP STOSD // Fill count dwords
  412. @exit:
  413. POP EDI
  414. end;
  415. procedure MoveLongword(const Source; var Dest; Count: Integer);
  416. asm
  417. // EAX = Source; EDX = Dest; ECX = Count
  418. PUSH ESI
  419. PUSH EDI
  420. MOV ESI,EAX // Source
  421. MOV EDI,EDX // Destination
  422. MOV EAX,ECX // Counter
  423. CMP EDI,ESI
  424. JE @exit
  425. REP MOVSD
  426. @exit:
  427. POP EDI
  428. POP ESI
  429. end;
  430. procedure DrawTBXIcon(Canvas: TCanvas; const R: TRect;
  431. ImageList: TCustomImageList; ImageIndex: Integer);
  432. begin
  433. ImageList.Draw(Canvas, R.Left, R.Top, ImageIndex);
  434. end;
  435. procedure DrawTBXIconShadow(Canvas: TCanvas; const R: TRect;
  436. ImageList: TCustomImageList; ImageIndex: Integer; Density: Integer);
  437. const
  438. D_DIV: array [0..2] of Cardinal = (3, 8, 20);
  439. D_ADD: array [0..2] of Cardinal = (255 - 255 div 3, 255 - 255 div 8, 255 - 255 div 20);
  440. var
  441. ImageWidth, ImageHeight: Integer;
  442. I, J: Integer;
  443. Src, Dst: ^Cardinal;
  444. S, C, CBRB, CBG: Cardinal;
  445. begin
  446. Assert(Density in [0..2]);
  447. ImageWidth := R.Right - R.Left;
  448. ImageHeight := R.Bottom - R.Top;
  449. with ImageList do
  450. begin
  451. if Width < ImageWidth then ImageWidth := Width;
  452. if Height < ImageHeight then ImageHeight := Height;
  453. end;
  454. StockBitmap1.Width := ImageWidth;
  455. StockBitmap1.Height := ImageHeight;
  456. StockBitmap2.Width := ImageWidth;
  457. StockBitmap2.Height := ImageHeight;
  458. BitBlt(StockBitmap1.Canvas.Handle, 0, 0, ImageWidth, ImageHeight,
  459. Canvas.Handle, R.Left, R.Top, SRCCOPY);
  460. BitBlt(StockBitmap2.Canvas.Handle, 0, 0, ImageWidth, ImageHeight,
  461. Canvas.Handle, R.Left, R.Top, SRCCOPY);
  462. ImageList.Draw(StockBitmap2.Canvas, 0, 0, ImageIndex, True);
  463. for J := 0 to ImageHeight - 1 do
  464. begin
  465. Src := StockBitmap2.ScanLine[J];
  466. Dst := StockBitmap1.ScanLine[J];
  467. for I := 0 to ImageWidth - 1 do
  468. begin
  469. S := Src^;
  470. if S <> Dst^ then
  471. begin
  472. // The algorithm does not work well against a black background
  473. if ColorIntensity(Dst^) < 80 then
  474. Dst^ := $00909090;
  475. CBRB := Dst^ and $00FF00FF;
  476. CBG := Dst^ and $0000FF00;
  477. C := ((S and $00FF0000) shr 16 * 29 + (S and $0000FF00) shr 8 * 150 +
  478. (S and $000000FF) * 76) shr 8;
  479. C := C div D_DIV[Density] + D_ADD[Density];
  480. Dst^ := ((CBRB * C and $FF00FF00) or (CBG * C and $00FF0000)) shr 8;
  481. end;
  482. Inc(Src);
  483. Inc(Dst);
  484. end;
  485. end;
  486. BitBlt(Canvas.Handle, R.Left, R.Top, ImageWidth, ImageHeight,
  487. StockBitmap1.Canvas.Handle, 0, 0, SRCCOPY);
  488. end;
  489. procedure DrawTBXIconFlatShadow(Canvas: TCanvas; const R: TRect;
  490. ImageList: TCustomImageList; ImageIndex: Integer; ShadowColor: TColor);
  491. const
  492. CShadowThreshold = 180 * 256;
  493. var
  494. ImageWidth, ImageHeight: Integer;
  495. I, J: Integer;
  496. P: ^Cardinal;
  497. C: Cardinal;
  498. SrcDC, DstDC: HDC;
  499. begin
  500. ImageWidth := R.Right - R.Left;
  501. ImageHeight := R.Bottom - R.Top;
  502. with ImageList do
  503. begin
  504. if Width < ImageWidth then ImageWidth := Width;
  505. if Height < ImageHeight then ImageHeight := Height;
  506. end;
  507. StockBitmap2.Width := ImageWidth;
  508. StockBitmap2.Height := ImageHeight;
  509. StockBitmap2.Canvas.Brush.Color := clWhite;
  510. StockBitmap2.Canvas.FillRect(Rect(0, 0, ImageWidth, ImageHeight));
  511. ImageList.Draw(StockBitmap2.Canvas, 0, 0, ImageIndex, True);
  512. for J := 0 to ImageHeight - 1 do
  513. begin
  514. P := StockBitmap2.ScanLine[J];
  515. for I := 0 to ImageWidth - 1 do
  516. begin
  517. C := P^ and $00FFFFFF;
  518. if C <> $0 then
  519. begin
  520. C := (C and $FF0000) shr 16 * 76 + (C and $00FF00) shr 8 * 150 + (C and $0000FF) * 29;
  521. if C > CShadowThreshold then P^ := $00FFFFFF
  522. else P^ := $00000000;
  523. end;
  524. Inc(P);
  525. end;
  526. end;
  527. StockMonoBitmap.Width := ImageWidth;
  528. StockMonoBitmap.Height := ImageHeight;
  529. StockMonoBitmap.Canvas.Brush.Color := clBlack;
  530. BitBlt(StockMonoBitmap.Canvas.Handle, 0, 0, ImageWidth, ImageHeight,
  531. StockBitmap2.Canvas.Handle, 0, 0, SRCCOPY);
  532. SrcDC := StockMonoBitmap.Canvas.Handle;
  533. Canvas.Brush.Color := ColorToRGB(ShadowColor);
  534. DstDC := Canvas.Handle;
  535. Windows.SetTextColor(DstDC, clWhite);
  536. Windows.SetBkColor(DstDC, clBlack);
  537. BitBlt(DstDC, R.Left, R.Top, ImageWidth, ImageHeight, SrcDC, 0, 0, ROP_DSPDxax);
  538. end;
  539. procedure DrawGlyph(DC: HDC; X, Y: Integer; ImageList: TCustomImageList; ImageIndex: Integer; Color: TColor);
  540. var
  541. B: TBitmap;
  542. OldTextColor, OldBkColor: Longword;
  543. OldBrush, Brush: HBrush;
  544. begin
  545. if Color = clNone then Exit;
  546. B := TBitmap.Create;
  547. B.Monochrome := True;
  548. ImageList.GetBitmap(ImageIndex, B);
  549. OldTextColor := SetTextColor(DC, clBlack);
  550. OldBkColor := SetBkColor(DC, clWhite);
  551. if Color < 0 then Brush := GetSysColorBrush(Color and $FF)
  552. else Brush := CreateSolidBrush(Color);
  553. OldBrush := SelectObject(DC, Brush);
  554. BitBlt(DC, X, Y, ImageList.Width, ImageList.Height, B.Canvas.Handle, 0, 0, ROP_DSPDxax);
  555. SelectObject(DC, OldBrush);
  556. if Color >= 0 then DeleteObject(Brush);
  557. SetTextColor(DC, OldTextColor);
  558. SetBkColor(DC, OldBkColor);
  559. B.Free;
  560. end;
  561. procedure DrawGlyph(DC: HDC; const R: TRect; Width, Height: Integer; const Bits; Color: TColor); overload;
  562. var
  563. B: TBitmap;
  564. OldTextColor, OldBkColor: Longword;
  565. OldBrush, Brush: HBrush;
  566. begin
  567. B := TBitmap.Create;
  568. B.Handle := CreateBitmap(8, 8, 1, 1, @Bits);
  569. OldTextColor := SetTextColor(DC, clBlack);
  570. OldBkColor := SetBkColor(DC, clWhite);
  571. if Color < 0 then Brush := GetSysColorBrush(Color and $FF)
  572. else Brush := CreateSolidBrush(Color);
  573. OldBrush := SelectObject(DC, Brush);
  574. BitBlt(DC, (R.Left + R.Right + 1 - Width) div 2, (R.Top + R.Bottom + 1 - Height) div 2, Width, Height, B.Canvas.Handle, 0, 0, ROP_DSPDxax);
  575. SelectObject(DC, OldBrush);
  576. if Color >= 0 then DeleteObject(Brush);
  577. SetTextColor(DC, OldTextColor);
  578. SetBkColor(DC, OldBkColor);
  579. B.Free;
  580. end;
  581. procedure InitializeStock;
  582. var
  583. NonClientMetrics: TNonClientMetrics;
  584. begin
  585. StockBitmap1 := TBitmap.Create;
  586. StockBitmap1.PixelFormat := pf32bit;
  587. StockBitmap2 := TBitmap.Create;
  588. StockBitmap2.PixelFormat := pf32bit;
  589. StockMonoBitmap := TBitmap.Create;
  590. StockMonoBitmap.Monochrome := True;
  591. StockCompatibleBitmap := TBitmap.Create;
  592. StockCompatibleBitmap.Width := 8;
  593. StockCompatibleBitmap.Height := 8;
  594. SmCaptionFont := TFont.Create;
  595. NonClientMetrics.cbSize := SizeOf(NonClientMetrics);
  596. if SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, @NonClientMetrics, 0) then
  597. SmCaptionFont.Handle := CreateFontIndirect(NonClientMetrics.lfSmCaptionFont);
  598. end;
  599. procedure FinalizeStock;
  600. begin
  601. SmCaptionFont.Free;
  602. SmCaptionFont := nil;
  603. StockCompatibleBitmap.Free;
  604. StockMonoBitmap.Free;
  605. StockBitmap2.Free;
  606. StockBitmap1.Free;
  607. end;
  608. procedure RecreateStock;
  609. begin
  610. FinalizeStock;
  611. InitializeStock;
  612. end;
  613. { TShadow } ////////////////////////////////////////////////////////////////////
  614. procedure TShadow.Clear(const R: TRect);
  615. begin
  616. FClearRect := R;
  617. end;
  618. constructor TShadow.Create(const Bounds: TRect; Opacity: Byte; LoColor: Boolean; Edges: TShadowEdges);
  619. begin
  620. inherited Create(nil);
  621. Hide;
  622. ParentWindow := Application.Handle;
  623. BoundsRect := Bounds;
  624. Color := clBtnShadow;
  625. FOpacity := Opacity;
  626. FEdges := Edges;
  627. FSaveBits := False;
  628. if LoColor then FStyle := ssFlat
  629. else FStyle := ssLayered;
  630. end;
  631. procedure TShadow.CreateParams(var Params: TCreateParams);
  632. begin
  633. inherited;
  634. with Params do
  635. begin
  636. Style := (Style and not (WS_CHILD or WS_GROUP or WS_TABSTOP)) or WS_POPUP;
  637. ExStyle := ExStyle or WS_EX_TOOLWINDOW;
  638. if FSaveBits then WindowClass.Style := WindowClass.Style or CS_SAVEBITS;
  639. end;
  640. end;
  641. procedure TShadow.GradB(const R: TRect);
  642. var
  643. J, W, H: Integer;
  644. V: Cardinal;
  645. P: ^Cardinal;
  646. begin
  647. W := R.Right - R.Left;
  648. H := R.Bottom - R.Top;
  649. for J := 0 to H - 1 do
  650. begin
  651. P := FBuffer.ScanLine[J + R.Top];
  652. Inc(P, R.Left);
  653. V := (255 - J shl 8 div H) shl 24;
  654. FillLongword(P^, W, V);
  655. end;
  656. end;
  657. procedure TShadow.GradBL(const R: TRect);
  658. var
  659. I, J, W, H, CX, CY, D, DMax, A, B: Integer;
  660. P: ^Cardinal;
  661. begin
  662. W := R.Right - R.Left;
  663. H := R.Bottom - R.Top;
  664. DMax := W;
  665. if H > W then DMax := H;
  666. CX := DMax - 1;
  667. CY := H - DMax;
  668. for J := 0 to H - 1 do
  669. begin
  670. P := FBuffer.ScanLine[J + R.Top];
  671. Inc(P, R.Left);
  672. for I := 0 to W - 1 do
  673. begin
  674. A := Abs(I - CX);
  675. B := Abs(J - CY);
  676. D := A;
  677. if B > A then D := B;
  678. D := (A + B + D) * 128 div DMax;
  679. if D < 255 then P^ := (255 - D) shl 24
  680. else P^ := 0;
  681. Inc(P);
  682. end;
  683. end;
  684. end;
  685. procedure TShadow.GradBR(const R: TRect);
  686. var
  687. I, J, W, H, CX, CY, D, DMax, A, B: Integer;
  688. P: ^Cardinal;
  689. begin
  690. W := R.Right - R.Left;
  691. H := R.Bottom - R.Top;
  692. DMax := W;
  693. if H > W then DMax := H;
  694. CX := W - DMax;
  695. CY := H - DMax;
  696. for J := 0 to H - 1 do
  697. begin
  698. P := FBuffer.ScanLine[J + R.Top];
  699. Inc(P, R.Left);
  700. for I := 0 to W - 1 do
  701. begin
  702. A := Abs(I - CX);
  703. B := Abs(J - CY);
  704. D := A;
  705. if B > A then D := B;
  706. D := (A + B + D) * 128 div DMax;
  707. if D < 255 then P^ := (255 - D) shl 24
  708. else P^ := 0;
  709. Inc(P);
  710. end;
  711. end;
  712. end;
  713. procedure TShadow.GradR(const R: TRect);
  714. var
  715. I, J, W: Integer;
  716. P: ^Cardinal;
  717. ScanLine: array of Cardinal;
  718. begin
  719. W := R.Right - R.Left;
  720. SetLength(ScanLine, W);
  721. for I := 0 to W - 1 do
  722. ScanLine[I] :=(255 - I shl 8 div W) shl 24;
  723. for J := R.Top to R.Bottom - 1 do
  724. begin
  725. P := FBuffer.ScanLine[J];
  726. Inc(P, R.Left);
  727. MoveLongword(ScanLine[0], P^, W);
  728. end;
  729. end;
  730. procedure TShadow.GradTR(const R: TRect);
  731. var
  732. I, J, W, H, CX, CY, D, DMax, A, B: Integer;
  733. P: ^Cardinal;
  734. begin
  735. W := R.Right - R.Left;
  736. H := R.Bottom - R.Top;
  737. DMax := W;
  738. if H > W then DMax := H;
  739. CX := W - DMax;
  740. CY := DMax - 1;
  741. for J := 0 to H - 1 do
  742. begin
  743. P := FBuffer.ScanLine[J + R.Top];
  744. Inc(P, R.Left);
  745. for I := 0 to W - 1 do
  746. begin
  747. A := Abs(I - CX);
  748. B := Abs(J - CY);
  749. D := A;
  750. if B > A then D := B;
  751. D := (A + B + D) * 128 div DMax;
  752. if D < 255 then P^ := (255 - D) shl 24
  753. else P^ := 0;
  754. Inc(P);
  755. end;
  756. end;
  757. end;
  758. procedure TShadow.Render;
  759. var
  760. DstDC: HDC;
  761. SrcPos, DstPos: TPoint;
  762. TheSize: TSize;
  763. BlendFunc: TBlendFunction;
  764. begin
  765. if FStyle <> ssLayered then Exit;
  766. SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) or $00080000{WS_EX_LAYERED});
  767. DstDC := GetDC(0);
  768. try
  769. SrcPos := Point(0, 0);
  770. with BoundsRect do
  771. begin
  772. DstPos := Point(Left, Top);
  773. TheSize.cx := Right - Left;
  774. TheSize.cy := Bottom - Top;
  775. end;
  776. BlendFunc.BlendOp := 0;
  777. BlendFunc.BlendFlags := 0;
  778. BlendFunc.SourceConstantAlpha := FOpacity;
  779. BlendFunc.AlphaFormat := 1;
  780. FBuffer := TBitmap.Create;
  781. FBuffer.PixelFormat := pf32bit;
  782. FBuffer.Width := TheSize.cx;
  783. FBuffer.Height := TheSize.cy;
  784. FillBuffer;
  785. UpdateLayeredWindow(
  786. Handle,
  787. DstDC,
  788. @DstPos,
  789. @TheSize,
  790. FBuffer.Canvas.Handle,
  791. @SrcPos,
  792. 0,
  793. @BlendFunc,
  794. $00000002{ULW_ALPHA});
  795. FBuffer.Free;
  796. finally
  797. ReleaseDC(0, DstDC);
  798. end;
  799. end;
  800. procedure TShadow.Show(ParentHandle: HWND);
  801. begin
  802. SetWindowPos(Handle, ParentHandle, 0, 0, 0, 0,
  803. SWP_NOACTIVATE or SWP_NOSENDCHANGING or SWP_NOMOVE or
  804. SWP_NOOWNERZORDER or SWP_NOSIZE or SWP_SHOWWINDOW);
  805. end;
  806. procedure TShadow.WMNCHitTest(var Message: TMessage);
  807. begin
  808. Message.Result := HTTRANSPARENT;
  809. end;
  810. { THorzShadow }
  811. procedure THorzShadow.FillBuffer;
  812. var
  813. R: TRect;
  814. L1, L2, L3: Integer;
  815. begin
  816. if seTopLeft in FEdges then L1 := Height else L1 := 0;
  817. if seBottomRight in FEdges then L3 := Height else L3 := 0;
  818. if L1 + L3 > Width then
  819. begin
  820. if (L1 > 0) and (L3 > 0) then
  821. begin
  822. L1 := Width div 2;
  823. L3 := L1;
  824. end
  825. else if L1 > 0 then L1 := Width
  826. else if L3 > 0 then L3 := Width;
  827. end;
  828. L2 := Width - L1 - L3;
  829. R := Rect(0, 0, Width, Height);
  830. R.Right := R.Left + L1;
  831. if L1 > 0 then GradBL(R);
  832. R.Left := R.Right;
  833. R.Right := R.Left + L2;
  834. if L2 > 0 then GradB(R);
  835. if L3 > 0 then
  836. begin
  837. R.Left := R.Right;
  838. R.Right := R.Left + L3;
  839. GradBR(R);
  840. end;
  841. end;
  842. { TVertShadow }
  843. procedure TVertShadow.FillBuffer;
  844. var
  845. R: TRect;
  846. L1, L2, L3: Integer;
  847. begin
  848. if seTopLeft in FEdges then L1 := Width else L1 := 0;
  849. if seBottomRight in FEdges then L3 := Width else L3 := 0;
  850. if L1 + L3 > Height then
  851. begin
  852. if (L1 > 0) and (L3 > 0) then
  853. begin
  854. L1 := Height div 2;
  855. L3 := L1;
  856. end
  857. else if L1 > 0 then L1 := Height
  858. else if L3 > 0 then L3 := Height;
  859. end;
  860. L2 := Height - L1 - L3;
  861. R := Rect(0, 0, Width, Height);
  862. R.Bottom := R.Top + L1;
  863. if L1 > 0 then GradTR(R);
  864. R.Top := R.Bottom;
  865. R.Bottom := R.Top + L2;
  866. if L2 > 0 then GradR(R);
  867. if L3 > 0 then
  868. begin
  869. R.Top := R.Bottom;
  870. R.Bottom := R.Top + L3;
  871. GradBR(R);
  872. end;
  873. end;
  874. { TShadows }
  875. constructor TShadows.Create(R1, R2: TRect; TheSize: Integer; Opacity: Byte; LoColor: Boolean);
  876. var
  877. R: TRect;
  878. R1Valid, R2Valid: Boolean;
  879. begin
  880. if LoColor then
  881. begin
  882. TheSize := TheSize div 2;
  883. end;
  884. R1Valid := not IsRectEmpty(R1);
  885. R2Valid := not IsRectEmpty(R2);
  886. if not (R1Valid or R2Valid) then Exit;
  887. if R1Valid xor R2Valid then
  888. begin
  889. { A simple square shadow }
  890. if R1Valid then R := R1 else R:= R2;
  891. with R do
  892. begin
  893. V1 := TVertShadow.Create(Rect(Right, Top + TheSize, Right + TheSize, Bottom), Opacity, LoColor, [seTopLeft]);
  894. H1 := THorzShadow.Create(Rect(Left + TheSize, Bottom, Right + TheSize, Bottom + TheSize), Opacity, LoColor, [seTopLeft, seBottomRight])
  895. end;
  896. end
  897. else
  898. begin
  899. if (R1.Bottom <= R2.Top + 2) or (R1.Top >= R2.Bottom - 2) then
  900. begin
  901. if R1.Top > R2.Top then
  902. begin
  903. R := R2;
  904. R2 := R1;
  905. R1 := R;
  906. end;
  907. if R1.Left + TheSize < R2.Left then
  908. H1 := THorzShadow.Create(Rect(R1.Left + TheSize, R1.Bottom, R2.Left, R1.Bottom + TheSize), Opacity, LoColor, [seTopLeft]);
  909. H2 := THorzShadow.Create(Rect(R2.Left + TheSize, R2.Bottom, R2.Right + TheSize, R2.Bottom + TheSize), Opacity, LoColor, [seTopLeft, seBottomRight]);
  910. V1 := TVertShadow.Create(Rect(R1.Right, R1.Top + TheSize, R1.Right + TheSize, R1.Bottom), Opacity, LoColor, [seTopLeft]);
  911. if R1.Right > R2.Right then
  912. H3 := THorzShadow.Create(Rect(R2.Right, R1.Bottom, R1.Right + TheSize, R1.Bottom + TheSize), Opacity, LoColor, [seTopLeft, seBottomRight]);
  913. if R1.Right + TheSize < R2.Right then
  914. V2 := TVertShadow.Create(Rect(R2.Right, R2.Top + TheSize, R2.Right + TheSize, R2.Bottom), Opacity, LoColor, [seTopLeft])
  915. else
  916. V2 := TVertShadow.Create(Rect(R2.Right, R2.Top + 1, R2.Right + TheSize, R2.Bottom), Opacity, LoColor, []);
  917. end
  918. else if (R1.Right <= R2.Left + 2) or (R1.Left >= R2.Right - 2) then
  919. begin
  920. if R1.Left > R2.Left then
  921. begin
  922. R := R2;
  923. R2 := R1;
  924. R1 := R;
  925. end;
  926. if R1.Top + TheSize < R2.Top then
  927. V1 := TVertShadow.Create(Rect(R1.Right, R1.Top + TheSize, R1.Right + TheSize, R2.Top), Opacity, LoColor, [seTopLeft]);
  928. V2 := TVertShadow.Create(Rect(R2.Right, R2.Top + TheSize, R2.Right + TheSize, R2.Bottom + TheSize), Opacity, LoColor, [seTopLeft, seBottomRight]);
  929. H1 := THorzShadow.Create(Rect(R1.Left + TheSize, R1.Bottom, R1.Right, R1.Bottom + TheSize), Opacity, LoColor, [seTopLeft]);
  930. if R1.Bottom > R2.Bottom then
  931. V3 := TVertShadow.Create(Rect(R1.Right, R2.Bottom, R1.Right + TheSize, R1.Bottom + TheSize), Opacity, LoColor, [seTopLeft, seBottomRight]);
  932. if R1.Bottom + TheSize < R2.Bottom then
  933. H2 := THorzShadow.Create(Rect(R2.Left + TheSize, R2.Bottom, R2.Right, R2.Bottom + TheSize), Opacity, LoColor, [seTopLeft])
  934. else
  935. H2 := THorzShadow.Create(Rect(R2.Left, R2.Bottom, R2.Right, R2.Bottom + TheSize), Opacity, LoColor, []);
  936. end;
  937. end;
  938. if V1 <> nil then V1.Render;
  939. if H1 <> nil then H1.Render;
  940. if V2 <> nil then V2.Render;
  941. if H2 <> nil then H2.Render;
  942. if V3 <> nil then V3.Render;
  943. if H3 <> nil then H3.Render;
  944. SetSaveBits(True);
  945. end;
  946. destructor TShadows.Destroy;
  947. begin
  948. H3.Free;
  949. V3.Free;
  950. H2.Free;
  951. V2.Free;
  952. H1.Free;
  953. V1.Free;
  954. inherited;
  955. end;
  956. procedure TShadows.SetSaveBits(Value: Boolean);
  957. begin
  958. FSaveBits := Value;
  959. if V1 <> nil then V1.FSaveBits := Value;
  960. if H1 <> nil then H1.FSaveBits := Value;
  961. if V2 <> nil then V2.FSaveBits := Value;
  962. if H2 <> nil then H2.FSaveBits := Value;
  963. if V3 <> nil then V3.FSaveBits := Value;
  964. if H3 <> nil then H3.FSaveBits := Value;
  965. end;
  966. procedure TShadows.Show(ParentHandle: HWND);
  967. begin
  968. if V1 <> nil then V1.Show(ParentHandle);
  969. if H1 <> nil then H1.Show(ParentHandle);
  970. if V2 <> nil then V2.Show(ParentHandle);
  971. if H2 <> nil then H2.Show(ParentHandle);
  972. if V3 <> nil then V3.Show(ParentHandle);
  973. if H3 <> nil then H3.Show(ParentHandle);
  974. end;
  975. initialization
  976. InitializeStock;
  977. finalization
  978. FinalizeStock;
  979. end.