fills.inc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 1999-2000 by Thomas Schatzl and Carl Eric Codere
  4. This include implements polygon filling and flood filling.
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. { simple descriptive name }
  12. function max(a, b : Longint) : Longint;
  13. begin
  14. max := b;
  15. if (a > b) then max := a;
  16. end;
  17. { here too }
  18. function min(a, b : Longint) : Longint;
  19. begin
  20. min := b;
  21. if (a < b) then min := a;
  22. end;
  23. procedure fillpoly(numpoints : Word; var polypoints);
  24. { disable range check mode }
  25. {$ifopt R+}
  26. {$define OPT_R_WAS_ON}
  27. {$R-}
  28. {$endif}
  29. type
  30. pedge = ^tedge;
  31. tedge = packed record
  32. yMin, yMax, x, dX, dY, frac : Longint;
  33. end;
  34. pedgearray = ^tedgearray;
  35. tedgearray = array[0..0] of tedge;
  36. ppedgearray = ^tpedgearray;
  37. tpedgearray = array[0..0] of pedge;
  38. var
  39. nActive, nNextEdge : Longint;
  40. p0, p1 : pointtype;
  41. i, j, gap, x0, x1, y, nEdges : Longint;
  42. ET : pedgearray;
  43. GET, AET : ppedgearray;
  44. t : pedge;
  45. ptable : ^pointtype;
  46. begin
  47. { /********************************************************************
  48. * Add entries to the global edge table. The global edge table has a
  49. * bucket for each scan line in the polygon. Each bucket contains all
  50. * the edges whose yMin == yScanline. Each bucket contains the yMax,
  51. * the x coordinate at yMax, and the denominator of the slope (dX)
  52. */}
  53. getmem(et, sizeof(tedge) * numpoints);
  54. getmem(get, sizeof(pedge) * numpoints);
  55. getmem(aet, sizeof(pedge) * numpoints);
  56. ptable := @polypoints;
  57. { check for getmem success }
  58. nEdges := 0;
  59. for i := 0 to (numpoints-1) do begin
  60. p0 := ptable[i];
  61. if (i+1) >= numpoints then p1 := ptable[0]
  62. else p1 := ptable[i+1];
  63. { draw the edges }
  64. Line(p0.x,p0.y,p1.x,p1.y);
  65. { ignore if this is a horizontal edge}
  66. if (p0.y = p1.y) then continue;
  67. { swap ptable if necessary to ensure p0 contains yMin}
  68. if (p0.y > p1.y) then begin
  69. p0 := p1;
  70. p1 := ptable[i];
  71. end;
  72. { create the new edge }
  73. et^[nEdges].ymin := p0.y;
  74. et^[nEdges].ymax := p1.y;
  75. et^[nEdges].x := p0.x;
  76. et^[nEdges].dX := p1.x-p0.x;
  77. et^[nEdges].dy := p1.y-p0.y;
  78. et^[nEdges].frac := 0;
  79. get^[nEdges] := @et^[nEdges];
  80. inc(nEdges);
  81. end;
  82. { sort the GET on ymin }
  83. gap := 1;
  84. while (gap < nEdges) do gap := 3*gap+1;
  85. gap := gap div 3;
  86. while (gap > 0) do begin
  87. for i := gap to (nEdges-1) do begin
  88. j := i - gap;
  89. while (j >= 0) do begin
  90. if (GET^[j]^.ymin <= GET^[j+gap]^.yMin) then break;
  91. t := GET^[j];
  92. GET^[j] := GET^[j+gap];
  93. GET^[j+gap] := t;
  94. dec(j, gap);
  95. end;
  96. end;
  97. gap := gap div 3;
  98. end;
  99. { initialize the active edge table, and set y to first entering edge}
  100. nActive := 0;
  101. nNextEdge := 0;
  102. y := GET^[nNextEdge]^.ymin;
  103. { Now process the edges using the scan line algorithm. Active edges
  104. will be added to the Active Edge Table (AET), and inactive edges will
  105. be deleted. X coordinates will be updated with incremental integer
  106. arithmetic using the slope (dY / dX) of the edges. }
  107. while (nNextEdge < nEdges) or (nActive <> 0) do begin
  108. {Move from the ET bucket y to the AET those edges whose yMin == y
  109. (entering edges) }
  110. while (nNextEdge < nEdges) and (GET^[nNextEdge]^.ymin = y) do begin
  111. AET^[nActive] := GET^[nNextEdge];
  112. inc(nActive);
  113. inc(nNextEdge);
  114. end;
  115. { Remove from the AET those entries for which yMax == y (leaving
  116. edges) }
  117. i := 0;
  118. while (i < nActive) do begin
  119. if (AET^[i]^.yMax = y) then begin
  120. dec(nActive);
  121. System.move(AET^[i+1], AET^[i], (nActive-i)*sizeof(pedge));
  122. end else
  123. inc(i);
  124. end;
  125. if (y >= 0) then begin
  126. {Now sort the AET on x. Since the list is usually quite small,
  127. the sort is implemented as a simple non-recursive shell sort }
  128. gap := 1;
  129. while (gap < nActive) do gap := 3*gap+1;
  130. gap := gap div 3;
  131. while (gap > 0) do begin
  132. for i := gap to (nActive-1) do begin
  133. j := i - gap;
  134. while (j >= 0) do begin
  135. if (AET^[j]^.x <= AET^[j+gap]^.x) then break;
  136. t := AET^[j];
  137. AET^[j] := AET^[j+gap];
  138. AET^[j+gap] := t;
  139. dec(j, gap);
  140. end;
  141. end;
  142. gap := gap div 3;
  143. end;
  144. { Fill in desired pixels values on scan line y by using pairs of x
  145. coordinates from the AET }
  146. i := 0;
  147. while (i < nActive) do begin
  148. x0 := AET^[i]^.x;
  149. x1 := AET^[i+1]^.x;
  150. {Left edge adjustment for positive fraction. 0 is interior. }
  151. if (AET^[i]^.frac >= 0) then inc(x0);
  152. {Right edge adjustment for negative fraction. 0 is exterior. }
  153. if (AET^[i+1]^.frac <= 0) then dec(x1);
  154. x0 := max(x0, 0);
  155. x1 := min(x1, viewWidth);
  156. { Draw interior spans}
  157. if (x1 >= x0) then begin
  158. PatternLine(x0, x1, y);
  159. end;
  160. inc(i, 2);
  161. end;
  162. end;
  163. { Update all the x coordinates. Edges are scan converted using a
  164. modified midpoint algorithm (Bresenham's algorithm reduces to the
  165. midpoint algorithm for two dimensional lines) }
  166. for i := 0 to (nActive-1) do begin
  167. t := AET^[i];
  168. { update the fraction by dX}
  169. inc(t^.frac, t^.dX);
  170. if (t^.dX < 0) then
  171. while ( -(t^.frac) >= t^.dY) do begin
  172. inc(t^.frac, t^.dY);
  173. dec(t^.x);
  174. end
  175. else
  176. while (t^.frac >= t^.dY) do begin
  177. dec(t^.frac, t^.dY);
  178. inc(t^.x);
  179. end;
  180. end;
  181. inc(y);
  182. if (y >= ViewHeight) then break;
  183. end;
  184. System.freemem(et, sizeof(tedge) * numpoints);
  185. System.freemem(get, sizeof(pedge) * numpoints);
  186. System.freemem(aet, sizeof(pedge) * numpoints);
  187. end;
  188. { maximum supported Y resultion }
  189. const
  190. MaxYRes = 2048;
  191. { changing this to 1 or 2 doesn't improve performance noticably }
  192. YResDiv = 4;
  193. type
  194. PFloodLine = ^TFloodLine;
  195. TFloodLine = record
  196. next: PFloodLine;
  197. x1 : smallint;
  198. x2 : smallint;
  199. y : smallint;
  200. end;
  201. TDrawnList = Array[0..(MaxYRes - 1) div YResDiv] of PFloodLine;
  202. var
  203. DrawnList : TDrawnList;
  204. Buffer : Record { Union for byte and word addressing of buffer }
  205. ByteIndex : Word;
  206. WordIndex : Word;
  207. Case Boolean Of
  208. False : (Bytes : Array [0..StdBufferSize-1] Of Byte);
  209. True : (Words : Array [0..(StdBufferSize DIV 2)-1] Of Word);
  210. End;
  211. s1, s2, s3 : PWordArray; { Three buffers for scanlines }
  212. Procedure PushPoint (x, y : smallint);
  213. {********************************************************}
  214. { Adds a point to the list of points to check if we }
  215. { need to draw. Doesn't add the point if there is a }
  216. { buffer overflow. }
  217. {********************************************************}
  218. Begin
  219. If Buffer.WordIndex<(StdBufferSize DIV 2)-3 then
  220. Begin
  221. Buffer.Words[Buffer.WordIndex]:=x;
  222. Buffer.Words[Buffer.WordIndex+1]:=y;
  223. Inc (Buffer.WordIndex,2);
  224. End
  225. End;
  226. Procedure PopPoint (Var x, y : smallint);
  227. {********************************************************}
  228. { Removes a point from the list of points to check, if }
  229. { we try to access an illegal point, then the routine }
  230. { returns -1,-1 as a coordinate pair. }
  231. {********************************************************}
  232. Begin
  233. If Buffer.WordIndex>1 then
  234. Begin
  235. x:=Buffer.Words[Buffer.WordIndex-2];
  236. y:=Buffer.Words[Buffer.WordIndex-1];
  237. Dec (Buffer.WordIndex,2);
  238. End
  239. Else
  240. Begin
  241. x:=-1;
  242. y:=-1;
  243. End;
  244. End;
  245. {********************************************************}
  246. { Procedure AddLinePoints() }
  247. {--------------------------------------------------------}
  248. { Adds a line segment to the list of lines which will be }
  249. { drawn to the screen. The line added is on the specified}
  250. { Y axis, from the x1 to x2 coordinates. }
  251. {********************************************************}
  252. Procedure AddLinePoints(x1,x2,y: smallint);
  253. var temp: PFloodLine;
  254. begin
  255. new(temp);
  256. temp^.x1 := x1;
  257. temp^.x2 := x2;
  258. temp^.y := y;
  259. temp^.next := DrawnList[y div YResDiv];
  260. DrawnList[y div YResDiv] := temp;
  261. end;
  262. {********************************************************}
  263. { Procedure AlreadyDrawn() }
  264. {--------------------------------------------------------}
  265. { This routine searches through the list of segments }
  266. { which will be drawn to the screen, and determines if }
  267. { the specified point (x,y) will already be drawn. }
  268. { i.e : Checks if the x,y point lies within a known }
  269. { segment which will be drawn to the screen. This makes }
  270. { sure that we don't draw some segments two times. }
  271. { Return TRUE if the point is already in the segment list}
  272. { to draw, otherwise returns FALSE. }
  273. {********************************************************}
  274. Function AlreadyDrawn(x, y: smallint): boolean;
  275. var
  276. temp : PFloodLine;
  277. begin
  278. AlreadyDrawn := false;
  279. temp := DrawnList[y div YResDiv];
  280. while assigned(temp) do
  281. begin
  282. if (temp^.y = y) and
  283. (temp^.x1 <= x) and
  284. (temp^.x2 >= x) then
  285. begin
  286. AlreadyDrawn := true;
  287. exit;
  288. end;
  289. temp := temp^.next;
  290. end;
  291. end;
  292. {********************************************************}
  293. { Procedure CleanUpDrawnList }
  294. {--------------------------------------------------------}
  295. { removes all elements from the DrawnList. Doesn't init }
  296. { elements of it with NILL }
  297. {********************************************************}
  298. Procedure CleanUpDrawnList;
  299. var
  300. l: longint;
  301. temp1, temp2: PFloodLine;
  302. begin
  303. for l := 0 to high(DrawnList) do
  304. begin
  305. temp1 := DrawnList[l];
  306. while assigned(temp1) do
  307. begin
  308. temp2 := temp1;
  309. temp1 := temp1^.next;
  310. dispose(temp2);
  311. end;
  312. end;
  313. end;
  314. Procedure FloodFill (x, y : smallint; Border: word);
  315. {********************************************************}
  316. { Procedure FloodFill() }
  317. {--------------------------------------------------------}
  318. { This routine fills a region of the screen bounded by }
  319. { the <Border> color. It uses the current fillsettings }
  320. { for the flood filling. Clipping is supported, and }
  321. { coordinates are local/viewport relative. }
  322. {********************************************************}
  323. Var
  324. stemp: PWordArray;
  325. Beginx : smallint;
  326. d, e : Byte;
  327. Cont : Boolean;
  328. BackupColor : Word;
  329. x1, x2, prevy: smallint;
  330. Begin
  331. FillChar(DrawnList,sizeof(DrawnList),0);
  332. { init prevy }
  333. prevy := 32767;
  334. { Save current drawing color }
  335. BackupColor := CurrentColor;
  336. CurrentColor := FillSettings.Color;
  337. { MaxX is based on zero index }
  338. GetMem (s1,(ViewWidth+1)*2); { A pixel color represents a word }
  339. GetMem (s2,(ViewWidth+1)*2); { A pixel color represents a word }
  340. GetMem (s3,(ViewWidth+1)*2); { A pixel color represents a word }
  341. if (not assigned(s1)) or (not assigned(s2)) or (not assigned(s3)) then
  342. begin
  343. _GraphResult := grNoFloodMem;
  344. exit;
  345. end;
  346. If (x<0) Or (y<0) Or
  347. (x>ViewWidth) Or (y>ViewHeight) then Exit;
  348. { Index of points to check }
  349. Buffer.WordIndex:=0;
  350. PushPoint (x,y);
  351. While Buffer.WordIndex>0 Do
  352. Begin
  353. PopPoint (x,y);
  354. { Get the complete lines for the following }
  355. If y <> prevy then
  356. begin
  357. If (prevy - y = 1) then
  358. { previous line was one below the new one, so the previous s2 }
  359. { = new s1 }
  360. Begin
  361. stemp := s3;
  362. s3 := s1;
  363. s1 := s2;
  364. s2 := stemp;
  365. GetScanline(0,ViewWidth,y-1,s2^);
  366. End
  367. Else If (y - prevy = 1) then
  368. { previous line was one above the new one, so the previous s3 }
  369. { = new s1 }
  370. Begin
  371. stemp := s2;
  372. s2 := s1;
  373. s1 := s3;
  374. s3 := stemp;
  375. GetScanline(0,ViewWidth,y+1,s3^);
  376. End
  377. Else
  378. begin
  379. GetScanline(0,ViewWidth,y-1,s2^);
  380. GetScanline(0,ViewWidth,y,s1^);
  381. GetScanline(0,ViewWidth,y+1,s3^);
  382. end;
  383. end;
  384. prevy := y;
  385. { check the current scan line }
  386. While (s1^[x]<>Border) And (x<=ViewWidth) Do Inc (x);
  387. d:=0;
  388. e:=0;
  389. dec(x);
  390. Beginx:=x;
  391. REPEAT
  392. { check the above line }
  393. If y<ViewHeight then
  394. Begin
  395. Cont:=(s3^[x]<>Border) and (not AlreadyDrawn(x,y+1));
  396. If (e=0) And Cont then
  397. Begin
  398. PushPoint (x,y+1);
  399. e:=1;
  400. End
  401. Else
  402. If (e=1) And Not Cont then e:=0;
  403. End;
  404. { check the line below }
  405. If (y>0) then
  406. Begin
  407. Cont:=(s2^[x]<>Border) and (not AlreadyDrawn(x,y-1));
  408. If (d=0) And Cont then
  409. Begin
  410. PushPoint (x,y-1);
  411. d:=1;
  412. End
  413. Else
  414. If (d=1) And Not Cont then d:=0;
  415. End;
  416. Dec (x);
  417. Until (x<0) Or (s1^[x]=Border);
  418. { swap the values }
  419. x1:=x+1;
  420. x2:=BeginX;
  421. if x1 > x2 then
  422. Begin
  423. x:=x1;
  424. x1:=x2;
  425. x2:=x;
  426. end;
  427. { Add to the list of drawn lines }
  428. AddLinePoints(x1,x2,y);
  429. PatternLine (x1,x2,y);
  430. End; { end while }
  431. System.FreeMem (s1,(ViewWidth+1)*2);
  432. System.FreeMem (s2,(ViewWidth+1)*2);
  433. System.FreeMem (s3,(ViewWidth+1)*2);
  434. CleanUpDrawnList;
  435. CurrentColor := BackUpColor;
  436. End;
  437. { restore previous range check mode }
  438. {$ifdef OPT_R_WAS_ON}
  439. {$R+}
  440. {$endif}