AARotate_Fast.pas 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904
  1. // AARotate_Fast v1.2
  2. //
  3. // This unit is based on the VB code written by Lefteris Eleftheriades.
  4. // Please visit "http://www2.cs.ucy.ac.cy/~cs06ee1/", if you want to know more
  5. // about the background of this unit.
  6. //
  7. // v1.22 : 30 Mar 2009
  8. // Fixed floating point exception (occured at Transparent = true) (ver 1.20)
  9. // Added a parameter : Scale (ver 1.20)
  10. // Added a parameter : ApplySrcAlpha (ver 1.21)
  11. // Fixed bug at caculating transparency (ver 1.22)
  12. //
  13. // v1.1 : 23 Mar 2009
  14. // Added a parameter : Transparent
  15. //
  16. // v1.0 : 17 Mar 2009
  17. // Initial release
  18. //
  19. // written by Silhwan Hyun (hyunsh@hanafos.com)
  20. unit AARotate_Fast;
  21. interface
  22. uses Windows, graphics, Math, SysUtils;
  23. // Parameters & Result
  24. // SrcBitmap : The source image to be rotated.
  25. // Rotation : The degree by which the image should be rotated clockwise.
  26. // BgColor : The color of the background where the rotated bitmap does not overlap the
  27. // destination bitmap.
  28. // Transparent : Decides if the BgColor is treated as Transparent color.
  29. // It means BGColor is excluded at adding color elements and Alpha.
  30. // ApplySrcAlpha : This parameter is valid only if the PixelFormat of source image is
  31. // pf32bit.
  32. // Decides whether to apply source image's alpha channel(acually the "rgbReserved"
  33. // elements of TBitmap's pixel. We can load PNG file preserving alpha channel
  34. // data by use of GdipLoadImageFromFile and GdipCreateHBITMAPFromBitmap. See demo
  35. // project.) at calculating the value of destination image's transparency.
  36. // AutoBlend : This parameter is not effective if Transparent is true. (= considered as
  37. // false if Transparent is true)
  38. // Decides if the edges of the rotated image should be blended with the
  39. // background color defined by BgColor.
  40. // If false, the rgbReserved byte of each pixel will be set to the appropriate
  41. // alpha values so that the rotated image can be blended onto another image later
  42. // without a harsh edge.
  43. // Scale : The size(width and height) factor of rotated image.
  44. // Min : 0.1 ~ Max : 10.0 (ex: 0.5 : 50%, 1.0 : 100%, 2.0 : 200%)
  45. // note) Limited the scaled image is not lesser than 5 pixel in height or width
  46. // Result : The rotated image. Returns nil if there are errors.
  47. function FastAARotatedBitmap(SrcBitmap : TBitmap; Rotation : double; BgColor : integer;
  48. Transparent, ApplySrcAlpha, AutoBlend : boolean; Scale : double) : TBitmap;
  49. procedure DrawCircleBack(ABitmap : TBitmap; bgColor, lnColor : integer);
  50. // Draw a circle on Bitmap - see comments in implementation
  51. procedure DrawCircle(Bitmap: TBitmap; CenterX, CenterY, Radius,
  52. LineWidth, Feather: single);
  53. // Draw a disk on Bitmap - see comments in implementation
  54. procedure DrawDisk(Bitmap: TBitmap; CenterX, CenterY, Radius, Feather: single);
  55. implementation
  56. procedure DrawDisk(Bitmap: TBitmap; CenterX, CenterY, Radius, Feather: single);
  57. // Draw a disk on Bitmap. Bitmap must be a 256 color (pf8bit) palette bitmap,
  58. // and parts outside the disk will get palette index 0, parts inside will get
  59. // palette index 255, and in the antialiased area (feather), the pixels will
  60. // get values inbetween.
  61. // ***Parameters***
  62. // Bitmap:
  63. // The bitmap to draw on
  64. // CenterX, CenterY:
  65. // The center of the disk (float precision). Note that [0, 0] would be the
  66. // center of the first pixel. To draw in the exact middle of a 100x100 bitmap,
  67. // use CenterX = 49.5 and CenterY = 49.5
  68. // Radius:
  69. // The radius of the drawn disk in pixels (float precision)
  70. // Feather:
  71. // The feather area. Use 1 pixel for a 1-pixel antialiased area. Pixel centers
  72. // outside 'Radius + Feather / 2' become 0, pixel centers inside 'Radius - Feather/2'
  73. // become 255. Using a value of 0 will yield a bilevel image.
  74. // Copyright (c) 2003 Nils Haeck M.Sc. www.simdesign.nl
  75. var
  76. x, y: integer;
  77. LX, RX, LY, RY: integer;
  78. Fact: integer;
  79. RPF2, RMF2: single;
  80. P: PByteArray;
  81. SqY, SqDist: single;
  82. sqX: array of single;
  83. begin
  84. // Determine some helpful values (singles)
  85. RPF2 := sqr(Radius + Feather/2);
  86. RMF2 := sqr(Radius - Feather/2);
  87. // Determine bounds:
  88. LX := Max(floor(CenterX - RPF2), 0);
  89. RX := Min(ceil (CenterX + RPF2), Bitmap.Width - 1);
  90. LY := Max(floor(CenterY - RPF2), 0);
  91. RY := Min(ceil (CenterY + RPF2), Bitmap.Height - 1);
  92. // Optimization run: find squares of X first
  93. SetLength(SqX, RX - LX + 1);
  94. for x := LX to RX do
  95. SqX[x - LX] := sqr(x - CenterX);
  96. // Loop through Y values
  97. for y := LY to RY do begin
  98. P := Bitmap.Scanline[y];
  99. SqY := Sqr(y - CenterY);
  100. // Loop through X values
  101. for x := LX to RX do begin
  102. // determine squared distance from center for this pixel
  103. SqDist := SqY + SqX[x - LX];
  104. // inside inner circle? Most often..
  105. if sqdist < RMF2 then begin
  106. // inside the inner circle.. just give the scanline the new color
  107. P[x] := 255
  108. end else begin
  109. // inside outer circle?
  110. if sqdist < RPF2 then begin
  111. // We are inbetween the inner and outer bound, now mix the color
  112. Fact := round(((Radius - sqrt(sqdist)) * 2 / Feather) * 127.5 + 127.5);
  113. P[x] := Max(0, Min(Fact, 255)); // just in case limit to [0, 255]
  114. end else begin
  115. P[x] := 0;
  116. end;
  117. end;
  118. end;
  119. end;
  120. end;
  121. procedure DrawCircle(Bitmap: TBitmap; CenterX, CenterY, Radius,
  122. LineWidth, Feather: single);
  123. // Draw a circle on Bitmap. Bitmap must be a 256 color (pf8bit) palette bitmap,
  124. // and parts outside the circle will get palette index 0, parts inside will get
  125. // palette index 255, and in the antialiased area (feather), the pixels will
  126. // get values inbetween.
  127. // ***Parameters***
  128. // Bitmap:
  129. // The bitmap to draw on
  130. // CenterX, CenterY:
  131. // The center of the circle (float precision). Note that [0, 0] would be the
  132. // center of the first pixel. To draw in the exact middle of a 100x100 bitmap,
  133. // use CenterX = 49.5 and CenterY = 49.5
  134. // Radius:
  135. // The radius of the drawn circle in pixels (float precision)
  136. // LineWidth
  137. // The line width of the drawn circle in pixels (float precision)
  138. // Feather:
  139. // The feather area. Use 1 pixel for a 1-pixel antialiased area. Pixel centers
  140. // outside 'Radius + Feather / 2' become 0, pixel centers inside 'Radius - Feather/2'
  141. // become 255. Using a value of 0 will yield a bilevel image. Note that Feather
  142. // must be equal or smaller than LineWidth (or it will be adjusted internally)
  143. // Copyright (c) 2003 Nils Haeck M.Sc. www.simdesign.nl
  144. var
  145. x, y: integer;
  146. LX, RX, LY, RY: integer;
  147. Fact: integer;
  148. ROPF2, ROMF2, RIPF2, RIMF2: single;
  149. OutRad, InRad: single;
  150. P: PByteArray;
  151. SqY, SqDist: single;
  152. sqX: array of single;
  153. begin
  154. // Determine some helpful values (singles)
  155. OutRad := Radius + LineWidth/2;
  156. InRad := Radius - LineWidth/2;
  157. ROPF2 := sqr(OutRad + Feather/2);
  158. ROMF2 := sqr(OutRad - Feather/2);
  159. RIPF2 := sqr(InRad + Feather/2);
  160. RIMF2 := sqr(InRad - Feather/2);
  161. // Determine bounds:
  162. LX := Max(floor(CenterX - ROPF2), 0);
  163. RX := Min(ceil (CenterX + ROPF2), Bitmap.Width - 1);
  164. LY := Max(floor(CenterY - ROPF2), 0);
  165. RY := Min(ceil (CenterY + ROPF2), Bitmap.Height - 1);
  166. // Checks
  167. if Feather > LineWidth then Feather := LineWidth;
  168. // Optimization run: find squares of X first
  169. SetLength(SqX, RX - LX + 1);
  170. for x := LX to RX do
  171. SqX[x - LX] := sqr(x - CenterX);
  172. // Loop through Y values
  173. for y := LY to RY do begin
  174. P := Bitmap.Scanline[y];
  175. SqY := Sqr(y - CenterY);
  176. // Loop through X values
  177. for x := LX to RX do begin
  178. // determine squared distance from center for this pixel
  179. SqDist := SqY + SqX[x - LX];
  180. // now first check if we're completely inside (most often)
  181. if SqDist < RIMF2 then begin
  182. // We're on the disk inside everything
  183. P[x] := 0;
  184. end else begin
  185. // completely outside?
  186. if SqDist < ROPF2 then begin
  187. // inside outer line - feather?
  188. if SqDist < ROMF2 then begin
  189. // check if we're in inside feather area
  190. if SqDist < RIPF2 then begin
  191. // We are in the feather area of inner line, now mix the color
  192. Fact := round(((sqrt(sqdist) - InRad) * 2 / Feather) * 127.5 + 127.5);
  193. P[x] := Max(0, Min(Fact, 255)); // just in case limit to [0, 255]
  194. end else begin
  195. // on the line
  196. P[x] := 255;
  197. end;
  198. end else begin
  199. // We are in the feather area of outer line, now mix the color
  200. Fact := round(((OutRad - sqrt(sqdist)) * 2 / Feather) * 127.5 + 127.5);
  201. P[x] := Max(0, Min(Fact, 255)); // just in case limit to [0, 255]
  202. end;
  203. end else begin
  204. // outside everything
  205. P[x] := 0;
  206. end;
  207. end;
  208. end;
  209. end;
  210. end;
  211. procedure DrawCircleBack(ABitmap : TBitmap; bgColor, lnColor : integer);
  212. // Create a 256-color bitmap and call the DrawCircle procedure
  213. var
  214. i, y: integer;
  215. pal: PLogPalette;
  216. hpal: HPALETTE;
  217. ColRGB, BgrRGB: integer;
  218. //ACenterX, ACenterY,
  219. //ARadius, AFeather,
  220. //ALineWidth
  221. //: single;
  222. begin
  223. // 8 bits per pixel
  224. ABitmap.PixelFormat := pf8bit;
  225. // Create a gradient palette between foreground and background color
  226. GetMem(pal, sizeof(TLogPalette) + sizeof(TPaletteEntry) * 255);
  227. try
  228. pal.palVersion := $300;
  229. pal.palNumEntries := 256;
  230. ColRGB := ColorToRGB(bgColor);
  231. BgrRGB := ColorToRGB(clWhite);
  232. for i := 0 to 255 do begin
  233. pal.palPalEntry[i].peRed := round(i / 255 * (ColRGB AND $FF) + (255 - i) / 255 * (BgrRGB AND $FF));
  234. pal.palPalEntry[i].peGreen := round(i / 255 * (ColRGB shr 8 AND $FF) + (255 - i) / 255 * (BgrRGB shr 8 AND $FF));
  235. pal.palPalEntry[i].peBlue := round(i / 255 * (ColRGB shr 16 AND $FF) + (255 - i) / 255 * (BgrRGB shr 16 AND $FF));
  236. end;
  237. hpal := CreatePalette(pal^);
  238. if hpal <> 0 then
  239. ABitmap.Palette := hpal;
  240. finally
  241. FreeMem(pal);
  242. end;
  243. // Fill bitmap with background color
  244. for y := 0 to ABitmap.Height - 1 do
  245. FillChar(ABitmap.Scanline[y]^, ABitmap.Width, 0);
  246. end;
  247. function aar_roundup(A : Double) : longint;
  248. begin
  249. if Abs(A - trunc(A + 0.0000000005)) < 0.000000001 then
  250. aar_roundup := trunc(A + 0.0000000005)
  251. else
  252. aar_roundup := trunc(A + 1);
  253. end;
  254. function aar_cos(degrees : double) : double;
  255. var
  256. off : double;
  257. idegrees : integer;
  258. begin
  259. off := (degrees / 30 - round(degrees / 30));
  260. if (off < 0.0000001) and (off > -0.0000001) then
  261. begin
  262. idegrees := round(degrees);
  263. if (idegrees < 0) then
  264. idegrees := (360 - (-idegrees mod 360))
  265. else
  266. idegrees := (idegrees mod 360);
  267. case (idegrees) of
  268. 0 : result := 1.0;
  269. 30 : result := 0.866025403784439;
  270. 60 : result := 0.5;
  271. 90 : result := 0.0;
  272. 120 : result := -0.5;
  273. 150 : result := -0.866025403784439;
  274. 180 : result := -1.0;
  275. 210 : result := -0.866025403784439;
  276. 240 : result := -0.5;
  277. 270 : result := 0.0;
  278. 300 : result := 0.5;
  279. 330 : result := 0.866025403784439;
  280. 360 : result := 1.0;
  281. else
  282. result := cos(degrees * 3.14159265358979 / 180); // it shouldn't get here
  283. //result := cos(degrees * 3.141592 / 180); // it shouldn't get here
  284. end;
  285. end else
  286. result := cos(degrees * 3.14159265358979 / 180);
  287. //result := cos(degrees * 3.141592 / 180);
  288. end;
  289. function aar_sin(degrees : double) : double;
  290. begin
  291. result := aar_cos(degrees + 90.0);
  292. end;
  293. function byterange(a : double) : byte;
  294. var
  295. b : integer;
  296. begin
  297. b := round(a);
  298. if b < 0 then
  299. b := 0;
  300. if b > 255 then
  301. b := 255;
  302. result := b;
  303. end;
  304. function dorotate(src : HBITMAP; rotation : double; bgcolor : integer; transparent,
  305. use_src_alpha, autoblend : boolean; scale : double) : HBITMAP;
  306. const
  307. mx : array[0..3] of integer = (-1, 1, 1, -1);
  308. my : array[0..3] of integer = (-1, -1, 1, 1);
  309. var
  310. indminx, indminy : integer;
  311. indmaxx, indmaxy : integer;
  312. // px, py : integer;
  313. px, py : double;
  314. pcos, psin : double;
  315. xres, yres : double;
  316. width, height : integer;
  317. srcbmp : Bitmap;
  318. srcdib : array of TRGBQUAD;
  319. dstdib : array of TRGBQUAD;
  320. srcdibbmap : TBITMAPINFO;
  321. ldc : HDC;
  322. backcolor : TRGBQUAD;
  323. TR, TB, TG : byte;
  324. XX, YY : integer;
  325. ix, iy : integer;
  326. cx, cy : integer;
  327. tx, ty : double;
  328. dx, dy : double;
  329. DstIndex, SrcIndex : integer;
  330. TopL, TopR, BotL, BotR : double;
  331. TopL_B, TopR_B, BotL_B, BotR_B : boolean;
  332. Dst_rgbRed, Dst_rgbBlue, Dst_rgbGreen : double;
  333. screenmode : DEVMODE;
  334. dstbmp : HBITMAP;
  335. dstdibmap : TBITMAPINFO;
  336. autoblend_ : boolean;
  337. Alpha : byte;
  338. RAlpha : double;
  339. pcos2, psin2 : double;
  340. InRegionRatio : double;
  341. A : double;
  342. begin
  343. //Calculate some index values so that values can easily be looked up
  344. indminx := trunc(rotation / 90) mod 4;
  345. indminy := (indminx + 1) mod 4;
  346. indmaxx := (indminx + 2) mod 4;
  347. indmaxy := (indminx + 3) mod 4;
  348. //Load the source bitmaps information
  349. if (GetObject(src, sizeof(srcbmp), @srcbmp) = 0) then
  350. begin
  351. result := 0;
  352. exit;
  353. end;
  354. //Set the rotation axis default values
  355. px := srcbmp.bmWidth div 2; // pivot x
  356. py := srcbmp.bmHeight div 2; // pivot y
  357. // px := srcbmp.bmWidth / 2; // pivot x
  358. // py := srcbmp.bmHeight / 2; // pivot y
  359. //Calculate the cos and sin value
  360. pcos := aar_cos(Rotation) / abs(scale);
  361. psin := aar_sin(Rotation) / abs(scale);
  362. pcos2 := aar_cos(Rotation) * abs(scale);
  363. psin2 := aar_sin(Rotation) * abs(scale);
  364. //Calculate the x and y offset of the rotated image (half the width and height of the rotated image)
  365. xres := mx[indmaxx] * px * pcos2 - my[indmaxx] * py * psin2;
  366. yres := mx[indmaxy] * px * psin2 + my[indmaxy] * py * pcos2;
  367. //Get the width and height of the rotated image
  368. //width := aar_roundup(xres * 2);
  369. A := xres * 2;
  370. if Abs(A - trunc(A + 0.0000000005)) < 0.000000001 then
  371. width := trunc(A + 0.0000000005)
  372. else
  373. width := trunc(A + 1);
  374. //height := aar_roundup(yres * 2);
  375. A := yres * 2;
  376. if Abs(A - trunc(A + 0.0000000005)) < 0.000000001 then
  377. height := trunc(A + 0.0000000005)
  378. else
  379. height := trunc(A + 1);
  380. cx := (width - srcbmp.bmWidth) div 2;
  381. cy := (height - srcbmp.bmHeight) div 2;
  382. //Create the source dib array and the destdib array
  383. SetLength(srcdib, srcbmp.bmWidth * srcbmp.bmHeight);
  384. SetLength(dstdib, width * height);
  385. //Load source bits into srcdib
  386. srcdibbmap.bmiHeader.biSize := sizeof(srcdibbmap.bmiHeader);
  387. srcdibbmap.bmiHeader.biWidth := srcbmp.bmWidth;
  388. srcdibbmap.bmiHeader.biHeight := -srcbmp.bmHeight;
  389. srcdibbmap.bmiHeader.biPlanes := 1;
  390. srcdibbmap.bmiHeader.biBitCount := 32;
  391. srcdibbmap.bmiHeader.biCompression := BI_RGB;
  392. ldc := CreateCompatibleDC(0);
  393. GetDIBits(ldc, src, 0, srcbmp.bmHeight, srcdib, srcdibbmap, DIB_RGB_COLORS);
  394. DeleteDC(ldc);
  395. backcolor.rgbRed := bgcolor and $000000FF;
  396. backcolor.rgbGreen := (bgcolor and $0000FF00) div $00000100;
  397. backcolor.rgbBlue := (bgcolor and $00FF0000) div $00010000;
  398. TR := backcolor.rgbRed;
  399. TG := backcolor.rgbGreen;
  400. TB := backcolor.rgbBlue;
  401. // Surpress AutoBlend option if transparent is true;
  402. if transparent then autoblend_ := false else autoblend_ := autoblend;
  403. for XX := -cx to (width - cx - 1) do // for destination's width
  404. begin
  405. for YY := -cy to (height - cy - 1) do // for destination's height
  406. begin
  407. // Get the rotation translation (gives the SourceImage coordinate for each DestImage x,y)
  408. tx := (XX - px) * PCos - (YY - py) * pSin + px;
  409. ty := (XX - px) * pSin + (YY - py) * PCos + py;
  410. // Get nearest to the left pixel
  411. if (tx > -1) and (tx < 0) then // Consider just outer border
  412. ix := -1
  413. else
  414. ix := trunc(tx);
  415. if (ty > -1) and (ty < 0) then // Consider just outer border
  416. iy := -1
  417. else
  418. iy := trunc(ty);
  419. // Get the digits after the decimal point
  420. dx := Abs(tx - ix);
  421. dy := Abs(ty - iy);
  422. DstIndex := XX + cx + (YY + cy) * Width;
  423. SrcIndex := ix + iy * srcbmp.bmWidth;
  424. if (tx > -1) and (ix + 1 <= srcbmp.bmWidth) and
  425. (ty > -1) and (iy + 1 <= srcbmp.bmHeight) then
  426. begin
  427. // The SourcePixel color maybe a combination of upto four pixels as tx and ty
  428. // are not integers.
  429. // The intersepted (by the current calculated source pixel) area each pixel
  430. // involved (see .doc for more info)
  431. TopL := (1 - dx) * (1 - dy);
  432. TopR := dx * (1 - dy);
  433. BotL := (1 - dx) * dy;
  434. BotR := dx * dy;
  435. // The sum of (TopL + TopR + BotL + BotR) is always 1
  436. if (tx >= 0) and (ix + 1 < srcbmp.bmWidth) and
  437. (ty >= 0) and (iy + 1 < srcbmp.bmHeight) then
  438. begin
  439. // All the intersepted areas are placed within srcbmp region
  440. // Antialiasing: DestColor = SourceTopLeftPixel * TopLeftAreaIntersectedBySourcePixel
  441. // + SourceTopRightPixel * TopRightAreaIntersectedBySourcePixel
  442. // + bottomleft... + bottomrigth...
  443. if not transparent then
  444. begin
  445. dstdib[DstIndex].rgbRed := byterange(srcdib[SrcIndex].rgbRed * TopL
  446. + srcdib[SrcIndex+1].rgbRed * TopR
  447. + srcdib[SrcIndex+srcbmp.bmWidth].rgbRed * BotL
  448. + srcdib[SrcIndex+srcbmp.bmWidth+1].rgbRed * BotR);
  449. dstdib[DstIndex].rgbBlue := byterange(srcdib[SrcIndex].rgbBlue * TopL
  450. + srcdib[SrcIndex+1].rgbBlue * TopR
  451. + srcdib[SrcIndex+srcbmp.bmWidth].rgbBlue * BotL
  452. + srcdib[SrcIndex+srcbmp.bmWidth+1].rgbBlue * BotR);
  453. dstdib[DstIndex].rgbGreen := byterange(srcdib[SrcIndex].rgbGreen * TopL
  454. + srcdib[SrcIndex+1].rgbGreen * TopR
  455. + srcdib[SrcIndex+srcbmp.bmWidth].rgbGreen * BotL
  456. + srcdib[SrcIndex+srcbmp.bmWidth+1].rgbGreen * BotR);
  457. if use_src_alpha then
  458. dstdib[DstIndex].rgbReserved
  459. := byterange(srcdib[SrcIndex].rgbReserved * TopL
  460. + srcdib[SrcIndex+1].rgbReserved * TopR
  461. + srcdib[SrcIndex+srcbmp.bmWidth].rgbReserved * BotL
  462. + srcdib[SrcIndex+srcbmp.bmWidth+1].rgbReserved * BotR)
  463. else
  464. dstdib[DstIndex].rgbReserved := 255; // alpha value for future use
  465. end else
  466. begin // if transparent
  467. RAlpha := 0;
  468. TopL_B := true;
  469. TopR_B := true;
  470. BotL_B := true;
  471. BotR_B := true;
  472. dstdib[DstIndex].rgbRed := 0;
  473. dstdib[DstIndex].rgbBlue := 0;
  474. dstdib[DstIndex].rgbGreen := 0;
  475. if (srcdib[SrcIndex].rgbRed <> TR) or
  476. (srcdib[SrcIndex].rgbBlue <> TB) or
  477. (srcdib[SrcIndex].rgbGreen <> TG) then
  478. begin
  479. if use_src_alpha then
  480. RAlpha := TopL * srcdib[SrcIndex].rgbReserved / 255
  481. else
  482. RAlpha := TopL;
  483. TopL_B := false;
  484. dstdib[DstIndex].rgbRed := byterange(srcdib[SrcIndex].rgbRed * TopL);
  485. dstdib[DstIndex].rgbBlue := byterange(srcdib[SrcIndex].rgbBlue * TopL);
  486. dstdib[DstIndex].rgbGreen := byterange(srcdib[SrcIndex].rgbGreen * TopL);
  487. end;
  488. if TopR <> 0 then
  489. if (srcdib[SrcIndex+1].rgbRed <> TR) or (srcdib[SrcIndex+1].rgbBlue <> TB) or
  490. (srcdib[SrcIndex+1].rgbGreen <> TG) then
  491. begin
  492. if use_src_alpha then
  493. RAlpha := RAlpha + TopR * srcdib[SrcIndex+1].rgbReserved / 255
  494. else
  495. RAlpha := RAlpha + TopR;
  496. TopR_B := false;
  497. dstdib[DstIndex].rgbRed := dstdib[DstIndex].rgbRed
  498. + byterange(srcdib[SrcIndex+1].rgbRed * TopR);
  499. dstdib[DstIndex].rgbBlue := dstdib[DstIndex].rgbBlue
  500. + byterange(srcdib[SrcIndex+1].rgbBlue * TopR);
  501. dstdib[DstIndex].rgbGreen := dstdib[DstIndex].rgbGreen
  502. + byterange(srcdib[SrcIndex+1].rgbGreen * TopR);
  503. end;
  504. if BotL <> 0 then
  505. if (srcdib[SrcIndex+srcbmp.bmWidth].rgbRed <> TR) or
  506. (srcdib[SrcIndex+srcbmp.bmWidth].rgbBlue <> TB) or
  507. (srcdib[SrcIndex+srcbmp.bmWidth].rgbGreen <> TG) then
  508. begin
  509. if use_src_alpha then
  510. RAlpha := RAlpha + BotL * srcdib[SrcIndex+srcbmp.bmWidth].rgbReserved / 255
  511. else
  512. RAlpha := RAlpha + BotL;
  513. BotL_B := false;
  514. dstdib[DstIndex].rgbRed := dstdib[DstIndex].rgbRed
  515. + byterange(srcdib[SrcIndex+srcbmp.bmWidth].rgbRed * BotL);
  516. dstdib[DstIndex].rgbBlue := dstdib[DstIndex].rgbBlue
  517. + byterange(srcdib[SrcIndex+srcbmp.bmWidth].rgbBlue * BotL);
  518. dstdib[DstIndex].rgbGreen := dstdib[DstIndex].rgbGreen
  519. + byterange(srcdib[SrcIndex+srcbmp.bmWidth].rgbGreen * BotL);
  520. end;
  521. if BotR <> 0 then
  522. if (srcdib[SrcIndex+srcbmp.bmWidth+1].rgbRed <> TR) or
  523. (srcdib[SrcIndex+srcbmp.bmWidth+1].rgbBlue <> TB) or
  524. (srcdib[SrcIndex+srcbmp.bmWidth+1].rgbGreen <> TG) then
  525. begin
  526. if use_src_alpha then
  527. RAlpha := RAlpha + BotR * srcdib[SrcIndex+srcbmp.bmWidth+1].rgbReserved / 255
  528. else
  529. RAlpha := RAlpha + BotR;
  530. BotR_B := false;
  531. dstdib[DstIndex].rgbRed := byterange(dstdib[DstIndex].rgbRed
  532. + srcdib[SrcIndex+srcbmp.bmWidth+1].rgbRed * BotR);
  533. dstdib[DstIndex].rgbBlue := byterange(dstdib[DstIndex].rgbBlue
  534. + srcdib[SrcIndex+srcbmp.bmWidth+1].rgbBlue * BotR);
  535. dstdib[DstIndex].rgbGreen := byterange(dstdib[DstIndex].rgbGreen
  536. + srcdib[SrcIndex+srcbmp.bmWidth+1].rgbGreen * BotR);
  537. end;
  538. // InRegionRatio gets the ratio of the non-transparent area
  539. // + 0.0001 : Put to avoid floating point exception by huge number.
  540. InRegionRatio := 1 - (TopL * ord(TopL_B) + TopR * ord(TopR_B)
  541. + BotR * ord(BotR_B) + BotL * ord(BotL_B)) + 0.0001;
  542. dstdib[DstIndex].rgbRed := byterange(dstdib[DstIndex].rgbRed / InRegionRatio);
  543. dstdib[DstIndex].rgbBlue := byterange(dstdib[DstIndex].rgbBlue / InRegionRatio);
  544. dstdib[DstIndex].rgbGreen := byterange(dstdib[DstIndex].rgbGreen / InRegionRatio);
  545. dstdib[DstIndex].rgbReserved := byterange(RAlpha * 255);
  546. end;
  547. end else
  548. begin
  549. // Some elements of the intersepted areas are placed out of srcbmp region.
  550. // For the intersepted areas which are placed out of srcbmp region,
  551. // - Transparent is false AND AutoBlend is true : add background color
  552. // - Transparent is true OR AutoBlend is false : do nothing
  553. // Determine the elements which are placed out of srcbmp region.
  554. TopL_B := false; TopR_B := false; BotL_B := false; BotR_B := false;
  555. if tx < 0 then begin TopL_B := true; BotL_B := true; end;
  556. if ty < 0 then begin TopL_B := true; TopR_B := true; end;
  557. if ix = (srcbmp.bmWidth - 1) then begin TopR_B := true; BotR_B := true; end;
  558. if iy = (srcbmp.bmHeight - 1) then begin BotL_B := true; BotR_B := true; end;
  559. RAlpha := 0;
  560. Dst_rgbRed := 0;
  561. Dst_rgbBlue := 0;
  562. Dst_rgbGreen := 0;
  563. if TopL_B then // if Top-Left element is out of srcbmp region
  564. begin
  565. if autoblend_ then
  566. begin
  567. Dst_rgbRed := backcolor.rgbRed * TopL;
  568. Dst_rgbBlue := backcolor.rgbBlue * TopL;
  569. Dst_rgbGreen := backcolor.rgbGreen * TopL;
  570. end;
  571. end else begin // Top-Left element is within srcbmp region
  572. if not transparent then
  573. begin
  574. Dst_rgbRed := srcdib[SrcIndex].rgbRed * TopL;
  575. Dst_rgbBlue := srcdib[SrcIndex].rgbBlue * TopL;
  576. Dst_rgbGreen := srcdib[SrcIndex].rgbGreen * TopL;
  577. if use_src_alpha then
  578. RAlpha := TopL * srcdib[SrcIndex].rgbReserved / 255
  579. else
  580. RAlpha := TopL;
  581. end else
  582. begin // if transparent then
  583. if (srcdib[SrcIndex].rgbRed <> TR) or (srcdib[SrcIndex].rgbBlue <> TB) or
  584. (srcdib[SrcIndex].rgbGreen <> TG) then
  585. begin
  586. Dst_rgbRed := srcdib[SrcIndex].rgbRed * TopL;
  587. Dst_rgbBlue := srcdib[SrcIndex].rgbBlue * TopL;
  588. Dst_rgbGreen := srcdib[SrcIndex].rgbGreen * TopL;
  589. if use_src_alpha then
  590. RAlpha := TopL * srcdib[SrcIndex].rgbReserved / 255
  591. else
  592. RAlpha := TopL;
  593. end;
  594. end;
  595. end;
  596. if TopR_B then // if Top-Right element is out of srcbmp region
  597. begin
  598. if autoblend_ then
  599. begin
  600. Dst_rgbRed := Dst_rgbRed + backcolor.rgbRed * TopR;
  601. Dst_rgbBlue := Dst_rgbBlue + backcolor.rgbBlue * TopR;
  602. Dst_rgbGreen := Dst_rgbGreen + backcolor.rgbGreen * TopR;
  603. end;
  604. end else begin // Top-Right element is within srcbmp region
  605. if not transparent then
  606. begin
  607. Dst_rgbRed := Dst_rgbRed + srcdib[SrcIndex+1].rgbRed * TopR;
  608. Dst_rgbBlue := Dst_rgbBlue + srcdib[SrcIndex+1].rgbBlue * TopR;
  609. Dst_rgbGreen := Dst_rgbGreen + srcdib[SrcIndex+1].rgbGreen * TopR;
  610. if use_src_alpha then
  611. RAlpha := RAlpha + TopR * srcdib[SrcIndex+1].rgbReserved / 255
  612. else
  613. RAlpha := RAlpha + TopR;
  614. end else
  615. begin // if transparent then
  616. if (srcdib[SrcIndex+1].rgbRed <> TR) or (srcdib[SrcIndex+1].rgbBlue <> TB) or
  617. (srcdib[SrcIndex+1].rgbGreen <> TG) then
  618. begin
  619. Dst_rgbRed := Dst_rgbRed + srcdib[SrcIndex+1].rgbRed * TopR;
  620. Dst_rgbBlue := Dst_rgbBlue + srcdib[SrcIndex+1].rgbBlue * TopR;
  621. Dst_rgbGreen := Dst_rgbGreen + srcdib[SrcIndex+1].rgbGreen * TopR;
  622. if use_src_alpha then
  623. RAlpha := RAlpha + TopR * srcdib[SrcIndex+1].rgbReserved / 255
  624. else
  625. RAlpha := RAlpha + TopR;
  626. end;
  627. end;
  628. end;
  629. if BotL_B then // if Bottom-Left element is out of srcbmp region
  630. begin
  631. if autoblend_ then
  632. begin
  633. Dst_rgbRed := Dst_rgbRed + backcolor.rgbRed * BotL;
  634. Dst_rgbBlue := Dst_rgbBlue + backcolor.rgbBlue * BotL;
  635. Dst_rgbGreen := Dst_rgbGreen + backcolor.rgbGreen * BotL;
  636. end;
  637. end else begin // Bottom-Left element is within srcbmp region
  638. if not transparent then
  639. begin
  640. Dst_rgbRed := Dst_rgbRed + srcdib[SrcIndex+srcbmp.bmWidth].rgbRed * BotL;
  641. Dst_rgbBlue := Dst_rgbBlue + srcdib[SrcIndex+srcbmp.bmWidth].rgbBlue * BotL;
  642. Dst_rgbGreen := Dst_rgbGreen + srcdib[SrcIndex+srcbmp.bmWidth].rgbGreen * BotL;
  643. if use_src_alpha then
  644. RAlpha := RAlpha + BotL * srcdib[SrcIndex+srcbmp.bmWidth].rgbReserved / 255
  645. else
  646. RAlpha := RAlpha + BotL;
  647. end else
  648. begin // if transparent then
  649. if (srcdib[SrcIndex+srcbmp.bmWidth].rgbRed <> TR) or
  650. (srcdib[SrcIndex+srcbmp.bmWidth].rgbBlue <> TB) or
  651. (srcdib[SrcIndex+srcbmp.bmWidth].rgbGreen <> TG) then
  652. begin
  653. Dst_rgbRed := Dst_rgbRed + srcdib[SrcIndex+srcbmp.bmWidth].rgbRed * BotL;
  654. Dst_rgbBlue := Dst_rgbBlue + srcdib[SrcIndex+srcbmp.bmWidth].rgbBlue * BotL;
  655. Dst_rgbGreen := Dst_rgbGreen + srcdib[SrcIndex+srcbmp.bmWidth].rgbGreen * BotL;
  656. if use_src_alpha then
  657. RAlpha := RAlpha + BotL * srcdib[SrcIndex+srcbmp.bmWidth].rgbReserved / 255
  658. else
  659. RAlpha := RAlpha + BotL;
  660. end;
  661. end;
  662. end;
  663. if BotR_B then // if Bottom-Right element is out of srcbmp region
  664. begin
  665. if autoblend_ then
  666. begin
  667. Dst_rgbRed := Dst_rgbRed + backcolor.rgbRed * BotR;
  668. Dst_rgbBlue := Dst_rgbBlue + backcolor.rgbBlue * BotR;
  669. Dst_rgbGreen := Dst_rgbGreen + backcolor.rgbGreen * BotR;
  670. end;
  671. end else begin // Bottom-Right element is within srcbmp region
  672. if not transparent then
  673. begin
  674. Dst_rgbRed := Dst_rgbRed + srcdib[SrcIndex+srcbmp.bmWidth+1].rgbRed * BotR;
  675. Dst_rgbBlue := Dst_rgbBlue + srcdib[SrcIndex+srcbmp.bmWidth+1].rgbBlue * BotR;
  676. Dst_rgbGreen := Dst_rgbGreen + srcdib[SrcIndex+srcbmp.bmWidth+1].rgbGreen * BotR;
  677. if use_src_alpha then
  678. RAlpha := RAlpha + BotR * srcdib[SrcIndex+srcbmp.bmWidth+1].rgbReserved / 255
  679. else
  680. RAlpha := RAlpha + BotR;
  681. end else
  682. begin // if transparent then
  683. if (srcdib[SrcIndex+srcbmp.bmWidth+1].rgbRed <> TR) or
  684. (srcdib[SrcIndex+srcbmp.bmWidth+1].rgbBlue <> TB) or
  685. (srcdib[SrcIndex+srcbmp.bmWidth+1].rgbGreen <> TG) then
  686. begin
  687. Dst_rgbRed := Dst_rgbRed + srcdib[SrcIndex+srcbmp.bmWidth+1].rgbRed * BotR;
  688. Dst_rgbBlue := Dst_rgbBlue + srcdib[SrcIndex+srcbmp.bmWidth+1].rgbBlue * BotR;
  689. Dst_rgbGreen := Dst_rgbGreen + srcdib[SrcIndex+srcbmp.bmWidth+1].rgbGreen * BotR;
  690. if use_src_alpha then
  691. RAlpha := RAlpha + BotR * srcdib[SrcIndex+srcbmp.bmWidth+1].rgbReserved / 255
  692. else
  693. RAlpha := RAlpha + BotR;
  694. end;
  695. end;
  696. end;
  697. if transparent then begin
  698. // InRegionRatio gets the ratio of the area within srcbmp region.
  699. // + 0.0001 : Put to avoid floating point exception by huge number.
  700. InRegionRatio := 1 - (TopL * ord(TopL_B) + TopR * ord(TopR_B)
  701. + BotR * ord(BotR_B) + BotL * ord(BotL_B)) + 0.0001;
  702. dstdib[DstIndex].rgbRed := byterange(Dst_rgbRed / InRegionRatio);
  703. dstdib[DstIndex].rgbBlue := byterange(Dst_rgbBlue / InRegionRatio);
  704. dstdib[DstIndex].rgbGreen := byterange(Dst_rgbGreen / InRegionRatio);
  705. end else begin
  706. dstdib[DstIndex].rgbRed := byterange(Dst_rgbRed);
  707. dstdib[DstIndex].rgbBlue := byterange(Dst_rgbBlue);
  708. dstdib[DstIndex].rgbGreen := byterange(Dst_rgbGreen);
  709. end;
  710. // Set alpha value for future use
  711. if autoblend_ then
  712. dstdib[DstIndex].rgbReserved := 255
  713. else
  714. dstdib[DstIndex].rgbReserved := byterange(RAlpha * 255);
  715. { else if transparent then
  716. begin
  717. dstdib[DstIndex].rgbReserved := byterange(RAlpha * 255);
  718. end else begin
  719. Alpha := byterange((1 - (TopL * integer(TopL_B)
  720. + TopR * integer(TopR_B)
  721. + BotR * integer(BotR_B)
  722. + BotL * integer(BotL_B))) * 255);
  723. dstdib[DstIndex].rgbReserved := Alpha;
  724. end; }
  725. end;
  726. end else
  727. begin
  728. // for entirely out of srcbmp region
  729. // Color the destination with the background color, if (not transparent).
  730. if (not transparent) then
  731. begin
  732. dstdib[DstIndex].rgbRed := backcolor.rgbRed;
  733. dstdib[DstIndex].rgbBlue := backcolor.rgbBlue;
  734. dstdib[DstIndex].rgbGreen := backcolor.rgbGreen;
  735. end;
  736. dstdib[DstIndex].rgbReserved := 0; // alpha value for future use
  737. end;
  738. end; // for YY
  739. end; // for XX
  740. SetLength(srcdib, 0);
  741. //Get Current Display Settings
  742. screenmode.dmSize := sizeof(DEVMODE);
  743. EnumDisplaySettings(nil, $FFFFFFFF{ENUM_CURRENT_SETTINGS}, screenmode);
  744. //Create the final bitmap object
  745. dstbmp := CreateBitmap(width, height, 1, screenmode.dmBitsPerPel, nil);
  746. //Write the bits into the bitmap and return it
  747. dstdibmap.bmiHeader.biSize := sizeof(dstdibmap.bmiHeader);
  748. dstdibmap.bmiHeader.biWidth := width;
  749. dstdibmap.bmiHeader.biHeight := -height;
  750. dstdibmap.bmiHeader.biPlanes := 1;
  751. dstdibmap.bmiHeader.biBitCount := 32;
  752. dstdibmap.bmiHeader.biCompression := BI_RGB;
  753. SetDIBits(0, dstbmp, 0, height, dstdib, dstdibmap, DIB_RGB_COLORS);
  754. SetLength(dstdib, 0);
  755. result := dstbmp;
  756. end;
  757. function FastAARotatedBitmap(SrcBitmap : TBitmap;
  758. Rotation : double;
  759. BgColor : integer;
  760. Transparent,
  761. ApplySrcAlpha,
  762. AutoBlend : boolean;
  763. Scale : double) : TBitmap;
  764. var
  765. res : HBITMAP;
  766. mult : integer;
  767. //MinScale : double;
  768. UseAlphaChannel : boolean;
  769. begin
  770. if SrcBitmap.Empty then
  771. begin
  772. result := nil;
  773. exit;
  774. end;
  775. if SrcBitmap.PixelFormat = pf24bit then
  776. UseAlphaChannel := false
  777. else if SrcBitmap.PixelFormat = pf32bit then
  778. UseAlphaChannel := ApplySrcAlpha
  779. else begin
  780. result := nil;
  781. exit;
  782. end;
  783. //Get rotation between (0, 360)
  784. mult := trunc(Rotation / 360);
  785. if (Rotation >= 0) then
  786. Rotation := Rotation - 360.0 * mult
  787. else
  788. Rotation := Rotation - 360.0 * (mult - 1);
  789. //Limit the scaled image is not lesser than 5 pixel in height or width
  790. {
  791. if (SrcBitmap.Height <= 5) or (SrcBitmap.Width <= 5) then
  792. MinScale := 1
  793. else if SrcBitmap.Height > SrcBitmap.Width then
  794. MinScale := 5 / SrcBitmap.Width
  795. else
  796. MinScale := 5 / SrcBitmap.Height;
  797. //Get scale between (0.1, 10.0)
  798. if Scale < MinScale then
  799. Scale := MinScale;
  800. if Scale < 0.1 then
  801. Scale := 0.1
  802. else if Scale > 10.0 then
  803. Scale := 10.0;
  804. }
  805. res := dorotate(SrcBitmap.Handle, Rotation, BgColor, Transparent, UseAlphaChannel, AutoBlend, Scale);
  806. if res <> 0 then
  807. begin
  808. try
  809. Result := TBitmap.Create;
  810. Result.PixelFormat := pf32bit;
  811. Result.Handle := res;
  812. except
  813. Result := nil;
  814. end;
  815. end else
  816. Result := nil;
  817. end;
  818. end.