WMGraphicsSmooth.Mod 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165
  1. (**
  2. AUTHOR: "Alexey Morozov";
  3. PURPOSE: "Smooth (antialiased) graphics";
  4. *)
  5. MODULE WMGraphicsSmooth;
  6. IMPORT SYSTEM, Raster, Strings, WMGraphics, WMRectangles, Reals;
  7. CONST
  8. (** Line cap types *)
  9. CapButt* = 0; (** A flat edge is added to each end of the line; *)
  10. CapRound* = 1; (** A rounded end cap is added to each end of the line; *)
  11. CapSquare* = 2; (** A square end cap is added to each end of the line; *)
  12. CapNone* = 3; (** No use of any cap *)
  13. TYPE
  14. Real* = WMGraphics.Real;
  15. (**
  16. Canvas used for smooth (antialiased) graphics
  17. *)
  18. Canvas* = OBJECT(WMGraphics.BufferCanvas)
  19. VAR
  20. lineWidth-: Real; (** line width in pixels *)
  21. capType-: LONGINT; (** line cap type *)
  22. rasterMode: Raster.Mode;
  23. drawColor: WMGraphics.Color;
  24. drawColorMap: ARRAY 256 OF Raster.Pixel;
  25. halfLineWidth: Real;
  26. halfLineWidthBy255: Real;
  27. invLineWidthBy255: Real;
  28. halfLineWidthSqr: Real;
  29. PROCEDURE &New(img : Raster.Image);
  30. VAR firstTime: BOOLEAN;
  31. BEGIN
  32. firstTime := generator = NIL;
  33. New^(img);
  34. IF firstTime THEN (*! for cases when the same canvas is reused with another image *)
  35. generator := Strings.NewString("WMGraphicsSmooth.GenCanvas");
  36. SetLineWidth(1);
  37. SetLineCap(CapRound);
  38. SetDrawColor(WMGraphics.Blue);
  39. Raster.InitMode(rasterMode,Raster.srcOverDst);
  40. END;
  41. Raster.Bind(rasterMode, Raster.PixelFormat, img.fmt);
  42. END New;
  43. PROCEDURE SetDrawColor(color: WMGraphics.Color);
  44. VAR
  45. i: LONGINT;
  46. s: LONGINT;
  47. r, g, b, a: LONGINT;
  48. BEGIN
  49. WMGraphics.ColorToRGBA(color,r,g,b,a);
  50. s := (256 * a) DIV 255;
  51. FOR i := 0 TO 255 DO Raster.SetRGBA(drawColorMap[i],r,g,b,a - (s*i+128) DIV 256); END;
  52. drawColor := color;
  53. END SetDrawColor;
  54. (**
  55. Set line width in pixels
  56. *)
  57. PROCEDURE SetLineWidth*(w: Real);
  58. BEGIN
  59. IF w < 0.5 THEN w := 0.5; END;
  60. IF w # lineWidth THEN
  61. IF w > 0.5 THEN
  62. halfLineWidth := 0.5*w;
  63. halfLineWidthBy255 := 255*halfLineWidth;
  64. invLineWidthBy255 := 255/w;
  65. halfLineWidthSqr := halfLineWidth*halfLineWidth;
  66. END;
  67. lineWidth := w;
  68. END;
  69. END SetLineWidth;
  70. (**
  71. Setup line cap type
  72. *)
  73. PROCEDURE SetLineCap*(lineCap: LONGINT);
  74. BEGIN
  75. capType := MIN(CapSquare,MAX(CapButt,lineCap));
  76. END SetLineCap;
  77. (**
  78. Draw an antialiased line represented by real-valued coordinates of the starting and end points
  79. *)
  80. PROCEDURE LineReal*(x0, y0, x1, y1: Real; color: WMGraphics.Color; mode: LONGINT);
  81. BEGIN
  82. (*! do not do anything in case of an invalid line specification *)
  83. IF Reals.IsNaN(x0) OR Reals.IsNaN(y0) OR Reals.IsNaN(x1) OR Reals.IsNaN(y1) THEN
  84. RETURN;
  85. END;
  86. IF color # drawColor THEN SetDrawColor(color); END;
  87. (* transform local coordinates to the global coordinate system *)
  88. x0 := x0 + dx; y0 := y0 + dy;
  89. x1 := x1 + dx; y1 := y1 + dy;
  90. (* check whether the line crosses the canvas rectangle with account of line width and a few pixels for antialiasing *)
  91. IF WMGraphics.ClipRect IN clipMode THEN
  92. IF ~ClipLineReal((clipRect.l-2)-lineWidth,(clipRect.t-2)-lineWidth,(clipRect.r+1)+lineWidth,(clipRect.b+1)+lineWidth, x0,y0,x1,y1) THEN
  93. RETURN;
  94. END;
  95. ELSIF ~ClipLineReal((limits.l-2)-lineWidth,(limits.t-2)-lineWidth,(limits.r+1)+lineWidth,(limits.b+1)+lineWidth, x0,y0,x1,y1) THEN
  96. RETURN;
  97. END;
  98. IF x0 <= x1 THEN
  99. IF lineWidth <= 0.5 THEN
  100. DrawThinLine(x0,y0, x1,y1);
  101. ELSE
  102. DrawThickLine(x0,y0, x1,y1);
  103. END;
  104. ELSE
  105. IF lineWidth <= 0.5 THEN
  106. DrawThinLine(x1,y1, x0,y0);
  107. ELSE
  108. DrawThickLine(x1,y1, x0,y0);
  109. END;
  110. END;
  111. END LineReal;
  112. PROCEDURE Disk*(x0, y0: Real; radius: Real; color: WMGraphics.Color; mode: LONGINT);
  113. BEGIN
  114. (*! do not do anything in case of an invalid disk specification *)
  115. IF Reals.IsNaN(x0) OR Reals.IsNaN(y0) OR Reals.IsNaN(radius) OR (radius <= 0) THEN
  116. RETURN;
  117. END;
  118. IF color # drawColor THEN SetDrawColor(color); END;
  119. DrawDisk(x0,y0,radius);
  120. END Disk;
  121. PROCEDURE DrawDisk(x0, y0, radius: Real);
  122. VAR
  123. yend, w: LONGINT;
  124. err: Real;
  125. inner, outer: WMRectangles.Rectangle;
  126. d, radiusSqr, s: Real;
  127. PROCEDURE DrawLeftRight(x, y, yend: LONGINT; left: BOOLEAN);
  128. VAR
  129. xx, incx, k, m: LONGINT;
  130. v, v0, v1: Real;
  131. BEGIN
  132. IF left THEN incx := -1; ELSE incx := 1; END;
  133. v0 := x - x0; v1 := y - y0;
  134. err := v0*v0 + v1*v1 - radiusSqr;
  135. v0 := 1 + 2*incx*v0;
  136. v1 := 1 + 2*v1; m := 0;
  137. WHILE y <= yend DO
  138. xx := x; v := err; k := 0;
  139. REPEAT
  140. w := ENTIER(v*s);
  141. IF w < 0 THEN
  142. Raster.Put(img,xx,y,drawColorMap[0],rasterMode);
  143. ELSIF w <= 255 THEN
  144. Raster.Put(img,xx,y,drawColorMap[w],rasterMode);
  145. END;
  146. v := v + v0 + k; INC(k,2); (*v := v + 1 + 2*incx*(xx-x0);*)
  147. INC(xx,incx);
  148. UNTIL w >= 255;
  149. err := err + v1 + m; INC(m,2); (*err := err + 1 + 2*(y - y0);*)
  150. INC(y);
  151. END;
  152. END DrawLeftRight;
  153. PROCEDURE DrawTopBottom(x, y, xend: LONGINT; top: BOOLEAN);
  154. VAR
  155. yy, incy, k, m: LONGINT;
  156. v, v0, v1: Real;
  157. BEGIN
  158. IF top THEN incy := -1; ELSE incy := 1; END;
  159. v0 := x - x0; v1 := y - y0;
  160. err := v0*v0 + v1*v1 - radiusSqr;
  161. v0 := 1 + 2*v0;
  162. v1 := 1 + 2*incy*v1; k := 0;
  163. WHILE x <= xend DO
  164. yy := y; v := err; m := 0;
  165. REPEAT
  166. w := ENTIER(v*s);
  167. IF w < 0 THEN
  168. Raster.Put(img,x,yy,drawColorMap[0],rasterMode);
  169. ELSIF w <= 255 THEN
  170. Raster.Put(img,x,yy,drawColorMap[w],rasterMode);
  171. END;
  172. v := v + v1 + m; INC(m,2); (*v := v + 1 + 2*incy*(yy-y0);*)
  173. INC(yy,incy);
  174. UNTIL w >= 255;
  175. err := err + v0 + k; INC(k,2); (*err := err + 1 + 2*(x - x0);*)
  176. INC(x);
  177. END;
  178. END DrawTopBottom;
  179. BEGIN
  180. (* outer rectangle (with account of a few pixels for antialiasing) *)
  181. outer.l := ENTIER(x0-radius)-4;
  182. outer.r := ENTIER(x0+radius)+3;
  183. outer.t := ENTIER(y0-radius)-4;
  184. outer.b := ENTIER(y0+radius)+3;
  185. IF WMGraphics.ClipRect IN clipMode THEN
  186. WMRectangles.ClipRect(outer, clipRect);
  187. ELSE
  188. WMRectangles.ClipRect(outer, limits);
  189. END;
  190. IF ~WMRectangles.RectEmpty(outer) THEN
  191. (* inner rectangle *)
  192. d := radius*0.707106781186547; (* half of the edge of the square inside the circle *)
  193. inner.l := ENTIER(x0-d)+1;
  194. inner.r := ENTIER(x0+d)+1;
  195. inner.t := ENTIER(y0-d)+1;
  196. inner.b := ENTIER(y0+d)+1;
  197. (*
  198. Scaling factor to compute the distance from the circle multiplied by 255;
  199. this computation uses first order Taylor series approximation of dist(x,y) = sqrt((x-x0)*(x-x0)+(y-y0)*(y-y0)) - radius =
  200. sqrt(err+radius*radius) - radius ~ err/(2*radius),
  201. where err(x,y) = (x-x0)*(x-x0)+(y-y0)*(y-y0) - radius*radius
  202. *)
  203. s := (255/2)/radius; (* scaling factor for computing alpha map index *)
  204. radiusSqr := radius*radius;
  205. IF ~WMRectangles.RectEmpty(inner) & WMRectangles.IsContained(outer,inner) THEN
  206. Raster.Fill(img, inner.l, inner.t, inner.r, inner.b, drawColorMap[0], rasterMode); (*Fill(inner,color,WMGraphics.ModeSrcOverDst);*)
  207. ELSE
  208. WMRectangles.ClipRect(inner, outer);
  209. IF ~WMRectangles.RectEmpty(inner) THEN
  210. Raster.Fill(img, inner.l, inner.t, inner.r, inner.b, drawColorMap[0], rasterMode);
  211. END;
  212. END;
  213. IF inner.l > outer.l THEN DrawLeftRight(inner.l-1,inner.t-2,inner.b+1,TRUE); END;
  214. IF inner.r <= outer.r THEN DrawLeftRight(inner.r,inner.t-2,inner.b+1,FALSE); END;
  215. IF inner.t > outer.t THEN DrawTopBottom(inner.l,inner.t-1,inner.r-1,TRUE); END;
  216. IF inner.b <= outer.b THEN DrawTopBottom(inner.l,inner.b,inner.r-1,FALSE); END;
  217. END;
  218. END DrawDisk;
  219. PROCEDURE PutPixel(x, y: LONGINT; drawColorMapIndex: LONGINT);
  220. VAR bit: LONGINT; adr: ADDRESS;
  221. BEGIN
  222. (*
  223. Implements Raster.Put(img,x,y,drawColorMap[drawColorMapIndex],rasterMode);
  224. *)
  225. IF (0 > x) OR (x >= img.width) OR (0 > y) OR (y >= img.height) THEN RETURN END;
  226. bit := x * img.fmt.bpp; adr := img.adr + y * img.bpr + bit DIV 8; bit := bit MOD 8;
  227. (*Raster.Bind(rasterMode, Raster.PixelFormat, img.fmt);*)
  228. rasterMode.transfer(rasterMode, ADDRESSOF(drawColorMap[drawColorMapIndex]), 0, adr, bit, 1)
  229. END PutPixel;
  230. PROCEDURE PutPixelSwapped(x, y: LONGINT; drawColorMapIndex: LONGINT);
  231. VAR bit: LONGINT; adr: ADDRESS;
  232. BEGIN
  233. (*
  234. Implements Raster.Put(img,y,x,drawColorMap[drawColorMapIndex],rasterMode);
  235. *)
  236. IF (0 > y) OR (y >= img.width) OR (0 > x) OR (x >= img.height) THEN RETURN END;
  237. bit := y * img.fmt.bpp; adr := img.adr + x * img.bpr + bit DIV 8; bit := bit MOD 8;
  238. (*Raster.Bind(rasterMode, Raster.PixelFormat, img.fmt);*)
  239. rasterMode.transfer(rasterMode, ADDRESSOF(drawColorMap[drawColorMapIndex]), 0, adr, bit, 1)
  240. END PutPixelSwapped;
  241. PROCEDURE DrawThinLine(x0, y0, x1, y1: Real);
  242. VAR
  243. incx, incy, x, y, yy, xend, xendCapStart, xendCapNone, xendCapEnd: LONGINT;
  244. dx, dy, sdx, sdy, dc, dm, dd, d0, d1, dr0, dr1, v, v0, v1, w: Real;
  245. putPixel: PROCEDURE{DELEGATE}(x, y: LONGINT; drawColorMapIndex: LONGINT);
  246. PROCEDURE DrawCapNone;
  247. BEGIN
  248. WHILE x # xend DO
  249. dc := dm - dd; (* dc = sdist(x,y) *)
  250. w := ABS(dc);
  251. IF w <= 0 THEN putPixel(x,y,0);
  252. ELSIF w <= 255 THEN putPixel(x,y,ENTIER(w));
  253. END;
  254. v := dc - dx;
  255. w := ABS(v);
  256. IF w <= 255 THEN
  257. yy := y - incy;
  258. IF w <= 0 THEN putPixel(x,yy,0); ELSE putPixel(x,yy,ENTIER(w)); END;
  259. w := -v + dx;
  260. IF w <= 255 THEN
  261. yy := y - incy;
  262. IF w <= 0 THEN putPixel(x,yy,0); ELSE putPixel(x,yy,ENTIER(w)); END;
  263. END;
  264. END;
  265. v := dc + dx;
  266. w := ABS(v);
  267. IF w <= 255 THEN
  268. yy := y + incy;
  269. IF w <= 0 THEN putPixel(x,yy,0); ELSE putPixel(x,yy,ENTIER(w)); END;
  270. w := v + dx;
  271. IF w <= 255 THEN
  272. yy := y + incy;
  273. IF w <= 0 THEN putPixel(x,yy,0); ELSE putPixel(x,yy,ENTIER(w)); END;
  274. END;
  275. END;
  276. IF dm < 0 THEN
  277. INC(y,incy);
  278. dm := dm + dx;
  279. END;
  280. dm := dm - dy;
  281. INC(x,incx);
  282. END;
  283. END DrawCapNone;
  284. PROCEDURE DrawCapButt;
  285. BEGIN
  286. WHILE x # xend DO
  287. dc := dm - dd; (* dc = sdist(x,y) *)
  288. IF (d0 >= 0) & (d1 <= 0) THEN
  289. w := ABS(dc);
  290. IF w <= 0 THEN putPixel(x,y,0);
  291. ELSIF w <= 255 THEN putPixel(x,y,ENTIER(w));
  292. END;
  293. ELSE
  294. w := MAX(MAX(-d0,d1),ABS(dc));
  295. IF w <= 255 THEN putPixel(x,y,ENTIER(w));
  296. END;
  297. END;
  298. v := dc - dx;
  299. w := ABS(v);
  300. IF w <= 255 THEN
  301. yy := y - incy; v0 := d0 - sdy; v1 := d1 - sdy;
  302. w := MAX(MAX(-v0,v1),w);
  303. IF w <= 255 THEN
  304. IF w <= 0 THEN putPixel(x,yy,0);
  305. ELSE putPixel(x,yy,ENTIER(w));
  306. END;
  307. w := -v + dx;
  308. IF w <= 255 THEN
  309. yy := y - incy; v0 := v0 - sdy; v1 := v1 - sdy;
  310. w := MAX(MAX(-v0,v1),w);
  311. IF w <= 0 THEN putPixel(x,yy,0);
  312. ELSIF w <= 255 THEN putPixel(x,yy,ENTIER(w));
  313. END;
  314. END;
  315. END;
  316. END;
  317. v := dc + dx;
  318. w := ABS(v);
  319. IF w <= 255 THEN
  320. yy := y + incy; v0 := d0 + sdy; v1 := d1 + sdy;
  321. w := MAX(MAX(-v0,v1),w);
  322. IF w <= 255 THEN
  323. IF w <= 0 THEN putPixel(x,yy,0);
  324. ELSE putPixel(x,yy,ENTIER(w));
  325. END;
  326. w := v + dx;
  327. IF w <= 255 THEN
  328. yy := y + incy; v0 := v0 + sdy; v1 := v1 + sdy;
  329. w := MAX(MAX(-v0,v1),w);
  330. IF w <= 0 THEN putPixel(x,yy,0);
  331. ELSIF w <= 255 THEN putPixel(x,yy,ENTIER(w));
  332. END;
  333. END;
  334. END;
  335. END;
  336. IF dm < 0 THEN
  337. INC(y,incy);
  338. dm := dm + dx;
  339. d0 := d0 + sdy; d1 := d1 + sdy;
  340. END;
  341. dm := dm - dy;
  342. d0 := d0 + sdx; d1 := d1 + sdx;
  343. INC(x,incx);
  344. END;
  345. END DrawCapButt;
  346. PROCEDURE DrawCapRound;
  347. VAR
  348. x_x0, x_x1, y_y0, y_y1: Real;
  349. BEGIN
  350. WHILE x # xend DO
  351. x_x0 := x - x0; x_x0 := x_x0*x_x0 - 0.01;
  352. x_x1 := x - x1; x_x1 := x_x1*x_x1 - 0.01;
  353. dc := dm - dd; (* dc = sdist(x,y) *)
  354. IF (d0 >= 0) & (d1 <= 0) THEN
  355. w := ABS(dc);
  356. IF w <= 0 THEN putPixel(x,y,0);
  357. ELSIF w <= 255 THEN putPixel(x,y,ENTIER(w));
  358. END;
  359. ELSE
  360. IF d0 < 0 THEN
  361. y_y0 := y - y0;
  362. w := (x_x0 + y_y0*y_y0)*(255/0.1);
  363. ELSE
  364. y_y1 := y - y1;
  365. w := (x_x1 + y_y1*y_y1)*(255/0.1);
  366. END;
  367. IF w <= 0 THEN putPixel(x,y,0);
  368. ELSIF w <= 255 THEN putPixel(x,y,ENTIER(w));
  369. END;
  370. END;
  371. v := dc - dx; w := ABS(v);
  372. IF w <= 255 THEN
  373. yy := y - incy; v0 := d0 - sdy; v1 := d1 - sdy;
  374. IF v0 < 0 THEN
  375. y_y0 := yy - y0; w := (x_x0 + y_y0*y_y0)*(255/0.1);
  376. ELSIF v1 > 0 THEN
  377. y_y1 := yy - y1; w := (x_x1 + y_y1*y_y1)*(255/0.1);
  378. END;
  379. IF w <= 255 THEN
  380. IF w <= 0 THEN putPixel(x,yy,0);
  381. ELSE putPixel(x,yy,ENTIER(w));
  382. END;
  383. END;
  384. yy := y - incy; v0 := v0 - sdy; v1 := v1 - sdy;
  385. w := -v + dx;
  386. IF w <= 255 THEN
  387. IF v0 < 0 THEN
  388. y_y0 := yy - y0; w := (x_x0 + y_y0*y_y0)*(255/0.1);
  389. ELSIF v1 > 0 THEN
  390. y_y1 := yy - y1; w := (x_x1 + y_y1*y_y1)*(255/0.1);
  391. END;
  392. IF w <= 255 THEN
  393. IF w <= 0 THEN putPixel(x,yy,0);
  394. ELSE putPixel(x,yy,ENTIER(w));
  395. END;
  396. END;
  397. END;
  398. END;
  399. v := dc + dx; w := ABS(v);
  400. IF w <= 255 THEN
  401. yy := y + incy; v0 := d0 + sdy; v1 := d1 + sdy;
  402. IF v0 < 0 THEN
  403. y_y0 := yy - y0; w := (x_x0 + y_y0*y_y0)*(255/0.1);
  404. ELSIF v1 > 0 THEN
  405. y_y1 := yy - y1; w := (x_x1 + y_y1*y_y1)*(255/0.1);
  406. END;
  407. IF w <= 255 THEN
  408. IF w <= 0 THEN putPixel(x,yy,0);
  409. ELSE putPixel(x,yy,ENTIER(w));
  410. END;
  411. END;
  412. yy := y + incy; v0 := v0 + sdy; v1 := v1 + sdy;
  413. w := v + dx;
  414. IF w <= 255 THEN
  415. IF v0 < 0 THEN
  416. y_y0 := yy - y0; w := (x_x0 + y_y0*y_y0)*(255/0.1);
  417. ELSIF v1 > 0 THEN
  418. y_y1 := yy - y1; w := (x_x1 + y_y1*y_y1)*(255/0.1);
  419. END;
  420. IF w <= 255 THEN
  421. IF w <= 0 THEN putPixel(x,yy,0);
  422. ELSE putPixel(x,yy,ENTIER(w));
  423. END;
  424. END;
  425. END;
  426. END;
  427. IF dm < 0 THEN
  428. INC(y,incy);
  429. dm := dm + dx;
  430. d0 := d0 + sdy; d1 := d1 + sdy;
  431. END;
  432. dm := dm - dy;
  433. d0 := d0 + sdx; d1 := d1 + sdx;
  434. INC(x,incx);
  435. END;
  436. END DrawCapRound;
  437. BEGIN
  438. dx := x1 - x0;
  439. dy := y1 - y0;
  440. IF ABS(dy) > ABS(dx) THEN (* slope > 1 *)
  441. v := x0; x0 := y0; y0 := v;
  442. v := x1; x1 := y1; y1 := v;
  443. v := dx; dx := dy; dy := v;
  444. putPixel := PutPixelSwapped;
  445. ELSE
  446. putPixel := PutPixel;
  447. END;
  448. IF dx >= 0 THEN incx := 1; ELSE incx := -1; END;
  449. IF dy >= 0 THEN incy := 1; ELSE incy := -1; END;
  450. (* normalize ranges by the distance between the point- will be used for computing the signed distance to the line *)
  451. dd := InvSqrt(dx*dx + dy*dy);
  452. dx := dx*dd; dy := dy*dd;
  453. sdx := dx; sdy := dy; (* differences used for lines perpendicular to the original line *)
  454. (* account for the signs of the coordinate differences *)
  455. IF ~((dx >= 0) & (dy >= 0)) OR (((dx < 0) & (dy < 0))) THEN
  456. dx := -dx; dy := -dy;
  457. END;
  458. IF capType = CapButt THEN
  459. w := halfLineWidth+2;
  460. v0 := w*sdx;
  461. x := ENTIER(x0 - v0);
  462. y := ENTIER(y0 - w*sdy);
  463. v1 := halfLineWidth*dy; (* move along the line perpendicular to the target line and going through the point (x0,y0) *)
  464. xendCapStart := ENTIER(x0 + v1)+incx;
  465. xendCapNone := ENTIER(x1 - v1)-incx;
  466. xendCapEnd := ENTIER(x1 + v0);
  467. ELSIF capType = CapRound THEN
  468. w := 1.4142135623731*halfLineWidth+2; (* halfLineWidth*sqrt(2) precisely *)
  469. v0 := w*sdx;
  470. x := ENTIER(x0 - v0);
  471. y := ENTIER(y0 - w*sdy);
  472. v1 := halfLineWidth*dy; (* move along the line perpendicular to the target line and going through the point (x0,y0) *)
  473. xendCapStart := ENTIER(x0 + v1)+incx;
  474. xendCapNone := ENTIER(x1 - v1)-incx;
  475. xendCapEnd := ENTIER(x1 + v0);
  476. ELSE
  477. x := ENTIER(x0);
  478. y := ENTIER(y0);
  479. xendCapEnd := ENTIER(x1);
  480. END;
  481. (* scaling to avoid multiplication by 255 for computing color map index *)
  482. dx := dx*255;
  483. dy := dy*255;
  484. sdx := sdx*255;
  485. sdy := sdy*255;
  486. dm := dx*(y + 0.5*incy - y0) - dy*((x+incx) - x0); (* signed distance to the line at the midpoint dm = sdist(x+incx,y+0.5*incy) *)
  487. dd := dx*0.5*incy - dy*incx; (* offset for computing signed distance at the current point (x,y-incy) *)
  488. d0 := sdy*(y - y0) + sdx*(x - x0); (* signed distance to the line that goes through (x0,y0) and perpendicular to the original line (sdist0) *)
  489. d1 := sdy*(y - y1) + sdx*(x - x1); (* signed distance to the line taht goes through (x1,y1) and perpendicular to the original line (sdist1) *)
  490. (* account for the signs of the increments *)
  491. IF incx < 0 THEN dy := -dy; sdx := -sdx; END;
  492. IF incy < 0 THEN dx := -dx; sdy := -sdy; END;
  493. CASE capType OF
  494. |CapButt:
  495. IF ((xendCapNone - xendCapStart)*incx > 1) & ((xendCapEnd - xendCapNone)*incx > 1) THEN
  496. xend := xendCapStart;
  497. DrawCapButt;
  498. xend := xendCapNone;
  499. DrawCapNone;
  500. (* compute d0 and d1 distances at the current point *)
  501. IF incx > 0 THEN v0 := sdx; ELSE v0 := -sdx; END;
  502. IF incy > 0 THEN v1 := sdy; ELSE v1 := -sdy; END;
  503. d0 := v1*(y - y0) + v0*(x - x0);
  504. d1 := v1*(y - y1) + v0*(x - x1);
  505. END;
  506. xend := xendCapEnd;
  507. DrawCapButt;
  508. |CapRound:
  509. IF ((xendCapNone - xendCapStart)*incx > 1) & ((xendCapEnd - xendCapNone)*incx > 1) THEN
  510. xend := xendCapStart;
  511. DrawCapRound;
  512. xend := xendCapNone;
  513. DrawCapNone;
  514. (* compute d0 and d1 distances at the current point *)
  515. IF incx > 0 THEN v0 := sdx; ELSE v0 := -sdx; END;
  516. IF incy > 0 THEN v1 := sdy; ELSE v1 := -sdy; END;
  517. d0 := v1*(y - y0) + v0*(x - x0);
  518. d1 := v1*(y - y1) + v0*(x - x1);
  519. END;
  520. xend := xendCapEnd;
  521. DrawCapRound;
  522. ELSE
  523. xend := xendCapEnd;
  524. DrawCapNone;
  525. END;
  526. END DrawThinLine;
  527. PROCEDURE DrawThickLine(x0, y0, x1, y1: Real);
  528. VAR
  529. incx, incy, x, y, yy, xend, xendCapStart, xendCapNone, xendCapEnd: LONGINT;
  530. dx, dy, sdx, sdy, dc, dm, dd, d0, d1, dr0, dr1, v, v0, v1, w: Real;
  531. putPixel: PROCEDURE{DELEGATE}(x, y: LONGINT; drawColorMapIndex: LONGINT);
  532. PROCEDURE DrawCapNone;
  533. BEGIN
  534. WHILE x # xend DO
  535. dc := dm - dd; (* dc = sdist(x,y) *)
  536. w := ABS(dc)-halfLineWidthBy255;
  537. IF w <= 0 THEN putPixel(x,y,0);
  538. ELSIF w <= 255 THEN putPixel(x,y,ENTIER(w));
  539. END;
  540. yy := y;
  541. v := dc - dx;
  542. w := ABS(v) - halfLineWidthBy255;
  543. WHILE w <= 255 DO
  544. DEC(yy,incy);
  545. IF w <= 0 THEN putPixel(x,yy,0);
  546. ELSE putPixel(x,yy,ENTIER(w));
  547. END;
  548. v := v - dx; w := -v - halfLineWidthBy255;
  549. END;
  550. yy := y;
  551. v := dc + dx;
  552. w := ABS(v) - halfLineWidthBy255;
  553. WHILE w <= 255 DO
  554. INC(yy,incy);
  555. IF w <= 0 THEN putPixel(x,yy,0);
  556. ELSE putPixel(x,yy,ENTIER(w));
  557. END;
  558. v := v + dx; w := v - halfLineWidthBy255;
  559. END;
  560. IF dm < 0 THEN
  561. INC(y,incy);
  562. dm := dm + dx;
  563. END;
  564. dm := dm - dy;
  565. INC(x,incx);
  566. END;
  567. END DrawCapNone;
  568. PROCEDURE DrawCapButt;
  569. BEGIN
  570. WHILE x # xend DO
  571. dc := dm - dd; (* dc = sdist(x,y) *)
  572. IF (d0 >= 0) & (d1 <= 0) THEN
  573. w := ABS(dc)-halfLineWidthBy255;
  574. IF w <= 0 THEN putPixel(x,y,0);
  575. ELSIF w <= 255 THEN putPixel(x,y,ENTIER(w));
  576. END;
  577. ELSE
  578. w := MAX(MAX(-d0,d1),ABS(dc)-halfLineWidthBy255);
  579. IF w <= 255 THEN putPixel(x,y,ENTIER(w));
  580. END;
  581. END;
  582. yy := y;
  583. v := dc - dx;
  584. w := ABS(v) - halfLineWidthBy255;
  585. IF w <= 255 THEN
  586. v0 := d0; v1 := d1;
  587. LOOP
  588. DEC(yy,incy); v0 := v0 - sdy; v1 := v1 - sdy;
  589. w := MAX(MAX(-v0,v1),w);
  590. IF w <= 0 THEN putPixel(x,yy,0);
  591. ELSIF w <= 255 THEN putPixel(x,yy,ENTIER(w));
  592. END;
  593. v := v - dx; w := -v - halfLineWidthBy255;
  594. IF w > 255 THEN EXIT; END;
  595. END;
  596. END;
  597. yy := y;
  598. v := dc + dx;
  599. w := ABS(v) - halfLineWidthBy255;
  600. IF w <= 255 THEN
  601. v0 := d0; v1 := d1;
  602. LOOP
  603. INC(yy,incy); v0 := v0 + sdy; v1 := v1 + sdy;
  604. w := MAX(MAX(-v0,v1),w);
  605. IF w <= 0 THEN putPixel(x,yy,0);
  606. ELSIF w <= 255 THEN putPixel(x,yy,ENTIER(w));
  607. END;
  608. v := v + dx; w := v - halfLineWidthBy255;
  609. IF w > 255 THEN EXIT; END;
  610. END;
  611. END;
  612. IF dm < 0 THEN
  613. INC(y,incy);
  614. dm := dm + dx;
  615. d0 := d0 + sdy; d1 := d1 + sdy;
  616. END;
  617. dm := dm - dy;
  618. d0 := d0 + sdx; d1 := d1 + sdx;
  619. INC(x,incx);
  620. END;
  621. END DrawCapButt;
  622. PROCEDURE DrawCapRound;
  623. VAR
  624. x_x0, x_x1, y_y0, y_y1: Real;
  625. BEGIN
  626. WHILE x # xend DO
  627. x_x0 := x - x0; x_x0 := x_x0*x_x0 - halfLineWidthSqr;
  628. x_x1 := x - x1; x_x1 := x_x1*x_x1 - halfLineWidthSqr;
  629. dc := dm - dd; (* dc = sdist(x,y) *)
  630. IF (d0 >= 0) & (d1 <= 0) THEN
  631. w := ABS(dc)-halfLineWidthBy255;
  632. IF w <= 0 THEN putPixel(x,y,0);
  633. ELSIF w <= 255 THEN putPixel(x,y,ENTIER(w));
  634. END;
  635. ELSE
  636. IF d0 < 0 THEN
  637. y_y0 := y - y0;
  638. w := (x_x0 + y_y0*y_y0)*invLineWidthBy255;
  639. ELSE
  640. y_y1 := y - y1;
  641. w := (x_x1 + y_y1*y_y1)*invLineWidthBy255;
  642. END;
  643. IF w <= 0 THEN putPixel(x,y,0);
  644. ELSIF w <= 255 THEN putPixel(x,y,ENTIER(w));
  645. END;
  646. END;
  647. yy := y;
  648. v := dc - dx; v0 := d0; v1 := d1;
  649. w := ABS(v) - halfLineWidthBy255;
  650. WHILE w <= 255 DO
  651. DEC(yy,incy); v0 := v0 - sdy; v1 := v1 - sdy;
  652. IF v0 < 0 THEN
  653. y_y0 := yy - y0;
  654. w := (x_x0 + y_y0*y_y0)*invLineWidthBy255;
  655. ELSIF v1 > 0 THEN
  656. y_y1 := yy - y1;
  657. w := (x_x1 + y_y1*y_y1)*invLineWidthBy255;
  658. END;
  659. IF w <= 0 THEN putPixel(x,yy,0);
  660. ELSIF w <= 255 THEN putPixel(x,yy,ENTIER(w));
  661. END;
  662. v := v - dx; w := -v - halfLineWidthBy255;
  663. END;
  664. yy := y;
  665. v := dc + dx; v0 := d0; v1 := d1;
  666. w := ABS(v) - halfLineWidthBy255;
  667. WHILE w <= 255 DO
  668. INC(yy,incy); v0 := v0 + sdy; v1 := v1 + sdy;
  669. IF v0 < 0 THEN
  670. y_y0 := yy - y0;
  671. w := (x_x0 + y_y0*y_y0)*invLineWidthBy255;
  672. ELSIF v1 > 0 THEN
  673. y_y1 := yy - y1;
  674. w := (x_x1 + y_y1*y_y1)*invLineWidthBy255;
  675. END;
  676. IF w <= 0 THEN putPixel(x,yy,0);
  677. ELSIF w <= 255 THEN putPixel(x,yy,ENTIER(w));
  678. END;
  679. v := v + dx; w := v - halfLineWidthBy255;
  680. END;
  681. IF dm < 0 THEN
  682. INC(y,incy);
  683. dm := dm + dx;
  684. d0 := d0 + sdy; d1 := d1 + sdy;
  685. END;
  686. dm := dm - dy;
  687. d0 := d0 + sdx; d1 := d1 + sdx;
  688. INC(x,incx);
  689. END;
  690. END DrawCapRound;
  691. BEGIN
  692. dx := x1 - x0;
  693. dy := y1 - y0;
  694. IF ABS(dy) > ABS(dx) THEN (* slope > 1 *)
  695. v := x0; x0 := y0; y0 := v;
  696. v := x1; x1 := y1; y1 := v;
  697. v := dx; dx := dy; dy := v;
  698. putPixel := PutPixelSwapped;
  699. ELSE
  700. putPixel := PutPixel;
  701. END;
  702. IF dx >= 0 THEN incx := 1; ELSE incx := -1; END;
  703. IF dy >= 0 THEN incy := 1; ELSE incy := -1; END;
  704. (* normalize ranges by the distance between the point- will be used for computing the signed distance to the line *)
  705. dd := InvSqrt(dx*dx + dy*dy);
  706. dx := dx*dd; dy := dy*dd;
  707. sdx := dx; sdy := dy; (* differences used for lines perpendicular to the original line *)
  708. (* account for the signs of the coordinate differences *)
  709. IF ~((dx >= 0) & (dy >= 0)) OR (((dx < 0) & (dy < 0))) THEN
  710. dx := -dx; dy := -dy;
  711. END;
  712. IF capType = CapButt THEN
  713. w := halfLineWidth+2;
  714. v0 := w*sdx;
  715. x := ENTIER(x0 - v0);
  716. y := ENTIER(y0 - w*sdy);
  717. v1 := halfLineWidth*dy; (* move along the line perpendicular to the target line and going through the point (x0,y0) *)
  718. xendCapStart := ENTIER(x0 + v1)+incx;
  719. xendCapNone := ENTIER(x1 - v1)-incx;
  720. xendCapEnd := ENTIER(x1 + v0);
  721. ELSIF capType = CapRound THEN
  722. w := 1.4142135623731*halfLineWidth+2; (* halfLineWidth*sqrt(2) precisely *)
  723. v0 := w*sdx;
  724. x := ENTIER(x0 - v0);
  725. y := ENTIER(y0 - w*sdy);
  726. v1 := halfLineWidth*dy; (* move along the line perpendicular to the target line and going through the point (x0,y0) *)
  727. xendCapStart := ENTIER(x0 + v1)+incx;
  728. xendCapNone := ENTIER(x1 - v1)-incx;
  729. xendCapEnd := ENTIER(x1 + v0);
  730. ELSE
  731. x := ENTIER(x0);
  732. y := ENTIER(y0);
  733. xendCapEnd := ENTIER(x1);
  734. END;
  735. (* scaling to avoid multiplication by 255 for computing color map index *)
  736. dx := dx*255;
  737. dy := dy*255;
  738. sdx := sdx*255;
  739. sdy := sdy*255;
  740. dm := dx*(y + 0.5*incy - y0) - dy*((x+incx) - x0); (* signed distance to the line at the midpoint dm = sdist(x+incx,y+0.5*incy) *)
  741. dd := dx*0.5*incy - dy*incx; (* offset for computing signed distance at the current point (x,y-incy) *)
  742. d0 := sdy*(y - y0) + sdx*(x - x0); (* signed distance to the line that goes through (x0,y0) and perpendicular to the original line (sdist0) *)
  743. d1 := sdy*(y - y1) + sdx*(x - x1); (* signed distance to the line taht goes through (x1,y1) and perpendicular to the original line (sdist1) *)
  744. (* account for the signs of the increments *)
  745. IF incx < 0 THEN dy := -dy; sdx := -sdx; END;
  746. IF incy < 0 THEN dx := -dx; sdy := -sdy; END;
  747. CASE capType OF
  748. |CapButt:
  749. IF ((xendCapNone - xendCapStart)*incx > 1) & ((xendCapEnd - xendCapNone)*incx > 1) THEN
  750. xend := xendCapStart;
  751. DrawCapButt;
  752. xend := xendCapNone;
  753. DrawCapNone;
  754. (* compute d0 and d1 distances at the current point *)
  755. IF incx > 0 THEN v0 := sdx; ELSE v0 := -sdx; END;
  756. IF incy > 0 THEN v1 := sdy; ELSE v1 := -sdy; END;
  757. d0 := v1*(y - y0) + v0*(x - x0);
  758. d1 := v1*(y - y1) + v0*(x - x1);
  759. END;
  760. xend := xendCapEnd;
  761. DrawCapButt;
  762. |CapRound:
  763. IF ((xendCapNone - xendCapStart)*incx > 1) & ((xendCapEnd - xendCapNone)*incx > 1) THEN
  764. xend := xendCapStart;
  765. DrawCapRound;
  766. xend := xendCapNone;
  767. DrawCapNone;
  768. (* compute d0 and d1 distances at the current point *)
  769. IF incx > 0 THEN v0 := sdx; ELSE v0 := -sdx; END;
  770. IF incy > 0 THEN v1 := sdy; ELSE v1 := -sdy; END;
  771. d0 := v1*(y - y0) + v0*(x - x0);
  772. d1 := v1*(y - y1) + v0*(x - x1);
  773. END;
  774. xend := xendCapEnd;
  775. DrawCapRound;
  776. ELSE
  777. xend := xendCapEnd;
  778. DrawCapNone;
  779. END;
  780. END DrawThickLine;
  781. END Canvas;
  782. PROCEDURE GenCanvas*(img:Raster.Image):WMGraphics.BufferCanvas; (* generator procedure *)
  783. VAR c:Canvas;
  784. BEGIN
  785. NEW(c,img); RETURN c (* img is NIL, needs a call of c.New(img) later on *)
  786. END GenCanvas;
  787. (**
  788. Fast inverse square root (1 / sqrt(x))
  789. Based on the "0x5f3759df" algorithm described in Wikipedia ( https://en.wikipedia.org/wiki/Fast_inverse_square_root )
  790. *)
  791. PROCEDURE InvSqrt(x: REAL): REAL;
  792. VAR y: Real;
  793. BEGIN
  794. y := SYSTEM.VAL(REAL,0x5f3759df - SYSTEM.VAL(LONGINT,x) DIV 2); (* ~4% of error *)
  795. RETURN y * ( 1.5E0 - y * y * (x * 0.5E0) ); (* ~0.15% of error *)
  796. END InvSqrt;
  797. (*
  798. Find intersection of a line with a given rectangle
  799. l, r, t, b: left,right,top,bottom coordinates of the rectangle
  800. x0, y0: starting line point
  801. x1, y1: end line point
  802. Intersection points are returned in x0, y0, x1, y1
  803. *)
  804. PROCEDURE ClipLineReal(l, t, r, b: Real; VAR x0, y0, x1, y1: Real): BOOLEAN;
  805. VAR
  806. dy, dx, x00, y00: Real;
  807. BEGIN
  808. dx := x1 - x0;
  809. dy := y1 - y0;
  810. IF dy = 0 THEN (* horizontal line *)
  811. IF (y0 >= t) & (y0 <= b) THEN
  812. IF x0 <= x1 THEN
  813. RETURN (x0 <= r) & (x1 >= l);
  814. ELSE
  815. RETURN (x1 <= r) & (x0 >= l);
  816. END;
  817. ELSE
  818. RETURN FALSE;
  819. END;
  820. ELSIF dx = 0 THEN (* vertical line *)
  821. IF (x0 >= l) & (x0 <= r) THEN
  822. IF y0 <= y1 THEN
  823. RETURN (y0 <= b) & (y1 >= t);
  824. ELSE
  825. RETURN (y1 <= b) & (y0 >= t);
  826. END;
  827. ELSE
  828. RETURN FALSE;
  829. END;
  830. ELSE
  831. IF x0 < x1 THEN
  832. IF (x0 <= r) & (x1 >= l) THEN
  833. IF y0 <= y1 THEN
  834. IF (y0 <= b) & (y1 >= t) THEN
  835. x00 := x0;
  836. y00 := y0;
  837. IF x0 < l THEN
  838. (* intersection with x = left *)
  839. y0 := y00 + (dy*(l-x00)) / dx; x0 := l;
  840. IF y0 < t THEN
  841. (* intersection with y = top *)
  842. x0 := x00 + (dx*(t-y00)) / dy; y0 := t;
  843. END;
  844. ELSIF y0 < t THEN
  845. (* intersection with y = top *)
  846. x0 := x00 + (dx*(t-y00)) / dy; y0 := t;
  847. IF x0 < l THEN
  848. (* intersection with x = left *)
  849. y0 := y00 + (dy*(l-x00)) / dx; x0 := l;
  850. END;
  851. END;
  852. IF x1 > r THEN
  853. (* intersection with x = right *)
  854. y1 := y00 + (dy*(r-x00)) / dx; x1 := r;
  855. IF y1 > b THEN
  856. (* intersection with y = bottom *)
  857. x1 := x00 + (dx*(b-y00)) / dy; y1 := b;
  858. END;
  859. ELSIF y1 > b THEN
  860. (* intersection with y = bottom *)
  861. x1 := x00 + (dx*(b-y00)) / dy; y1 := b;
  862. IF x1 > r THEN
  863. (* intersection with x = right *)
  864. y1 := y00 + (dy*(r-x00)) / dx; x1 := r;
  865. END;
  866. END;
  867. ASSERT(x0 >= l);
  868. ASSERT(y0 >= t);
  869. ASSERT(x1 <= r);
  870. ASSERT(y1 <= b);
  871. ELSE
  872. RETURN FALSE;
  873. END;
  874. ELSIF (y1 <= b) & (y0 >= t) THEN (* y0 > y1, dy < 0 *)
  875. x00 := x0;
  876. y00 := y0;
  877. dy := -dy;
  878. IF x0 < l THEN
  879. (* intersection with x = left *)
  880. y0 := y00 - (dy*(l-x00)) / dx; x0 := l;
  881. IF y0 > b THEN
  882. (* intersection with y = bottom *)
  883. x0 := x00 - (dx*(b-y00)) / dy; y0 := b;
  884. END;
  885. ELSIF y0 > b THEN
  886. (* intersection with y = bottom *)
  887. x0 := x00 - (dx*(b-y00)) / dy; y0 := b;
  888. IF x0 < l THEN
  889. (* intersection with x = left *)
  890. y0 := y00 - (dy*(l-x00)) / dx; x0 := l;
  891. END;
  892. END;
  893. IF x1 > r THEN
  894. (* intersection with x = right *)
  895. y1 := y00 - (dy*(r-x00)) / dx; x1 := r;
  896. IF y1 < t THEN
  897. (* intersection with y = top *)
  898. x1 := x00 - (dx*(t-y00)) / dy; y1 := t;
  899. END;
  900. ELSIF y1 < t THEN
  901. (* intersection with y = top *)
  902. x1 := x00 - (dx*(t-y00)) / dy; y1 := t;
  903. IF x1 > r THEN
  904. (* intersection with x = right *)
  905. y1 := y00 - (dy*(r-x00)) / dx; x1 := r;
  906. END;
  907. END;
  908. ASSERT(x0 >= l);
  909. ASSERT(y0 <= b);
  910. ASSERT(x1 <= r);
  911. ASSERT(y1 >= t);
  912. ELSE
  913. RETURN FALSE;
  914. END;
  915. ELSE
  916. RETURN FALSE;
  917. END;
  918. ELSE (* x0 >= x1 *)
  919. IF (x1 <= r) & (x0 >= l) THEN
  920. IF y0 <= y1 THEN
  921. IF (y0 <= b) & (y1 >= t) THEN
  922. x00 := x0;
  923. y00 := y0;
  924. dx := -dx;
  925. IF x0 > r THEN
  926. (* intersection with x = right *)
  927. y0 := y00 - (dy*(r-x00)) / dx; x0 := r;
  928. IF y0 < t THEN
  929. (* intersection with y = top *)
  930. x0 := x00 - (dx*(t-y00)) / dy; y0 := t;
  931. END;
  932. ELSIF y0 < t THEN
  933. (* intersection with y = top *)
  934. x0 := x00 - (dx*(t-y00)) / dy; y0 := t;
  935. IF x0 > r THEN
  936. (* intersection with x = right *)
  937. y0 := y00 - (dy*(r-x00)) / dx; x0 := r;
  938. END;
  939. END;
  940. IF x1 < l THEN
  941. (* intersection with x = left *)
  942. y1 := y00 - (dy*(l-x00)) / dx; x1 := l;
  943. IF y1 > b THEN
  944. (* intersection with y = bottom *)
  945. x1 := x00 - (dx*(b-y00)) / dy; y1 := b;
  946. END;
  947. ELSIF y1 > b THEN
  948. (* intersection with y = bottom *)
  949. x1 := x00 - (dx*(b-y00)) / dy; y1 := b;
  950. IF x1 < l THEN
  951. (* intersection with x = left *)
  952. y1 := y00 - (dy*(l-x00)) / dx; x1 := l;
  953. END;
  954. END;
  955. ASSERT(x0 <= r);
  956. ASSERT(y0 >= t);
  957. ASSERT(x1 >= l);
  958. ASSERT(y1 <= b);
  959. ELSE
  960. RETURN FALSE;
  961. END;
  962. ELSIF (y1 <= b) & (y0 >= t) THEN (* dy < 0, dx < 0 *)
  963. x00 := x0;
  964. y00 := y0;
  965. dy := -dy;
  966. dx := -dx;
  967. IF x0 > r THEN
  968. (* intersection with x = right *)
  969. y0 := y00 + (dy*(r-x00)) / dx; x0 := r;
  970. IF y0 > b THEN
  971. (* intersection with y = bottom *)
  972. x0 := x00 + (dx*(b-y00)) / dy; y0 := b;
  973. END;
  974. ELSIF y0 > b THEN
  975. (* intersection with y = bottom *)
  976. x0 := x00 + (dx*(b-y00)) / dy; y0 := b;
  977. IF x0 > r THEN
  978. (* intersection with x = right *)
  979. y0 := y00 + (dy*(r-x00)) / dx; x0 := r;
  980. END;
  981. END;
  982. IF x1 < l THEN
  983. (* intersection with x = left *)
  984. y1 := y00 + (dy*(l-x00)) / dx; x1 := l;
  985. IF y1 < t THEN
  986. (* intersection with y = top *)
  987. x1 := x00 + (dx*(t-y00)) / dy; y1 := t;
  988. END;
  989. ELSIF y1 < t THEN
  990. (* intersection with y = top *)
  991. x1 := x00 + (dx*(t-y00)) / dy; y1 := t;
  992. IF x1 < l THEN
  993. (* intersection with x = left *)
  994. y1 := y00 + (dy*(l-x00)) / dx; x1 := l;
  995. END;
  996. END;
  997. ASSERT(x0 <= r);
  998. ASSERT(y0 <= b);
  999. ASSERT(x1 >= l);
  1000. ASSERT(y1 >= t);
  1001. ELSE
  1002. RETURN FALSE;
  1003. END;
  1004. ELSE
  1005. RETURN FALSE;
  1006. END;
  1007. END;
  1008. END;
  1009. RETURN TRUE
  1010. END ClipLineReal;
  1011. END WMGraphicsSmooth.
  1012. SystemTools.FreeDownTo WMGraphicsSmooth ~