TBXUtils.pas 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096
  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. CBRB := Dst^ and $00FF00FF;
  473. CBG := Dst^ and $0000FF00;
  474. C := ((S and $00FF0000) shr 16 * 29 + (S and $0000FF00) shr 8 * 150 +
  475. (S and $000000FF) * 76) shr 8;
  476. C := C div D_DIV[Density] + D_ADD[Density];
  477. Dst^ := ((CBRB * C and $FF00FF00) or (CBG * C and $00FF0000)) shr 8;
  478. end;
  479. Inc(Src);
  480. Inc(Dst);
  481. end;
  482. end;
  483. BitBlt(Canvas.Handle, R.Left, R.Top, ImageWidth, ImageHeight,
  484. StockBitmap1.Canvas.Handle, 0, 0, SRCCOPY);
  485. end;
  486. procedure DrawTBXIconFlatShadow(Canvas: TCanvas; const R: TRect;
  487. ImageList: TCustomImageList; ImageIndex: Integer; ShadowColor: TColor);
  488. const
  489. CShadowThreshold = 180 * 256;
  490. var
  491. ImageWidth, ImageHeight: Integer;
  492. I, J: Integer;
  493. P: ^Cardinal;
  494. C: Cardinal;
  495. SrcDC, DstDC: HDC;
  496. begin
  497. ImageWidth := R.Right - R.Left;
  498. ImageHeight := R.Bottom - R.Top;
  499. with ImageList do
  500. begin
  501. if Width < ImageWidth then ImageWidth := Width;
  502. if Height < ImageHeight then ImageHeight := Height;
  503. end;
  504. StockBitmap2.Width := ImageWidth;
  505. StockBitmap2.Height := ImageHeight;
  506. StockBitmap2.Canvas.Brush.Color := clWhite;
  507. StockBitmap2.Canvas.FillRect(Rect(0, 0, ImageWidth, ImageHeight));
  508. ImageList.Draw(StockBitmap2.Canvas, 0, 0, ImageIndex, True);
  509. for J := 0 to ImageHeight - 1 do
  510. begin
  511. P := StockBitmap2.ScanLine[J];
  512. for I := 0 to ImageWidth - 1 do
  513. begin
  514. C := P^ and $00FFFFFF;
  515. if C <> $0 then
  516. begin
  517. C := (C and $FF0000) shr 16 * 76 + (C and $00FF00) shr 8 * 150 + (C and $0000FF) * 29;
  518. if C > CShadowThreshold then P^ := $00FFFFFF
  519. else P^ := $00000000;
  520. end;
  521. Inc(P);
  522. end;
  523. end;
  524. StockMonoBitmap.Width := ImageWidth;
  525. StockMonoBitmap.Height := ImageHeight;
  526. StockMonoBitmap.Canvas.Brush.Color := clBlack;
  527. BitBlt(StockMonoBitmap.Canvas.Handle, 0, 0, ImageWidth, ImageHeight,
  528. StockBitmap2.Canvas.Handle, 0, 0, SRCCOPY);
  529. SrcDC := StockMonoBitmap.Canvas.Handle;
  530. Canvas.Brush.Color := ColorToRGB(ShadowColor);
  531. DstDC := Canvas.Handle;
  532. Windows.SetTextColor(DstDC, clWhite);
  533. Windows.SetBkColor(DstDC, clBlack);
  534. BitBlt(DstDC, R.Left, R.Top, ImageWidth, ImageHeight, SrcDC, 0, 0, ROP_DSPDxax);
  535. end;
  536. procedure DrawGlyph(DC: HDC; X, Y: Integer; ImageList: TCustomImageList; ImageIndex: Integer; Color: TColor);
  537. var
  538. B: TBitmap;
  539. OldTextColor, OldBkColor: Longword;
  540. OldBrush, Brush: HBrush;
  541. begin
  542. if Color = clNone then Exit;
  543. B := TBitmap.Create;
  544. B.Monochrome := True;
  545. ImageList.GetBitmap(ImageIndex, B);
  546. OldTextColor := SetTextColor(DC, clBlack);
  547. OldBkColor := SetBkColor(DC, clWhite);
  548. if Color < 0 then Brush := GetSysColorBrush(Color and $FF)
  549. else Brush := CreateSolidBrush(Color);
  550. OldBrush := SelectObject(DC, Brush);
  551. BitBlt(DC, X, Y, ImageList.Width, ImageList.Height, B.Canvas.Handle, 0, 0, ROP_DSPDxax);
  552. SelectObject(DC, OldBrush);
  553. if Color >= 0 then DeleteObject(Brush);
  554. SetTextColor(DC, OldTextColor);
  555. SetBkColor(DC, OldBkColor);
  556. B.Free;
  557. end;
  558. procedure DrawGlyph(DC: HDC; const R: TRect; Width, Height: Integer; const Bits; Color: TColor); overload;
  559. var
  560. B: TBitmap;
  561. OldTextColor, OldBkColor: Longword;
  562. OldBrush, Brush: HBrush;
  563. begin
  564. B := TBitmap.Create;
  565. B.Handle := CreateBitmap(8, 8, 1, 1, @Bits);
  566. OldTextColor := SetTextColor(DC, clBlack);
  567. OldBkColor := SetBkColor(DC, clWhite);
  568. if Color < 0 then Brush := GetSysColorBrush(Color and $FF)
  569. else Brush := CreateSolidBrush(Color);
  570. OldBrush := SelectObject(DC, Brush);
  571. 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);
  572. SelectObject(DC, OldBrush);
  573. if Color >= 0 then DeleteObject(Brush);
  574. SetTextColor(DC, OldTextColor);
  575. SetBkColor(DC, OldBkColor);
  576. B.Free;
  577. end;
  578. procedure InitializeStock;
  579. var
  580. NonClientMetrics: TNonClientMetrics;
  581. begin
  582. StockBitmap1 := TBitmap.Create;
  583. StockBitmap1.PixelFormat := pf32bit;
  584. StockBitmap2 := TBitmap.Create;
  585. StockBitmap2.PixelFormat := pf32bit;
  586. StockMonoBitmap := TBitmap.Create;
  587. StockMonoBitmap.Monochrome := True;
  588. StockCompatibleBitmap := TBitmap.Create;
  589. StockCompatibleBitmap.Width := 8;
  590. StockCompatibleBitmap.Height := 8;
  591. SmCaptionFont := TFont.Create;
  592. NonClientMetrics.cbSize := SizeOf(NonClientMetrics);
  593. if SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, @NonClientMetrics, 0) then
  594. SmCaptionFont.Handle := CreateFontIndirect(NonClientMetrics.lfSmCaptionFont);
  595. end;
  596. procedure FinalizeStock;
  597. begin
  598. SmCaptionFont.Free;
  599. SmCaptionFont := nil;
  600. StockCompatibleBitmap.Free;
  601. StockMonoBitmap.Free;
  602. StockBitmap2.Free;
  603. StockBitmap1.Free;
  604. end;
  605. procedure RecreateStock;
  606. begin
  607. FinalizeStock;
  608. InitializeStock;
  609. end;
  610. { TShadow } ////////////////////////////////////////////////////////////////////
  611. procedure TShadow.Clear(const R: TRect);
  612. begin
  613. FClearRect := R;
  614. end;
  615. constructor TShadow.Create(const Bounds: TRect; Opacity: Byte; LoColor: Boolean; Edges: TShadowEdges);
  616. begin
  617. inherited Create(nil);
  618. Hide;
  619. ParentWindow := Application.Handle;
  620. BoundsRect := Bounds;
  621. Color := clBtnShadow;
  622. FOpacity := Opacity;
  623. FEdges := Edges;
  624. FSaveBits := False;
  625. if LoColor then FStyle := ssFlat
  626. else FStyle := ssLayered;
  627. end;
  628. procedure TShadow.CreateParams(var Params: TCreateParams);
  629. begin
  630. inherited;
  631. with Params do
  632. begin
  633. Style := (Style and not (WS_CHILD or WS_GROUP or WS_TABSTOP)) or WS_POPUP;
  634. ExStyle := ExStyle or WS_EX_TOOLWINDOW;
  635. if FSaveBits then WindowClass.Style := WindowClass.Style or CS_SAVEBITS;
  636. end;
  637. end;
  638. procedure TShadow.GradB(const R: TRect);
  639. var
  640. J, W, H: Integer;
  641. V: Cardinal;
  642. P: ^Cardinal;
  643. begin
  644. W := R.Right - R.Left;
  645. H := R.Bottom - R.Top;
  646. for J := 0 to H - 1 do
  647. begin
  648. P := FBuffer.ScanLine[J + R.Top];
  649. Inc(P, R.Left);
  650. V := (255 - J shl 8 div H) shl 24;
  651. FillLongword(P^, W, V);
  652. end;
  653. end;
  654. procedure TShadow.GradBL(const R: TRect);
  655. var
  656. I, J, W, H, CX, CY, D, DMax, A, B: Integer;
  657. P: ^Cardinal;
  658. begin
  659. W := R.Right - R.Left;
  660. H := R.Bottom - R.Top;
  661. DMax := W;
  662. if H > W then DMax := H;
  663. CX := DMax - 1;
  664. CY := H - DMax;
  665. for J := 0 to H - 1 do
  666. begin
  667. P := FBuffer.ScanLine[J + R.Top];
  668. Inc(P, R.Left);
  669. for I := 0 to W - 1 do
  670. begin
  671. A := Abs(I - CX);
  672. B := Abs(J - CY);
  673. D := A;
  674. if B > A then D := B;
  675. D := (A + B + D) * 128 div DMax;
  676. if D < 255 then P^ := (255 - D) shl 24
  677. else P^ := 0;
  678. Inc(P);
  679. end;
  680. end;
  681. end;
  682. procedure TShadow.GradBR(const R: TRect);
  683. var
  684. I, J, W, H, CX, CY, D, DMax, A, B: Integer;
  685. P: ^Cardinal;
  686. begin
  687. W := R.Right - R.Left;
  688. H := R.Bottom - R.Top;
  689. DMax := W;
  690. if H > W then DMax := H;
  691. CX := W - DMax;
  692. CY := H - DMax;
  693. for J := 0 to H - 1 do
  694. begin
  695. P := FBuffer.ScanLine[J + R.Top];
  696. Inc(P, R.Left);
  697. for I := 0 to W - 1 do
  698. begin
  699. A := Abs(I - CX);
  700. B := Abs(J - CY);
  701. D := A;
  702. if B > A then D := B;
  703. D := (A + B + D) * 128 div DMax;
  704. if D < 255 then P^ := (255 - D) shl 24
  705. else P^ := 0;
  706. Inc(P);
  707. end;
  708. end;
  709. end;
  710. procedure TShadow.GradR(const R: TRect);
  711. var
  712. I, J, W: Integer;
  713. P: ^Cardinal;
  714. ScanLine: array of Cardinal;
  715. begin
  716. W := R.Right - R.Left;
  717. SetLength(ScanLine, W);
  718. for I := 0 to W - 1 do
  719. ScanLine[I] :=(255 - I shl 8 div W) shl 24;
  720. for J := R.Top to R.Bottom - 1 do
  721. begin
  722. P := FBuffer.ScanLine[J];
  723. Inc(P, R.Left);
  724. MoveLongword(ScanLine[0], P^, W);
  725. end;
  726. end;
  727. procedure TShadow.GradTR(const R: TRect);
  728. var
  729. I, J, W, H, CX, CY, D, DMax, A, B: Integer;
  730. P: ^Cardinal;
  731. begin
  732. W := R.Right - R.Left;
  733. H := R.Bottom - R.Top;
  734. DMax := W;
  735. if H > W then DMax := H;
  736. CX := W - DMax;
  737. CY := DMax - 1;
  738. for J := 0 to H - 1 do
  739. begin
  740. P := FBuffer.ScanLine[J + R.Top];
  741. Inc(P, R.Left);
  742. for I := 0 to W - 1 do
  743. begin
  744. A := Abs(I - CX);
  745. B := Abs(J - CY);
  746. D := A;
  747. if B > A then D := B;
  748. D := (A + B + D) * 128 div DMax;
  749. if D < 255 then P^ := (255 - D) shl 24
  750. else P^ := 0;
  751. Inc(P);
  752. end;
  753. end;
  754. end;
  755. procedure TShadow.Render;
  756. var
  757. DstDC: HDC;
  758. SrcPos, DstPos: TPoint;
  759. TheSize: TSize;
  760. BlendFunc: TBlendFunction;
  761. begin
  762. if FStyle <> ssLayered then Exit;
  763. SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) or $00080000{WS_EX_LAYERED});
  764. DstDC := GetDC(0);
  765. try
  766. SrcPos := Point(0, 0);
  767. with BoundsRect do
  768. begin
  769. DstPos := Point(Left, Top);
  770. TheSize.cx := Right - Left;
  771. TheSize.cy := Bottom - Top;
  772. end;
  773. BlendFunc.BlendOp := 0;
  774. BlendFunc.BlendFlags := 0;
  775. BlendFunc.SourceConstantAlpha := FOpacity;
  776. BlendFunc.AlphaFormat := 1;
  777. FBuffer := TBitmap.Create;
  778. FBuffer.PixelFormat := pf32bit;
  779. FBuffer.Width := TheSize.cx;
  780. FBuffer.Height := TheSize.cy;
  781. FillBuffer;
  782. UpdateLayeredWindow(
  783. Handle,
  784. DstDC,
  785. @DstPos,
  786. @TheSize,
  787. FBuffer.Canvas.Handle,
  788. @SrcPos,
  789. 0,
  790. @BlendFunc,
  791. $00000002{ULW_ALPHA});
  792. FBuffer.Free;
  793. finally
  794. ReleaseDC(0, DstDC);
  795. end;
  796. end;
  797. procedure TShadow.Show(ParentHandle: HWND);
  798. begin
  799. SetWindowPos(Handle, ParentHandle, 0, 0, 0, 0,
  800. SWP_NOACTIVATE or SWP_NOSENDCHANGING or SWP_NOMOVE or
  801. SWP_NOOWNERZORDER or SWP_NOSIZE or SWP_SHOWWINDOW);
  802. end;
  803. procedure TShadow.WMNCHitTest(var Message: TMessage);
  804. begin
  805. Message.Result := HTTRANSPARENT;
  806. end;
  807. { THorzShadow }
  808. procedure THorzShadow.FillBuffer;
  809. var
  810. R: TRect;
  811. L1, L2, L3: Integer;
  812. begin
  813. if seTopLeft in FEdges then L1 := Height else L1 := 0;
  814. if seBottomRight in FEdges then L3 := Height else L3 := 0;
  815. if L1 + L3 > Width then
  816. begin
  817. if (L1 > 0) and (L3 > 0) then
  818. begin
  819. L1 := Width div 2;
  820. L3 := L1;
  821. end
  822. else if L1 > 0 then L1 := Width
  823. else if L3 > 0 then L3 := Width;
  824. end;
  825. L2 := Width - L1 - L3;
  826. R := Rect(0, 0, Width, Height);
  827. R.Right := R.Left + L1;
  828. if L1 > 0 then GradBL(R);
  829. R.Left := R.Right;
  830. R.Right := R.Left + L2;
  831. if L2 > 0 then GradB(R);
  832. if L3 > 0 then
  833. begin
  834. R.Left := R.Right;
  835. R.Right := R.Left + L3;
  836. GradBR(R);
  837. end;
  838. end;
  839. { TVertShadow }
  840. procedure TVertShadow.FillBuffer;
  841. var
  842. R: TRect;
  843. L1, L2, L3: Integer;
  844. begin
  845. if seTopLeft in FEdges then L1 := Width else L1 := 0;
  846. if seBottomRight in FEdges then L3 := Width else L3 := 0;
  847. if L1 + L3 > Height then
  848. begin
  849. if (L1 > 0) and (L3 > 0) then
  850. begin
  851. L1 := Height div 2;
  852. L3 := L1;
  853. end
  854. else if L1 > 0 then L1 := Height
  855. else if L3 > 0 then L3 := Height;
  856. end;
  857. L2 := Height - L1 - L3;
  858. R := Rect(0, 0, Width, Height);
  859. R.Bottom := R.Top + L1;
  860. if L1 > 0 then GradTR(R);
  861. R.Top := R.Bottom;
  862. R.Bottom := R.Top + L2;
  863. if L2 > 0 then GradR(R);
  864. if L3 > 0 then
  865. begin
  866. R.Top := R.Bottom;
  867. R.Bottom := R.Top + L3;
  868. GradBR(R);
  869. end;
  870. end;
  871. { TShadows }
  872. constructor TShadows.Create(R1, R2: TRect; TheSize: Integer; Opacity: Byte; LoColor: Boolean);
  873. var
  874. R: TRect;
  875. R1Valid, R2Valid: Boolean;
  876. begin
  877. if LoColor then
  878. begin
  879. TheSize := TheSize div 2;
  880. end;
  881. R1Valid := not IsRectEmpty(R1);
  882. R2Valid := not IsRectEmpty(R2);
  883. if not (R1Valid or R2Valid) then Exit;
  884. if R1Valid xor R2Valid then
  885. begin
  886. { A simple square shadow }
  887. if R1Valid then R := R1 else R:= R2;
  888. with R do
  889. begin
  890. V1 := TVertShadow.Create(Rect(Right, Top + TheSize, Right + TheSize, Bottom), Opacity, LoColor, [seTopLeft]);
  891. H1 := THorzShadow.Create(Rect(Left + TheSize, Bottom, Right + TheSize, Bottom + TheSize), Opacity, LoColor, [seTopLeft, seBottomRight])
  892. end;
  893. end
  894. else
  895. begin
  896. if (R1.Bottom <= R2.Top + 2) or (R1.Top >= R2.Bottom - 2) then
  897. begin
  898. if R1.Top > R2.Top then
  899. begin
  900. R := R2;
  901. R2 := R1;
  902. R1 := R;
  903. end;
  904. if R1.Left + TheSize < R2.Left then
  905. H1 := THorzShadow.Create(Rect(R1.Left + TheSize, R1.Bottom, R2.Left, R1.Bottom + TheSize), Opacity, LoColor, [seTopLeft]);
  906. H2 := THorzShadow.Create(Rect(R2.Left + TheSize, R2.Bottom, R2.Right + TheSize, R2.Bottom + TheSize), Opacity, LoColor, [seTopLeft, seBottomRight]);
  907. V1 := TVertShadow.Create(Rect(R1.Right, R1.Top + TheSize, R1.Right + TheSize, R1.Bottom), Opacity, LoColor, [seTopLeft]);
  908. if R1.Right > R2.Right then
  909. H3 := THorzShadow.Create(Rect(R2.Right, R1.Bottom, R1.Right + TheSize, R1.Bottom + TheSize), Opacity, LoColor, [seTopLeft, seBottomRight]);
  910. if R1.Right + TheSize < R2.Right then
  911. V2 := TVertShadow.Create(Rect(R2.Right, R2.Top + TheSize, R2.Right + TheSize, R2.Bottom), Opacity, LoColor, [seTopLeft])
  912. else
  913. V2 := TVertShadow.Create(Rect(R2.Right, R2.Top + 1, R2.Right + TheSize, R2.Bottom), Opacity, LoColor, []);
  914. end
  915. else if (R1.Right <= R2.Left + 2) or (R1.Left >= R2.Right - 2) then
  916. begin
  917. if R1.Left > R2.Left then
  918. begin
  919. R := R2;
  920. R2 := R1;
  921. R1 := R;
  922. end;
  923. if R1.Top + TheSize < R2.Top then
  924. V1 := TVertShadow.Create(Rect(R1.Right, R1.Top + TheSize, R1.Right + TheSize, R2.Top), Opacity, LoColor, [seTopLeft]);
  925. V2 := TVertShadow.Create(Rect(R2.Right, R2.Top + TheSize, R2.Right + TheSize, R2.Bottom + TheSize), Opacity, LoColor, [seTopLeft, seBottomRight]);
  926. H1 := THorzShadow.Create(Rect(R1.Left + TheSize, R1.Bottom, R1.Right, R1.Bottom + TheSize), Opacity, LoColor, [seTopLeft]);
  927. if R1.Bottom > R2.Bottom then
  928. V3 := TVertShadow.Create(Rect(R1.Right, R2.Bottom, R1.Right + TheSize, R1.Bottom + TheSize), Opacity, LoColor, [seTopLeft, seBottomRight]);
  929. if R1.Bottom + TheSize < R2.Bottom then
  930. H2 := THorzShadow.Create(Rect(R2.Left + TheSize, R2.Bottom, R2.Right, R2.Bottom + TheSize), Opacity, LoColor, [seTopLeft])
  931. else
  932. H2 := THorzShadow.Create(Rect(R2.Left, R2.Bottom, R2.Right, R2.Bottom + TheSize), Opacity, LoColor, []);
  933. end;
  934. end;
  935. if V1 <> nil then V1.Render;
  936. if H1 <> nil then H1.Render;
  937. if V2 <> nil then V2.Render;
  938. if H2 <> nil then H2.Render;
  939. if V3 <> nil then V3.Render;
  940. if H3 <> nil then H3.Render;
  941. SetSaveBits(True);
  942. end;
  943. destructor TShadows.Destroy;
  944. begin
  945. H3.Free;
  946. V3.Free;
  947. H2.Free;
  948. V2.Free;
  949. H1.Free;
  950. V1.Free;
  951. inherited;
  952. end;
  953. procedure TShadows.SetSaveBits(Value: Boolean);
  954. begin
  955. FSaveBits := Value;
  956. if V1 <> nil then V1.FSaveBits := Value;
  957. if H1 <> nil then H1.FSaveBits := Value;
  958. if V2 <> nil then V2.FSaveBits := Value;
  959. if H2 <> nil then H2.FSaveBits := Value;
  960. if V3 <> nil then V3.FSaveBits := Value;
  961. if H3 <> nil then H3.FSaveBits := Value;
  962. end;
  963. procedure TShadows.Show(ParentHandle: HWND);
  964. begin
  965. if V1 <> nil then V1.Show(ParentHandle);
  966. if H1 <> nil then H1.Show(ParentHandle);
  967. if V2 <> nil then V2.Show(ParentHandle);
  968. if H2 <> nil then H2.Show(ParentHandle);
  969. if V3 <> nil then V3.Show(ParentHandle);
  970. if H3 <> nil then H3.Show(ParentHandle);
  971. end;
  972. initialization
  973. InitializeStock;
  974. finalization
  975. FinalizeStock;
  976. end.