fills.inc 14 KB

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