fills.inc 14 KB

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