WMGraphicsSmooth.Mod 32 KB

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