ugraph.pas 40 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306
  1. // SPDX-License-Identifier: GPL-3.0-only
  2. unit UGraph;
  3. {$mode objfpc}{$H+}
  4. interface
  5. uses
  6. Classes, SysUtils, bgrabitmap, bgrabitmaptypes, LazPaintType, Graphics, BGRALayers, LCLType,
  7. BCComboBox;
  8. var
  9. NicePointMaxRadius: integer = 4;
  10. FrameDashLength: integer = 4;
  11. function ComputeRatio(ARatio: string): single;
  12. function RatioToStr(ARatio: single): string;
  13. function RectUnion(const rect1,Rect2: TRect): TRect;
  14. function RectInter(const rect1,Rect2: TRect): TRect;
  15. function RectOfs(const ARect: TRect; ofsX,ofsY: integer): TRect;
  16. function GetShapeBounds(const pts: array of TPointF; width: single): TRect;
  17. function DoPixelate(source: TBGRABitmap; pixelSize: integer; quality: string): TBGRABitmap;
  18. procedure DrawCheckers(bmp : TBGRABitmap; ARect: TRect);
  19. procedure DrawGrid(bmp: TBGRABitmap; sizex,sizey: single; ofsx,ofsy: single);
  20. function ComputeAngle(dx,dy: single): single;
  21. function GetSelectionCenter(bmp: TBGRABitmap): TPointF;
  22. procedure ComputeSelectionMask(image: TBGRABitmap; destMask: TBGRABitmap; ARect: TRect);
  23. procedure SubstractMask(image: TBGRABitmap; DestX,DestY: Integer; mask: TBGRABitmap; SourceMaskRect: TRect);
  24. function NicePointBounds(x,y: single): TRect;
  25. function NicePoint(bmp: TBGRABitmap; x,y: single; alpha: byte = 192):TRect; overload;
  26. function NicePoint(bmp: TBGRABitmap; ptF: TPointF; alpha: byte = 192):TRect; overload;
  27. procedure NiceLine(bmp: TBGRABitmap; x1,y1,x2,y2: single; alpha: byte = 192);
  28. function NiceText(bmp: TBGRABitmap; x,y,bmpWidth,bmpHeight: integer; s: string; align: TAlignment = taLeftJustify; valign: TTextLayout = tlTop): TRect;
  29. function ComputeColorCircle(tx,ty: integer; light: word; hueCorrection: boolean = true): TBGRABitmap;
  30. procedure RenderCloudsOn(bmp: TBGRABitmap; color: TBGRAPixel);
  31. procedure RenderWaterOn(bmp: TBGRABitmap; waterColor, skyColor: TBGRAPixel);
  32. function CreateMetalFloorTexture(tx: integer): TBGRABitmap;
  33. function CreatePlastikTexture(tx,ty: integer): TBGRABitmap;
  34. function CreateCamouflageTexture(tx,ty: integer): TBGRABitmap;
  35. function CreateSnowPrintTexture(tx,ty: integer): TBGRABitmap;
  36. function CreateRoundStoneTexture(tx,ty: integer): TBGRABitmap;
  37. function CreateStoneTexture(tx,ty: integer): TBGRABitmap;
  38. function CreateWaterTexture(tx,ty: integer): TBGRABitmap;
  39. function CreateMarbleTexture(tx,ty: integer): TBGRABitmap;
  40. function CreateWoodTexture(tx,ty: integer): TBGRABitmap;
  41. function CreateVerticalWoodTexture(tx,ty: integer): TBGRABitmap;
  42. function ClearTypeFilter(source: TBGRACustomBitmap): TBGRACustomBitmap;
  43. function ClearTypeInverseFilter(source: TBGRACustomBitmap): TBGRACustomBitmap;
  44. function WaveDisplacementFilter(source: TBGRACustomBitmap;
  45. ARect: TRect; ACenter: TPointF;
  46. AWaveLength, ADisplacement, APhase: single): TBGRACustomBitmap;
  47. function DoResample(source :TBGRABitmap; newWidth, newHeight: integer; StretchMode: TResampleMode): TBGRABitmap;
  48. procedure DrawPenStyle(AComboBox: TBCComboBox; ARect: TRect; APenStyle: TPenStyle; State: TOwnerDrawState); overload;
  49. procedure DrawPenStyle(ABitmap: TBGRABitmap; ARect: TRect; APenStyle: TPenStyle; c: TBGRAPixel); overload;
  50. procedure DrawArrow(AComboBox: TBCComboBox; ARect: TRect; AStart: boolean; AKindStr: string; ALineCap: TPenEndCap; State: TOwnerDrawState); overload;
  51. procedure DrawArrow(ABitmap: TBGRABitmap; ARect: TRect; AStart: boolean; AKindStr: string; ALineCap: TPenEndCap; AColor: TBGRAPixel); overload;
  52. implementation
  53. uses GraphType, math, Types, FileUtil, dialogs, BGRAAnimatedGif,
  54. BGRAGradients, BGRATextFX, uresourcestrings, LCScaleDPI,
  55. BGRAThumbnail, LCVectorPolyShapes;
  56. function ComputeRatio(ARatio: string): single;
  57. var
  58. idxCol,errPos: Integer;
  59. num,denom: double;
  60. begin
  61. result := 0;
  62. ARatio := stringreplace(ARatio,FormatSettings.DecimalSeparator,'.',[rfReplaceAll]);
  63. if ARatio = '' then exit;
  64. idxCol := pos(':',ARatio);
  65. if idxCol = 0 then exit;
  66. val(copy(ARatio,1,idxCol-1),num,errPos);
  67. if errPos <> 0 then exit;
  68. if num < 0 then exit;
  69. val(copy(ARatio,idxCol+1,length(ARatio)-idxCol),denom,errPos);
  70. if errPos <> 0 then exit;
  71. if denom <= 0 then exit;
  72. result := num/denom;
  73. end;
  74. function RatioToStr(ARatio: single): string;
  75. var
  76. num,denom: integer;
  77. procedure InvFrac;
  78. var temp: integer;
  79. begin
  80. temp := num;
  81. num := denom;
  82. denom := temp;
  83. end;
  84. procedure AddFrac(AValue: integer);
  85. begin
  86. inc(num, AValue*denom);
  87. end;
  88. const MaxDev = 3;
  89. var
  90. dev: array[1..MaxDev] of integer;
  91. devCount, i: integer;
  92. curVal, remain: Single;
  93. begin
  94. if ARatio < 0 then ARatio := -ARatio;
  95. curVal := ARatio;
  96. devCount := 0;
  97. repeat
  98. inc(devCount);
  99. dev[devCount] := trunc(ARatio);
  100. remain := frac(curVal);
  101. if abs(remain) < 1e-3 then break;
  102. curVal := 1/remain;
  103. until devCount = MaxDev;
  104. num := dev[devCount];
  105. denom := 1;
  106. for i := devCount-1 downto 1 do
  107. begin
  108. InvFrac;
  109. AddFrac(dev[i]);
  110. end;
  111. result := IntToStr(num)+':'+IntToStr(denom);
  112. end;
  113. function RectUnion(const rect1, Rect2: TRect): TRect;
  114. begin
  115. if IsRectEmpty(rect1) then
  116. begin
  117. if IsRectEmpty(rect2) then
  118. result := EmptyRect
  119. else
  120. result:= rect2;
  121. end else
  122. begin
  123. result := rect1;
  124. if not IsRectEmpty(rect2) then
  125. UnionRect(result,result,rect2);
  126. end;
  127. end;
  128. function RectInter(const rect1, Rect2: TRect): TRect;
  129. begin
  130. result := EmptyRect;
  131. IntersectRect(result,rect1,rect2);
  132. end;
  133. function RectOfs(const ARect: TRect; ofsX, ofsY: integer): TRect;
  134. begin
  135. result := ARect;
  136. OffsetRect(result,ofsX,ofsY);
  137. end;
  138. function GetShapeBounds(const pts: array of TPointF; width: single): TRect;
  139. var ix,iy,i: integer;
  140. begin
  141. width /= 2;
  142. result.Left := high(Integer);
  143. result.Top := high(Integer);
  144. result.Right := low(Integer);
  145. result.Bottom := low(Integer);
  146. for i := 0 to high(pts) do
  147. if not isEmptyPointF(pts[i]) then
  148. begin
  149. ix := floor(pts[i].x - width);
  150. iy := floor(pts[i].y - width);
  151. if ix < result.left then result.left := ix;
  152. if iy < result.Top then result.top := iy;
  153. ix := ceil(pts[i].x + width)+1;
  154. iy := ceil(pts[i].y + width)+1;
  155. if ix > result.right then result.right := ix;
  156. if iy > result.bottom then result.bottom := iy;
  157. end;
  158. if (result.right <= result.left) or (result.bottom <= result.top) then
  159. result := EmptyRect;
  160. end;
  161. function DoPixelate(source: TBGRABitmap; pixelSize: integer; quality: string): TBGRABitmap;
  162. var
  163. filter: TResampleFilter;
  164. useFilter: boolean;
  165. begin
  166. if quality = rsMitchell then
  167. filter := rfMitchell else
  168. if quality = rsSpline then
  169. filter := rfSpline
  170. else
  171. filter := rfLinear;
  172. useFilter := quality <> rsFast;
  173. result := source.FilterPixelate(pixelSize,useFilter,filter) as TBGRABitmap;
  174. end;
  175. procedure DrawCheckers(bmp: TBGRABitmap; ARect: TRect);
  176. begin
  177. DrawThumbnailCheckers(bmp, ARect, False);
  178. end;
  179. procedure DrawGrid(bmp: TBGRABitmap; sizex, sizey: single; ofsx,ofsy: single);
  180. var xb,yb: integer;
  181. imgGrid: TBGRABitmap;
  182. alpha: byte;
  183. begin
  184. ofsx := ofsx - floor(ofsx/sizex)*sizex;
  185. ofsy := ofsy - floor(ofsy/sizey)*sizey;
  186. imgGrid := TBGRABitmap.Create(bmp.Width,1);
  187. alpha := min(96,round((abs(sizex)+abs(sizey))*(96/16/2)));
  188. imgGrid.DrawLineAntialias(0,0,imgGrid.width-1,0,BGRA(255,255,255,alpha),BGRA(0,0,0,alpha),
  189. min(3,max(1,round(sizex/8))),true);
  190. for yb := 1 to ceil(bmp.Height/sizey) do
  191. begin
  192. bmp.PutImage(0,round(ofsy),imgGrid,dmFastBlend);
  193. ofsy += sizey;
  194. end;
  195. imgGrid.Free;
  196. imgGrid := TBGRABitmap.Create(1,bmp.Height);
  197. imgGrid.DrawLineAntialias(0,0,0,imgGrid.height-1,BGRA(0,0,0,alpha),BGRA(255,255,255,alpha),
  198. min(3,max(1,round(sizey/8))),true);
  199. for xb := 1 to ceil(bmp.Width/sizex) do
  200. begin
  201. bmp.PutImage(round(ofsx),0,imgGrid,dmFastBlend);
  202. ofsx += sizex;
  203. end;
  204. imgGrid.Free;
  205. end;
  206. procedure RenderCloudsOn(bmp: TBGRABitmap; color: TBGRAPixel);
  207. const minDensity=180; maxDensity=240;
  208. var i,k,x,y: integer;
  209. fact,radius: single;
  210. tempBmp: TBGRABitmap;
  211. ptemp: PBGRAPixel;
  212. begin
  213. if color.alpha = 0 then exit;
  214. tempBmp := TBGRABitmap.Create(bmp.width,bmp.Height,BGRABlack);
  215. fact := (bmp.width+bmp.Height)/15;
  216. for i := 120 downto 20 do
  217. begin
  218. for k := 1 to 2 do
  219. begin
  220. radius := ((i+random(50))/100)*fact;
  221. x := random(bmp.Width);
  222. y := random(bmp.Height);
  223. tempBmp.GradientFill(floor(x-radius),floor(y-radius),ceil(x+radius),ceil(y+radius),BGRA(255,255,255,128),BGRAPixelTransparent,gtRadial,pointf(x,y),pointf(x+radius+0.5,y),dmFastBlend,false);
  224. end;
  225. end;
  226. ptemp := tempBmp.Data;
  227. for i := tempBmp.nbPixels-1 downto 0 do
  228. begin
  229. if ptemp^.red < minDensity then ptemp^:= BGRAPixelTransparent else
  230. if ptemp^.red > maxDensity then ptemp^:= color else
  231. ptemp^ := BGRA(color.red,color.green,color.blue,color.alpha*(ptemp^.red-minDensity) div (maxDensity-minDensity));
  232. inc(ptemp);
  233. end;
  234. bmp.PutImage(0,0,tempBmp,dmDrawWithTransparency);
  235. tempBmp.free;
  236. end;
  237. procedure RenderWaterOn(bmp: TBGRABitmap; waterColor, skyColor: TBGRAPixel);
  238. var Noise,Temp: TBGRABitmap;
  239. Phong: TPhongShading;
  240. begin
  241. Noise := CreateCyclicPerlinNoiseMap(bmp.Width,bmp.Height,1,1,1.2);
  242. Temp := Noise.FilterBlurRadial(1,rbFast) as TBGRABitmap;
  243. Noise.Free;
  244. Noise := Temp;
  245. Noise.ApplyGlobalOpacity(waterColor.alpha);
  246. waterColor.alpha := 255;
  247. Phong := TPhongShading.Create;
  248. Phong.NegativeDiffusionFactor := 0.1;
  249. Phong.AmbientFactor := 0.7;
  250. Phong.LightSourceDistanceFactor := 0;
  251. Phong.LightDestFactor := 0;
  252. Phong.LightSourceIntensity := 300;
  253. Phong.LightPosition := Point(-500,-500);
  254. Phong.LightColor := skyColor;
  255. Phong.Draw(bmp,Noise,30,0,0,waterColor);
  256. Noise.Free;
  257. Phong.Free;
  258. end;
  259. function Interp256(value1,value2,position: integer): integer; inline;
  260. begin
  261. result := (value1*(256-position)+value2*position) shr 8;
  262. end;
  263. function Interp256(color1,color2: TBGRAPixel; position: integer): TBGRAPixel; inline;
  264. begin
  265. result.red := Interp256(color1.red,color2.red,position);
  266. result.green := Interp256(color1.green,color2.green,position);
  267. result.blue := Interp256(color1.blue,color2.blue,position);
  268. result.alpha := Interp256(color1.alpha,color2.alpha,position);
  269. end;
  270. function CreateWoodTexture(tx,ty: integer): TBGRABitmap;
  271. var
  272. colorOscillation, globalColorVariation: integer;
  273. p: PBGRAPixel;
  274. i: Integer;
  275. begin
  276. result := CreateCyclicPerlinNoiseMap(tx,ty,1.5,1.5,1,rfBestQuality);
  277. p := result.Data;
  278. for i := 0 to result.NbPixels-1 do
  279. begin
  280. colorOscillation := round(sqrt((sin(p^.red*Pi/16)+1)/2)*256);
  281. globalColorVariation := p^.red;
  282. p^:= Interp256( Interp256(BGRA(247,188,120),BGRA(255,218,170),colorOscillation),
  283. Interp256(BGRA(157,97,60),BGRA(202,145,112),colorOscillation), globalColorVariation);
  284. inc(p);
  285. end;
  286. end;
  287. function CreateVerticalWoodTexture(tx, ty: integer): TBGRABitmap;
  288. var
  289. globalPos: single;
  290. colorOscillation, globalColorVariation: integer;
  291. p: PBGRAPixel;
  292. i: Integer;
  293. x,nbVertical: integer;
  294. begin
  295. result := CreateCyclicPerlinNoiseMap(tx,ty,1,1,1,rfBestQuality);
  296. p := result.Data;
  297. x := 0;
  298. nbVertical := tx div 128;
  299. if nbVertical = 0 then nbVertical := 1;
  300. for i := 0 to result.NbPixels-1 do
  301. begin
  302. globalPos := p^.red*Pi/32 + nbVertical*x*2*Pi/tx*8;
  303. colorOscillation := round(sqrt((sin(globalPos)+1)/2)*256);
  304. globalColorVariation := p^.red; //round(sin(globalPos/8)*128+128);
  305. p^:= Interp256( Interp256(BGRA(247,188,120),BGRA(255,218,170),colorOscillation),
  306. Interp256(BGRA(157,97,60),BGRA(202,145,112),colorOscillation), globalColorVariation);
  307. inc(p);
  308. inc(x);
  309. if x = tx then x := 0;
  310. end;
  311. end;
  312. function ClearTypeFilter(source: TBGRACustomBitmap): TBGRACustomBitmap;
  313. var
  314. mul3,temp: TBGRACustomBitmap;
  315. xb,yb: integer;
  316. pmul3,pdest: PBGRAPixel;
  317. a: byte;
  318. begin
  319. source.ResampleFilter := rfSpline;
  320. mul3 := source.Resample(source.Width*3-2,source.Height);
  321. temp := source.NewBitmap(source.Width*3,source.Height);
  322. temp.PutImage(1,0,mul3,dmSet);
  323. for yb := 0 to temp.Height-1 do
  324. begin
  325. temp.SetPixel(0,yb,temp.GetPixel(1,yb));
  326. temp.SetPixel(temp.Width-1,yb,temp.GetPixel(temp.Width-2,yb));
  327. end;
  328. mul3.free;
  329. mul3 := temp;
  330. result := source.NewBitmap(source.Width,source.Height);
  331. for yb := 0 to result.Height-1 do
  332. begin
  333. pmul3 := mul3.ScanLine[yb];
  334. pdest := result.ScanLine[yb];
  335. for xb := result.width-1 downto 0 do
  336. begin
  337. a := (pmul3+1)^.alpha;
  338. if a = 0 then pdest^:= BGRAPixelTransparent
  339. else
  340. begin
  341. pdest^.alpha := a;
  342. if pmul3^.alpha = 0 then pdest^.red := 128 else
  343. pdest^.red := pmul3^.red;
  344. pdest^.green := (pmul3+1)^.green;
  345. if (pmul3+2)^.alpha = 0 then pdest^.blue := 128 else
  346. pdest^.blue := (pmul3+2)^.blue;
  347. end;
  348. inc(pdest);
  349. inc(pmul3,3);
  350. end;
  351. end;
  352. mul3.free;
  353. end;
  354. function ClearTypeInverseSubFilter(source: TBGRACustomBitmap): TBGRACustomBitmap;
  355. const
  356. blueA = 20;
  357. blueB = 0;
  358. blueC = 2;
  359. redA = 20;
  360. redB = 0;
  361. redC = 2;
  362. maxV = 255;
  363. var yb,xb: integer;
  364. psrc,pdest,pgray: PBGRAPixel;
  365. a,v: integer;
  366. grayscale,temp: TBGRACustomBitmap;
  367. function Merge3(c1, c2, c3: TBGRAPixel): TBGRAPixel;
  368. var c123: cardinal;
  369. begin
  370. if (c1.alpha = 0) then
  371. Result := MergeBGRA(c2,c3)
  372. else
  373. if (c2.alpha = 0) then
  374. Result := MergeBGRA(c1,c3)
  375. else
  376. if (c3.alpha = 0) then
  377. Result := MergeBGRA(c1,c2)
  378. else
  379. begin
  380. c123 := c1.alpha + c2.alpha + c3.alpha;
  381. Result.red := (c1.red * c1.alpha + c2.red * c2.alpha + c3.red * c3.alpha + c123 shr 1) div c123;
  382. Result.green := (c1.green * c1.alpha + c2.green * c2.alpha + c3.green * c3.alpha + c123 shr 1) div c123;
  383. Result.blue := (c1.blue * c1.alpha + c2.blue * c2.alpha + c3.blue * c3.alpha + c123 shr 1) div c123;
  384. Result.alpha := (c123 + 1) div 3;
  385. end;
  386. end;
  387. begin
  388. if source.width <= 1 then
  389. begin
  390. result := source.duplicate;
  391. exit;
  392. end;
  393. grayscale := source;
  394. temp := source.NewBitmap(source.Width,source.Height);
  395. for yb := 0 to source.Height-1 do
  396. begin
  397. psrc := source.Scanline[yb];
  398. pgray := grayscale.ScanLine[yb];
  399. pdest := temp.Scanline[yb];
  400. pdest^.red := psrc^.red;
  401. pdest^.green := psrc^.green;
  402. pdest^.alpha := psrc^.alpha;
  403. a := (psrc^.alpha*blueA) - ((psrc+1)^.alpha*(blueB));
  404. if a > 0 then
  405. begin
  406. v := ((integer(psrc^.blue)*blueA)*psrc^.alpha - integer((psrc+1)^.blue*blueB)*(psrc+1)^.alpha) div a;
  407. if v >= maxV then
  408. pdest^.blue := 255 else
  409. if v > 0 then
  410. pdest^.blue := v
  411. else
  412. pdest^.blue := 0;
  413. end
  414. else
  415. pdest^.blue := psrc^.blue;
  416. inc(pdest);
  417. inc(psrc);
  418. inc(pgray);
  419. for xb := source.width-3 downto 0 do
  420. begin
  421. pdest^.green := psrc^.green;
  422. pdest^.alpha := psrc^.alpha;
  423. a := (psrc^.alpha*redA) - ((psrc-1)^.alpha*(redB));
  424. if a > 0 then
  425. begin
  426. v := ((integer(psrc^.red)*redA)*psrc^.alpha - integer((psrc-1)^.red*redB+((pgray-1)^.green-pgray^.green)*redC)*(psrc-1)^.alpha) div a;
  427. if v >= maxV then
  428. pdest^.red := 255 else
  429. if v > 0 then
  430. pdest^.red := v
  431. else
  432. pdest^.red := 0;
  433. end
  434. else
  435. pdest^.red := psrc^.red;
  436. a := (psrc^.alpha*blueA) - ((psrc+1)^.alpha*(blueB));
  437. if a > 0 then
  438. begin
  439. v := ((integer(psrc^.blue)*blueA)*psrc^.alpha - integer((psrc+1)^.blue*blueB+((pgray+1)^.green-pgray^.green)*blueC)*(psrc+1)^.alpha) div a;
  440. if v >= maxV then
  441. pdest^.blue := 255 else
  442. if v > 0 then
  443. pdest^.blue := v
  444. else
  445. pdest^.blue := 0;
  446. end
  447. else
  448. pdest^.blue := psrc^.blue;
  449. inc(pdest);
  450. inc(psrc);
  451. inc(pgray);
  452. end;
  453. pdest^.green := psrc^.green;
  454. pdest^.blue := psrc^.blue;
  455. pdest^.alpha := psrc^.alpha;
  456. a := (psrc^.alpha*redA) - ((psrc-1)^.alpha*(redB));
  457. if a > 0 then
  458. begin
  459. v := ((integer(psrc^.red)*redA)*psrc^.alpha - integer((psrc-1)^.red*redB)*(psrc-1)^.alpha) div a;
  460. if v >= maxV then
  461. pdest^.red := 255 else
  462. if v > 0 then
  463. pdest^.red := v
  464. else
  465. pdest^.red := 0;
  466. end
  467. else
  468. pdest^.red := psrc^.red;
  469. end;
  470. result := temp;
  471. end;
  472. function ClearTypeSharpenFilter(source, diffbmp: TBGRACustomBitmap): TBGRACustomBitmap;
  473. const
  474. factnum = 3;
  475. factdenom = 5;
  476. var
  477. xb,yb,maxx: integer;
  478. psrc,pdest,pdiff: PBGRAPixel;
  479. d1,d2 : integer;
  480. function clamp(value: integer) : byte;
  481. begin
  482. if value <= 0 then result := 0
  483. else if value >= 255 then result := 255
  484. else result := value;
  485. end;
  486. function adjustDiff(ref,v1,v2: integer): integer;
  487. begin
  488. v1 -= ref;
  489. v2 -= ref;
  490. result := v1+v2;
  491. end;
  492. begin
  493. if diffbmp = nil then diffbmp := source;
  494. if (source.width <= 1) or (diffbmp.width <> source.width) or (diffbmp.height <> source.height) then
  495. begin
  496. result := source.Duplicate();
  497. exit;
  498. end;
  499. result := source.NewBitmap(source.Width,source.Height);
  500. for yb := 0 to result.Height-1 do
  501. begin
  502. psrc := source.ScanLine[yb];
  503. pdest := result.ScanLine[yb];
  504. pdiff := diffbmp.ScanLine[yb];
  505. maxx := result.width-1;
  506. for xb := 0 to maxx do
  507. begin
  508. if psrc^.alpha <> 0 then
  509. begin
  510. if (xb > 0) and ((psrc-1)^.alpha <>0) and (xb < maxx) and ((psrc+1)^.alpha <>0) then
  511. begin
  512. d1 := BGRADiff((pdiff-1)^,pdiff^);
  513. d2 := BGRADiff((pdiff+1)^,pdiff^);
  514. if (d1 > 20) and (d2 > 20) and (d1+d2 > 100) then
  515. begin
  516. pdest^.red := clamp(psrc^.red - (adjustDiff(psrc^.red,(psrc+1)^.red,(psrc-1)^.red))*factnum div (2*factdenom));
  517. pdest^.green := psrc^.green;
  518. pdest^.blue := clamp(psrc^.blue - (adjustDiff(psrc^.blue,(psrc+1)^.blue,(psrc-1)^.blue))*factnum div (2*factdenom));
  519. pdest^.alpha := psrc^.alpha;
  520. end
  521. else
  522. pdest^ := psrc^;
  523. end else
  524. if (xb < maxx) and ((psrc+1)^.alpha <>0) then
  525. begin
  526. pdest^.red := clamp(psrc^.red - ((psrc+1)^.red-psrc^.red) *factnum div factdenom);
  527. pdest^.green := psrc^.green;
  528. pdest^.blue := clamp(psrc^.blue - ((psrc+1)^.blue-psrc^.blue) *factnum div factdenom);
  529. pdest^.alpha := psrc^.alpha;
  530. end else
  531. if (xb > 0) and ((psrc-1)^.alpha <>0) then
  532. begin
  533. pdest^.red := clamp(psrc^.red - ((psrc-1)^.red-psrc^.red)*factnum div factdenom);
  534. pdest^.green := psrc^.green;
  535. pdest^.blue := clamp(psrc^.blue - ((psrc-1)^.blue-psrc^.blue)*factnum div factdenom);
  536. pdest^.alpha := psrc^.alpha;
  537. end else
  538. pdest^ := psrc^;
  539. end else
  540. pdest^ := BGRAPixelTransparent;
  541. inc(pdest);
  542. inc(psrc);
  543. inc(pdiff);
  544. end;
  545. end;
  546. end;
  547. function ClearTypeRemoveContradiction(source: TBGRACustomBitmap): TBGRACustomBitmap;
  548. var
  549. xb,yb: integer;
  550. dr,db: integer;
  551. ratio: single;
  552. psrc,pdest: PBGRAPixel;
  553. begin
  554. if source.width <= 1 then
  555. begin
  556. result := source.Duplicate();
  557. exit;
  558. end;
  559. result := source.NewBitmap(source.Width,source.Height);
  560. for yb := 0 to result.Height-1 do
  561. begin
  562. psrc := source.ScanLine[yb];
  563. pdest := result.ScanLine[yb];
  564. pdest^ := psrc^;
  565. for xb := result.width-2 downto 0 do
  566. begin
  567. (pdest+1)^ := (psrc+1)^;
  568. if (psrc^.alpha > 10) and ((psrc+1)^.alpha > 10) then
  569. begin
  570. dr := psrc^.red-(psrc+1)^.red;
  571. db := psrc^.blue-(psrc+1)^.blue;
  572. if ((db < 0) and (dr > 0)) or
  573. ((db > 0) and (dr < 0)) then
  574. begin
  575. ratio := abs(dr/db);
  576. if (ratio > 0.2) and (ratio < 5) then
  577. begin
  578. dr := (psrc^.red*psrc^.alpha+(psrc+1)^.red*(psrc+1)^.alpha) div (psrc^.alpha+(psrc+1)^.alpha);
  579. db := (psrc^.blue*psrc^.alpha+(psrc+1)^.blue*(psrc+1)^.alpha) div (psrc^.alpha+(psrc+1)^.alpha);
  580. pdest^.red := dr;
  581. pdest^.blue := db;
  582. (pdest+1)^.red := dr;
  583. (pdest+1)^.blue := db;
  584. end;
  585. end;
  586. end;
  587. inc(pdest);
  588. inc(psrc);
  589. end;
  590. end;
  591. end;
  592. function ClearTypeInverseFilter(source: TBGRACustomBitmap): TBGRACustomBitmap;
  593. var
  594. mul3,temp: TBGRACustomBitmap;
  595. xb,yb: integer;
  596. pmul3,pdest: PBGRAPixel;
  597. a: byte;
  598. begin
  599. source.ResampleFilter := rfSpline;
  600. mul3 := source.Resample(source.Width*3-2,source.Height);
  601. temp := source.NewBitmap(source.Width*3,source.Height);
  602. temp.PutImage(1,0,mul3,dmSet);
  603. for yb := 0 to temp.Height-1 do
  604. begin
  605. temp.SetPixel(0,yb,temp.GetPixel(1,yb));
  606. temp.SetPixel(temp.Width-1,yb,temp.GetPixel(temp.Width-2,yb));
  607. end;
  608. mul3.free;
  609. mul3 := temp;
  610. result := source.NewBitmap(source.Width,source.Height);
  611. for yb := 0 to result.Height-1 do
  612. begin
  613. pmul3 := mul3.ScanLine[yb];
  614. pdest := result.ScanLine[yb];
  615. for xb := result.width-1 downto 0 do
  616. begin
  617. a := (pmul3+1)^.alpha;
  618. if a = 0 then pdest^:= BGRAPixelTransparent
  619. else
  620. begin
  621. pdest^.alpha := a;
  622. if (pmul3+2)^.alpha = 0 then pdest^.red := 128 else
  623. pdest^.red := (pmul3+2)^.red;
  624. pdest^.green := (pmul3+1)^.green;
  625. if pmul3^.alpha = 0 then pdest^.blue := 128 else
  626. pdest^.blue := pmul3^.blue;
  627. end;
  628. inc(pdest);
  629. inc(pmul3,3);
  630. end;
  631. end;
  632. mul3.free;
  633. temp := ClearTypeRemoveContradiction(result);
  634. result.free;
  635. result := temp;
  636. temp := result;
  637. result := ClearTypeSharpenFilter(temp,source);
  638. temp.Free;
  639. temp := ClearTypeRemoveContradiction(result);
  640. result.free;
  641. result := temp;
  642. end;
  643. type
  644. { TWaveDisplacementScanner }
  645. TWaveDisplacementScanner = class(TBGRACustomScanner)
  646. Source: TBGRACustomBitmap;
  647. Center: TPointF;
  648. Wavelength, Displacement, PhaseRad: single;
  649. function ScanAt(X,Y: Single): TBGRAPixel; override;
  650. end;
  651. { TWaveDisplacementScanner }
  652. function TWaveDisplacementScanner.ScanAt(X, Y: Single): TBGRAPixel;
  653. var
  654. u, disp: TPointF;
  655. dist: Single;
  656. alpha: ValReal;
  657. begin
  658. u := PointF(X,Y)-Center;
  659. dist := VectLen(u);
  660. if dist = 0 then disp := PointF(0,0) else
  661. begin
  662. u := u*(1/dist);
  663. alpha := PhaseRad+dist*2*Pi/Wavelength;
  664. disp := u*sin(alpha)*Displacement;
  665. end;
  666. result := Source.GetPixel(x+disp.x,y+disp.y);
  667. end;
  668. function WaveDisplacementFilter(source: TBGRACustomBitmap; ARect: TRect;
  669. ACenter: TPointF; AWaveLength, ADisplacement, APhase: single): TBGRACustomBitmap;
  670. var scan: TWaveDisplacementScanner;
  671. begin
  672. scan := TWaveDisplacementScanner.Create;
  673. scan.Center := ACenter;
  674. scan.Source := source;
  675. scan.Wavelength := AWaveLength;
  676. scan.Displacement := ADisplacement;
  677. scan.PhaseRad := APhase*Pi/180;
  678. result := TBGRABitmap.Create(source.Width,source.Height);
  679. result.FillRect(ARect, scan, dmSet);
  680. scan.Free;
  681. end;
  682. function DoResample(source: TBGRABitmap; newWidth, newHeight: integer;
  683. StretchMode: TResampleMode): TBGRABitmap;
  684. begin
  685. result := source.Resample(newWidth,newHeight,StretchMode) as TBGRABitmap;
  686. end;
  687. procedure DrawArrowMask(AMask: TBGRABitmap; AStart: boolean; AKindStr: string; ALineCap: TPenEndCap);
  688. var
  689. kind: TArrowKind;
  690. x1,x2,xm1,xm2,y,w,temp: single;
  691. begin
  692. AMask.Fill(BGRABlack);
  693. kind := StrToArrowKind(AKindStr);
  694. ApplyArrowStyle(AMask.Arrow,AStart,kind,PointF(1.5,1.5));
  695. AMask.LineCap := ALineCap;
  696. w := AMask.Height/5;
  697. if w > 0 then
  698. begin
  699. x1 := w*2.5;
  700. x2 := 0;
  701. xm1 := 0;
  702. xm2 := w*2.5;
  703. if kind in[akNone,akCut] then x1 -= w*0.7 else
  704. if kind in[akFlipped,akFlippedCut] then x1 += w*0.7;
  705. if not AStart then
  706. begin
  707. temp := x1;
  708. x1 := -x2;
  709. x2 := -temp;
  710. end else
  711. begin
  712. xm1 := (AMask.Width-0.5)-xm1;
  713. xm2 := (AMask.Width-0.5)-xm2;
  714. end;
  715. x1 -= 0.5;
  716. x2 += AMask.Width-0.5;
  717. y := (AMask.Height-1)/2;
  718. if kind in[akTail,akNone,akTip] then w *= 2;
  719. AMask.DrawLineAntialias(x1,y,x2,y,BGRAWhite,w);
  720. if AMask.Width > AMask.Height*2 then
  721. AMask.GradientFill(0,0,AMask.width,AMask.height,BGRABlack,BGRAPixelTransparent,gtLinear,PointF(xm1,0),PointF(xm2,0),dmDrawWithTransparency);
  722. end;
  723. end;
  724. procedure DrawPenStyle(AComboBox: TBCComboBox; ARect: TRect;
  725. APenStyle: TPenStyle; State: TOwnerDrawState);
  726. var bmp : TBGRABitmap;
  727. c,c2: TBGRAPixel;
  728. begin
  729. if odSelected in State then
  730. begin
  731. c := ColorToBGRA(AComboBox.DropDownFontHighlight);
  732. c2 := ColorToBGRA(AComboBox.DropDownHighlight);
  733. end
  734. else
  735. begin
  736. c := ColorToBGRA(AComboBox.DropDownFontColor);
  737. c2 := ColorToBGRA(AComboBox.DropDownColor);
  738. end;
  739. with Size(ARect) do bmp := TBGRABitmap.Create(cx,cy,c2);
  740. DrawPenStyle(bmp, rect(0,0,ARect.Width,ARect.Height),APenStyle, c);
  741. bmp.Draw(ACombobox.Canvas,ARect.Left,ARect.Top,true);
  742. bmp.Free;
  743. end;
  744. procedure DrawPenStyle(ABitmap: TBGRABitmap; ARect: TRect;
  745. APenStyle: TPenStyle; c: TBGRAPixel);
  746. begin
  747. ABitmap.LineCap := pecFlat;
  748. ABitmap.PenStyle:= APenStyle;
  749. ABitmap.DrawLineAntialias(ARect.Left+ARect.Width/10-0.5,ARect.Top+ARect.Height/2-0.5,
  750. ARect.Right-ARect.Width/10-0.5,ARect.Top+ARect.Height/2-0.5, c, ARect.Width/10);
  751. end;
  752. procedure DrawArrow(AComboBox: TBCComboBox; ARect: TRect; AStart: boolean; AKindStr: string; ALineCap: TPenEndCap; State: TOwnerDrawState);
  753. var mask, bmp : TBGRABitmap;
  754. c,c2: TBGRAPixel;
  755. begin
  756. if odSelected in State then
  757. begin
  758. c2 := ColorToBGRA(AComboBox.DropDownHighlight);
  759. c := ColorToBGRA(AComboBox.DropDownFontHighlight);
  760. end else
  761. begin
  762. c2 := ColorToBGRA(AComboBox.DropDownColor);
  763. c := ColorToBGRA(AComboBox.DropDownFontColor);
  764. end;
  765. with Size(ARect) do mask:= TBGRABitmap.Create(cx,cy,BGRABlack);
  766. DrawArrowMask(mask, AStart, AKindStr, ALineCap);
  767. bmp := TBGRABitmap.Create(mask.Width,mask.Height,c2);
  768. bmp.FillMask(0,0,mask,c,dmDrawWithTransparency);
  769. bmp.Draw(ACombobox.Canvas,ARect.Left,ARect.Top,true);
  770. bmp.Free;
  771. mask.Free;
  772. end;
  773. procedure DrawArrow(ABitmap: TBGRABitmap; ARect: TRect; AStart: boolean; AKindStr: string; ALineCap: TPenEndCap; AColor: TBGRAPixel); overload;
  774. var mask: TBGRABitmap;
  775. begin
  776. with Size(ARect) do mask:= TBGRABitmap.Create(cx,cy,BGRABlack);
  777. DrawArrowMask(mask, AStart, AKindStr, ALineCap);
  778. ABitmap.FillMask(ARect.Left,ARect.Top, mask, AColor, dmDrawWithTransparency);
  779. mask.Free;
  780. end;
  781. function CreateMarbleTexture(tx,ty: integer): TBGRABitmap;
  782. var
  783. colorOscillation: integer;
  784. p: PBGRAPixel;
  785. i: Integer;
  786. begin
  787. result := CreateCyclicPerlinNoiseMap(tx,ty,0.5,0.5,0.8,rfBestQuality);
  788. p := result.Data;
  789. for i := 0 to result.NbPixels-1 do
  790. begin
  791. colorOscillation := round(sqrt(sqrt((sin(p^.red*Pi/128+0.5)+1)/2))*256);
  792. p^ := Interp256(BGRA(161,117,105),BGRA(218,197,180),colorOscillation);
  793. inc(p);
  794. end;
  795. end;
  796. function CreateWaterTexture(tx,ty: integer): TBGRABitmap;
  797. const blurSize = 5;
  798. var
  799. temp: TBGRABitmap;
  800. phong: TPhongShading;
  801. begin
  802. result := CreateCyclicPerlinNoiseMap(tx,ty,1,1,1.2,rfBestQuality);
  803. temp:= result.GetPart(rect(-blurSize,-blurSize,tx+blurSize,ty+blurSize)) as TBGRABitmap;
  804. BGRAReplace(temp,temp.FilterBlurRadial(blurSize,rbFast));
  805. phong := TPhongShading.Create;
  806. phong.LightSourceDistanceFactor := 0;
  807. phong.LightDestFactor := 0;
  808. phong.LightSourceIntensity := 150;
  809. phong.LightPositionZ := 80;
  810. phong.LightColor := BGRA(105,233,240);
  811. phong.NegativeDiffusionFactor := 0.3;
  812. phong.SpecularIndex := 20;
  813. phong.AmbientFactor := 0.4;
  814. phong.Draw(result,temp,20,-blurSize,-blurSize,BGRA(28,139,166));
  815. phong.Free;
  816. temp.Free;
  817. end;
  818. function CreateStoneTexture(tx,ty: integer): TBGRABitmap;
  819. var
  820. temp: TBGRABitmap;
  821. phong: TPhongShading;
  822. begin
  823. result := CreateCyclicPerlinNoiseMap(tx,ty,1,1,0.6);
  824. temp:= result.GetPart(rect(-2,-2,tx+2,ty+2)) as TBGRABitmap;
  825. phong := TPhongShading.Create;
  826. phong.LightSourceDistanceFactor := 0;
  827. phong.LightDestFactor := 0;
  828. phong.LightSourceIntensity := 100;
  829. phong.LightPositionZ := 100;
  830. phong.NegativeDiffusionFactor := 0.3;
  831. phong.AmbientFactor := 0.5;
  832. phong.Draw(result,temp,30,-2,-2,BGRA(170,170,170));
  833. phong.Free;
  834. temp.Free;
  835. end;
  836. function CreateRoundStoneTexture(tx,ty: integer): TBGRABitmap;
  837. var
  838. temp: TBGRABitmap;
  839. phong: TPhongShading;
  840. begin
  841. result := CreateCyclicPerlinNoiseMap(tx,ty,1,1,1.2,rfBestQuality);
  842. temp:= result.GetPart(rect(-2,-2,tx+2,ty+2)) as TBGRABitmap;
  843. BGRAReplace(temp,temp.FilterBlurRadial(2,rbFast));
  844. phong := TPhongShading.Create;
  845. phong.LightSourceDistanceFactor := 0;
  846. phong.LightDestFactor := 0;
  847. phong.LightSourceIntensity := 70;
  848. phong.LightPositionZ := 100;
  849. phong.NegativeDiffusionFactor := 0;
  850. phong.SpecularIndex := 10;
  851. phong.AmbientFactor := 0.5;
  852. phong.LightColor := BGRA(255,255,192);
  853. phong.Draw(result,temp,30,-2,-2,BGRA(170,170,170));
  854. phong.Free;
  855. temp.Free;
  856. end;
  857. function CreateSnowPrintTexture(tx,ty: integer): TBGRABitmap;
  858. var
  859. v: single;
  860. p: PBGRAPixel;
  861. i: Integer;
  862. temp: TBGRABitmap;
  863. phong: TPhongShading;
  864. begin
  865. result := CreateCyclicPerlinNoiseMap(tx,ty,1,1,1.2,rfBestQuality);
  866. p := result.Data;
  867. for i := 0 to result.NbPixels-1 do
  868. begin
  869. v := p^.red;
  870. if v > 80 then v := (v-80)/10+80;
  871. if v < 50 then v := 50-(50-v)/10;
  872. p^ := MapHeightToBGRA(v/255,255);
  873. inc(p);
  874. end;
  875. temp:= result.GetPart(rect(-2,-2,tx+2,ty+2)) as TBGRABitmap;
  876. phong := TPhongShading.Create;
  877. phong.LightSourceDistanceFactor := 0;
  878. phong.LightDestFactor := 0;
  879. phong.LightSourceIntensity := 100;
  880. phong.LightPositionZ := 100;
  881. phong.NegativeDiffusionFactor := 0.3;
  882. phong.Draw(result,temp,30,-2,-2,BGRAWhite);
  883. phong.Free;
  884. temp.Free;
  885. end;
  886. function CreateCamouflageTexture(tx,ty: integer): TBGRABitmap;
  887. var
  888. v: integer;
  889. p: PBGRAPixel;
  890. i: Integer;
  891. temp: TBGRABitmap;
  892. begin
  893. result := CreateCyclicPerlinNoiseMap(tx,ty,1,1,1,rfBestQuality);
  894. p := result.Data;
  895. for i := 0 to result.NbPixels-1 do
  896. begin
  897. v := p^.red;
  898. if v < 64 then p^:= BGRA(31,33,46) else
  899. if v < 128 then p^:= BGRA(89,71,57) else
  900. if v < 192 then p^:= BGRA(80,106,67) else
  901. p^:= BGRA(161,157,121);
  902. inc(p);
  903. end;
  904. temp := result.getPart(rect(-2,-2,tx+2,ty+2)) as TBGRABitmap;
  905. BGRAReplace(temp,temp.FilterMedian(moMediumSmooth));
  906. result.PutImage(-2,-2,temp,dmSet);
  907. temp.Free;
  908. end;
  909. function CreatePlastikTexture(tx,ty: integer): TBGRABitmap;
  910. const blurSize = 2;
  911. var
  912. temp: TBGRABitmap;
  913. phong: TPhongShading;
  914. p: PBGRAPixel;
  915. i: Integer;
  916. v: Byte;
  917. begin
  918. result := CreateCyclicPerlinNoiseMap(tx,ty,1,1,1);
  919. p := result.Data;
  920. for i := 0 to result.NbPixels-1 do
  921. begin
  922. v := p^.red;
  923. if v < 32 then v:= v*2 else
  924. if (v > 32) and (v < 224) then v := (v-32) div 2 + 64 else
  925. if v >= 224 then v:= (v-224)*2+(224-32) div 2;
  926. p^:= BGRA(v,v,v);
  927. inc(p);
  928. end;
  929. temp:= result.GetPart(rect(-blurSize,-blurSize,tx+blurSize,ty+blurSize)) as TBGRABitmap;
  930. BGRAReplace(temp,temp.FilterNormalize(False));
  931. BGRAReplace(temp,temp.FilterBlurMotion(ty div 6,90,False));
  932. BGRAReplace(temp,temp.FilterBlurRadial(blurSize,rbFast));
  933. phong := TPhongShading.Create;
  934. phong.LightSourceDistanceFactor := 0;
  935. phong.LightDestFactor := 0;
  936. phong.LightSourceIntensity := 300;
  937. phong.LightPositionZ := 10;
  938. phong.NegativeDiffusionFactor := 0;
  939. phong.AmbientFactor := 0.6;
  940. phong.SpecularIndex := 25;
  941. phong.SpecularFactor:= 10;
  942. phong.Draw(result,temp,10,-blurSize,-blurSize,BGRA(58,206,113));
  943. phong.Free;
  944. temp.Free;
  945. end;
  946. function CreateMetalFloorTexture(tx: integer): TBGRABitmap;
  947. var
  948. temp,noise: TBGRABitmap;
  949. phong: TPhongShading;
  950. ty: integer;
  951. begin
  952. ty := tx div 2;
  953. result := TBGRABitmap.Create(tx,ty,BGRABlack);
  954. result.FillEllipseAntialias(tx*1.2/8,ty/2,tx/20,ty/3,BGRA(240,240,240));
  955. result.FillEllipseAntialias(tx*2.8/8,ty/2,tx/20,ty/3,BGRA(240,240,240));
  956. result.FillEllipseAntialias(tx*3/4,ty*1.2/4,ty/3,tx/20,BGRA(240,240,240));
  957. result.FillEllipseAntialias(tx*3/4,ty*2.8/4,ty/3,tx/20,BGRA(240,240,240));
  958. BGRAReplace(result,result.FilterBlurRadial(1,rbFast));
  959. noise := CreateCyclicPerlinNoiseMap(tx,ty,1,1,1);
  960. noise.FillRect(0,0,tx,ty,BGRA(0,0,0,220),dmLinearBlend);
  961. result.BlendImage(0,0,noise,boAdditive);
  962. noise.free;
  963. temp:= result.GetPart(rect(-2,-2,tx+2,ty+2)) as TBGRABitmap;
  964. phong := TPhongShading.Create;
  965. phong.LightSourceDistanceFactor := 0;
  966. phong.LightDestFactor := 0;
  967. phong.LightSourceIntensity := 100;
  968. phong.LightPositionZ := 80;
  969. phong.NegativeDiffusionFactor := 0;
  970. phong.AmbientFactor := 0.5;
  971. phong.Draw(result,temp,10,-2,-2,BGRA(116,116,116));
  972. phong.Free;
  973. temp.Free;
  974. end;
  975. function ComputeAngle(dx, dy: single): single;
  976. begin
  977. if dy = 0 then
  978. begin
  979. if dx < 0 then result := 180 else result := 0;
  980. end else
  981. if dx = 0 then
  982. begin
  983. if dy < 0 then result := -90 else result := 90;
  984. end else
  985. begin
  986. result := ArcTan(dy/dx)*180/Pi;
  987. if dx < 0 then result += 180;
  988. end;
  989. end;
  990. function GetSelectionCenter(bmp: TBGRABitmap): TPointF;
  991. var xb,yb: integer; p: PBGRAPixel;
  992. xsum,ysum,asum,alpha: single;
  993. begin
  994. if bmp = nil then
  995. begin
  996. result := pointF(0,0);
  997. exit;
  998. end;
  999. xsum := 0;
  1000. ysum := 0;
  1001. asum := 0;
  1002. for yb := 0 to bmp.Height-1 do
  1003. begin
  1004. p := bmp.ScanLine[yb];
  1005. for xb := 0 to bmp.Width-1 do
  1006. begin
  1007. alpha := p^.red/255; inc(p);
  1008. xsum += xb*alpha;
  1009. ysum += yb*alpha;
  1010. asum += alpha;
  1011. end;
  1012. end;
  1013. if asum = 0 then
  1014. result := pointF(bmp.width/2-0.5, bmp.Height/2-0.5) else
  1015. result := pointF(xsum/asum,ysum/asum);
  1016. end;
  1017. procedure ComputeSelectionMask(image: TBGRABitmap; destMask: TBGRABitmap; ARect: TRect);
  1018. var
  1019. aimage: byte;
  1020. xb,yb: integer; pimage, pmask: PBGRAPixel;
  1021. begin
  1022. IntersectRect(ARect, ARect,rect(0,0,image.Width,image.Height));
  1023. IntersectRect(ARect, ARect,rect(0,0,destMask.Width,destMask.Height));
  1024. for yb := ARect.Top to ARect.Bottom-1 do
  1025. begin
  1026. pimage := image.ScanLine[yb]+ARect.Left;
  1027. pmask := destMask.ScanLine[yb]+ARect.Left;
  1028. for xb := ARect.Left to ARect.Right-1 do
  1029. begin
  1030. aimage := pimage^.alpha;
  1031. pmask^ := BGRA(aimage,aimage,aimage,255);
  1032. if aimage <> 0 then pimage^.alpha := 255;
  1033. inc(pimage);
  1034. inc(pmask);
  1035. end;
  1036. end;
  1037. end;
  1038. procedure SubstractMask(image: TBGRABitmap; DestX,DestY: Integer; mask: TBGRABitmap; SourceMaskRect: TRect);
  1039. var
  1040. xb,yb: integer;
  1041. pimage, pmask: PBGRAPixel;
  1042. aimage, amask: byte;
  1043. Delta: TPoint;
  1044. begin
  1045. if not IntersectRect(SourceMaskRect,SourceMaskRect,rect(0,0,mask.Width,mask.Height)) then exit;
  1046. Delta.X := - SourceMaskRect.Left + DestX;
  1047. Delta.Y := - SourceMaskRect.Top + DestY;
  1048. OffsetRect(SourceMaskRect, Delta.x, Delta.y);
  1049. if not IntersectRect(SourceMaskRect,SourceMaskRect,rect(0,0,image.Width,image.Height)) then exit;
  1050. OffsetRect(SourceMaskRect, -Delta.x, -Delta.y);
  1051. for yb := SourceMaskRect.Top to SourceMaskRect.Bottom-1 do
  1052. begin
  1053. pimage := image.ScanLine[yb+Delta.Y]+SourceMaskRect.Left+Delta.X;
  1054. pmask := mask.ScanLine[yb]+SourceMaskRect.Left;
  1055. for xb := SourceMaskRect.Left to SourceMaskRect.Right-1 do
  1056. begin
  1057. amask := pmask^.red;
  1058. if amask <> 0 then
  1059. begin
  1060. aimage := pimage^.alpha;
  1061. if aimage > amask then
  1062. pimage^.alpha := aimage-amask else
  1063. pimage^ := BGRAPixelTransparent;
  1064. end;
  1065. inc(pimage);
  1066. inc(pmask);
  1067. end;
  1068. end;
  1069. end;
  1070. function NicePointBounds(x,y: single): TRect;
  1071. begin
  1072. result := rect(floor(x)-NicePointMaxRadius-1,floor(y)-NicePointMaxRadius-1,
  1073. ceil(x)+NicePointMaxRadius+2,ceil(y)+NicePointMaxRadius+2);
  1074. end;
  1075. function NicePoint(bmp: TBGRABitmap; x, y: single; alpha: byte = 192): TRect;
  1076. begin
  1077. result := NicePointBounds(x,y);
  1078. if not Assigned(bmp) then exit;
  1079. bmp.EllipseAntialias(x,y,NicePointMaxRadius,NicePointMaxRadius,BGRA(0,0,0,alpha),1);
  1080. bmp.EllipseAntialias(x,y,NicePointMaxRadius-1,NicePointMaxRadius-1,BGRA(255,255,255,alpha),1);
  1081. bmp.EllipseAntialias(x,y,NicePointMaxRadius-2,NicePointMaxRadius-2,BGRA(0,0,0,alpha),1);
  1082. end;
  1083. function NicePoint(bmp: TBGRABitmap; ptF: TPointF; alpha: byte = 192): TRect;
  1084. begin
  1085. result := NicePoint(bmp,ptF.x,ptF.y,alpha);
  1086. end;
  1087. procedure NiceLine(bmp: TBGRABitmap; x1, y1, x2, y2: single; alpha: byte = 192);
  1088. begin
  1089. if not Assigned(bmp) then exit;
  1090. bmp.DrawLineAntialias(round(x1), round(y1), round(x2), round(y2),BGRA(0,0,0,alpha),3,True);
  1091. bmp.DrawLineAntialias(round(x1), round(y1), round(x2), round(y2),BGRA(255,255,255,alpha),1,True);
  1092. end;
  1093. function NiceText(bmp: TBGRABitmap; x, y, bmpWidth,bmpHeight: integer; s: string; align: TAlignment; valign: TTextLayout): TRect;
  1094. var fx: TBGRATextEffect;
  1095. f: TFont;
  1096. ofs: integer;
  1097. previousClip: TRect;
  1098. begin
  1099. f := TFont.Create;
  1100. f.Name := 'Arial';
  1101. f.Height := DoScaleY(16,OriginalDPI);
  1102. ofs := DoScaleX(4,OriginalDPI);
  1103. fx := TBGRATextEffect.Create(s,f,true);
  1104. if valign = tlBottom then y := y-fx.TextSize.cy else
  1105. if valign = tlCenter then y := y-fx.TextSize.cy div 2;
  1106. if y+fx.TextSize.cy > bmpHeight then y := bmpHeight-fx.TextSize.cy;
  1107. if y < 0 then y := 0;
  1108. if align = taRightJustify then x := x-fx.TextSize.cx else
  1109. if align = taCenter then x := x-fx.TextSize.cx div 2;
  1110. if x+fx.TextSize.cx > bmpWidth then x := bmpWidth-fx.TextSize.cx;
  1111. if x < 0 then x := 0;
  1112. result := rect(x,y,x+fx.TextWidth+2*ofs,y+fx.TextHeight+2*ofs);
  1113. if Assigned(bmp) then
  1114. begin
  1115. previousClip := bmp.ClipRect;
  1116. bmp.ClipRect := result;
  1117. fx.DrawShadow(bmp,x+ofs,y+ofs,ofs,BGRABlack);
  1118. fx.DrawOutline(bmp,x,y,BGRABlack);
  1119. fx.Draw(bmp,x,y,BGRAWhite);
  1120. bmp.ClipRect := previousClip;
  1121. end;
  1122. fx.Free;
  1123. f.Free;
  1124. end;
  1125. function ComputeColorCircle(tx, ty: integer; light: word; hueCorrection: boolean = true): TBGRABitmap;
  1126. var xb,yb : integer;
  1127. pdest: PBGRAPixel;
  1128. angle,xc,yc: single;
  1129. ec: TExpandedPixel;
  1130. c: TBGRAPixel;
  1131. gray,level: Word;
  1132. begin
  1133. result := TBGRABitmap.Create(tx,ty);
  1134. result.FillEllipseAntialias(tx/2-0.5,ty/2-0.5,tx/2,ty/2,BGRABlack);
  1135. xc := tx/2-0.5;
  1136. yc := ty/2-0.5;
  1137. for yb := 0 to ty-1 do
  1138. begin
  1139. pdest := result.scanline[yb];
  1140. For xb := 0 to tx-1 do
  1141. begin
  1142. if pdest^.alpha <> 0 then
  1143. begin
  1144. ec.alpha := $FFFF;
  1145. angle := ComputeAngle(xb-xc,yb-yc);
  1146. if angle < 0 then angle += 360;
  1147. if hueCorrection then
  1148. angle := GtoH(round(angle/360*65536) and 65535)/65536*360;
  1149. if angle < 60 then
  1150. begin
  1151. ec.red := $FFFF;
  1152. ec.green := round(angle/60*$FFFF);
  1153. ec.blue := $0000;
  1154. end else
  1155. if angle < 120 then
  1156. begin
  1157. ec.red := $FFFF-round((angle-60)/60*$FFFF);
  1158. ec.green := $FFFF;
  1159. ec.blue := $0000;
  1160. end else
  1161. if angle < 180 then
  1162. begin
  1163. ec.red := $0000;
  1164. ec.green := $FFFF;
  1165. ec.blue := round((angle-120)/60*$FFFF);
  1166. end else
  1167. if angle < 240 then
  1168. begin
  1169. ec.red := $0000;
  1170. ec.green := $FFFF-round((angle-180)/60*$FFFF);
  1171. ec.blue := $FFFF;
  1172. end else
  1173. if angle < 300 then
  1174. begin
  1175. ec.red := round((angle-240)/60*$FFFF);
  1176. ec.green := $0000;
  1177. ec.blue := $FFFF;
  1178. end else
  1179. begin
  1180. ec.red := $FFFF;
  1181. ec.green := $0000;
  1182. ec.blue := $FFFF-round((angle-300)/60*$FFFF);
  1183. end;
  1184. gray := min($FFFF,max(0,$FFFF - round((sqrt(sqr((xb-xc)/(tx/2))+sqr((yb-yc)/(ty/2)))*1.2-0.1)*$FFFF)));
  1185. level := max(max(ec.red,ec.green),ec.blue);
  1186. {$hints off}
  1187. ec.red := (ec.red*(not gray)+level*gray) shr 16;
  1188. ec.green := (ec.green*(not gray)+level*gray) shr 16;
  1189. ec.blue := (ec.blue*(not gray)+level*gray) shr 16;
  1190. {$hints on}
  1191. ec.red := (ec.red*light) shr 16;
  1192. ec.green := (ec.green*light) shr 16;
  1193. ec.blue := (ec.blue*light) shr 16;
  1194. c := GammaCompression(ec);
  1195. c.alpha := pdest^.alpha;
  1196. pdest^ := c;
  1197. end;
  1198. inc(pdest);
  1199. end;
  1200. end;
  1201. end;
  1202. initialization
  1203. Randomize;
  1204. end.