fills.inc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  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. type
  26. pedge = ^tedge;
  27. tedge = packed record
  28. yMin, yMax, x, dX, dY, frac : Longint;
  29. end;
  30. pedgearray = ^tedgearray;
  31. tedgearray = array[0..0] of tedge;
  32. ppedgearray = ^tpedgearray;
  33. tpedgearray = array[0..0] of pedge;
  34. var
  35. nActive, nNextEdge : Longint;
  36. p0, p1 : pointtype;
  37. i, j, gap, x0, x1, y, nEdges : Longint;
  38. ET : pedgearray;
  39. GET, AET : ppedgearray;
  40. t : pedge;
  41. ptable : ^pointtype;
  42. begin
  43. { /********************************************************************
  44. * Add entries to the global edge table. The global edge table has a
  45. * bucket for each scan line in the polygon. Each bucket contains all
  46. * the edges whose yMin == yScanline. Each bucket contains the yMax,
  47. * the x coordinate at yMax, and the denominator of the slope (dX)
  48. */}
  49. getmem(et, sizeof(tedge) * numpoints);
  50. getmem(get, sizeof(pedge) * numpoints);
  51. getmem(aet, sizeof(pedge) * numpoints);
  52. ptable := @polypoints;
  53. { check for getmem success }
  54. nEdges := 0;
  55. for i := 0 to (numpoints-1) do begin
  56. p0 := ptable[i];
  57. if (i+1) >= numpoints then p1 := ptable[0]
  58. else p1 := ptable[i+1];
  59. { ignore if this is a horizontal edge}
  60. if (p0.y = p1.y) then continue;
  61. {swap ptable if necessary to ensure p0 contains yMin}
  62. if (p0.y > p1.y) then begin
  63. p0 := p1;
  64. p1 := ptable[i];
  65. end;
  66. { create the new edge }
  67. et^[nEdges].ymin := p0.y;
  68. et^[nEdges].ymax := p1.y;
  69. et^[nEdges].x := p0.x;
  70. et^[nEdges].dX := p1.x-p0.x;
  71. et^[nEdges].dy := p1.y-p0.y;
  72. et^[nEdges].frac := 0;
  73. get^[nEdges] := @et^[nEdges];
  74. inc(nEdges);
  75. end;
  76. { sort the GET on ymin }
  77. gap := 1;
  78. while (gap < nEdges) do gap := 3*gap+1;
  79. gap := gap div 3;
  80. while (gap > 0) do begin
  81. for i := gap to (nEdges-1) do begin
  82. j := i - gap;
  83. while (j >= 0) do begin
  84. if (GET^[j]^.ymin <= GET^[j+gap]^.yMin) then break;
  85. t := GET^[j];
  86. GET^[j] := GET^[j+gap];
  87. GET^[j+gap] := t;
  88. dec(j, gap);
  89. end;
  90. end;
  91. gap := gap div 3;
  92. end;
  93. { initialize the active edge table, and set y to first entering edge}
  94. nActive := 0;
  95. nNextEdge := 0;
  96. y := GET^[nNextEdge]^.ymin;
  97. { Now process the edges using the scan line algorithm. Active edges
  98. will be added to the Active Edge Table (AET), and inactive edges will
  99. be deleted. X coordinates will be updated with incremental integer
  100. arithmetic using the slope (dY / dX) of the edges. }
  101. while (nNextEdge < nEdges) or (nActive <> 0) do begin
  102. {Move from the ET bucket y to the AET those edges whose yMin == y
  103. (entering edges) }
  104. while (nNextEdge < nEdges) and (GET^[nNextEdge]^.ymin = y) do begin
  105. AET^[nActive] := GET^[nNextEdge];
  106. inc(nActive);
  107. inc(nNextEdge);
  108. end;
  109. { Remove from the AET those entries for which yMax == y (leaving
  110. edges) }
  111. i := 0;
  112. while (i < nActive) do begin
  113. if (AET^[i]^.yMax = y) then begin
  114. dec(nActive);
  115. move(AET^[i+1], AET^[i], (nActive-i)*sizeof(pedge));
  116. end else
  117. inc(i);
  118. end;
  119. if (y >= 0) then begin
  120. {Now sort the AET on x. Since the list is usually quite small,
  121. the sort is implemented as a simple non-recursive shell sort }
  122. gap := 1;
  123. while (gap < nActive) do gap := 3*gap+1;
  124. gap := gap div 3;
  125. while (gap > 0) do begin
  126. for i := gap to (nActive-1) do begin
  127. j := i - gap;
  128. while (j >= 0) do begin
  129. if (AET^[j]^.x <= AET^[j+gap]^.x) then break;
  130. t := AET^[j];
  131. AET^[j] := AET^[j+gap];
  132. AET^[j+gap] := t;
  133. dec(j, gap);
  134. end;
  135. end;
  136. gap := gap div 3;
  137. end;
  138. { Fill in desired pixels values on scan line y by using pairs of x
  139. coordinates from the AET }
  140. i := 0;
  141. while (i < nActive) do begin
  142. x0 := AET^[i]^.x;
  143. x1 := AET^[i+1]^.x;
  144. {Left edge adjustment for positive fraction. 0 is interior. }
  145. if (AET^[i]^.frac > 0) then inc(x0);
  146. {Right edge adjustment for negative fraction. 0 is exterior. }
  147. if (AET^[i+1]^.frac <= 0) then dec(x1);
  148. x0 := max(x0, 0);
  149. x1 := min(x1, viewWidth);
  150. { Draw interior spans}
  151. if (x1 >= x0) then begin
  152. PatternLine(x0, x1, y);
  153. end;
  154. inc(i, 2);
  155. end;
  156. end;
  157. { Update all the x coordinates. Edges are scan converted using a
  158. modified midpoint algorithm (Bresenham's algorithm reduces to the
  159. midpoint algorithm for two dimensional lines) }
  160. for i := 0 to (nActive-1) do begin
  161. t := AET^[i];
  162. { update the fraction by dX}
  163. inc(t^.frac, t^.dX);
  164. if (t^.dX < 0) then
  165. while ( -(t^.frac) >= t^.dY) do begin
  166. inc(t^.frac, t^.dY);
  167. dec(t^.x);
  168. end
  169. else
  170. while (t^.frac >= t^.dY) do begin
  171. dec(t^.frac, t^.dY);
  172. inc(t^.x);
  173. end;
  174. end;
  175. inc(y);
  176. if (y >= ViewHeight) then break;
  177. end;
  178. freemem(et, sizeof(tedge) * numpoints);
  179. freemem(get, sizeof(pedge) * numpoints);
  180. freemem(aet, sizeof(pedge) * numpoints);
  181. end;
  182. { maximum supported Y resultion }
  183. const
  184. MaxYRes = 2048;
  185. { changing this to 1 or 2 doesn't improve performance noticably }
  186. YResDiv = 4;
  187. type
  188. PFloodLine = ^TFloodLine;
  189. TFloodLine = record
  190. next: PFloodLine;
  191. x1 : smallint;
  192. x2 : smallint;
  193. y : smallint;
  194. end;
  195. TDrawnList = Array[0..(MaxYRes - 1) div YResDiv] of PFloodLine;
  196. var
  197. DrawnList : TDrawnList;
  198. Buffer : Record { Union for byte and word addressing of buffer }
  199. ByteIndex : Word;
  200. WordIndex : Word;
  201. Case Boolean Of
  202. False : (Bytes : Array [0..StdBufferSize-1] Of Byte);
  203. True : (Words : Array [0..(StdBufferSize DIV 2)-1] Of Word);
  204. End;
  205. s1, s2, s3 : PWordArray; { Three buffers for scanlines }
  206. Procedure PushPoint (x, y : smallint);
  207. {********************************************************}
  208. { Adds a point to the list of points to check if we }
  209. { need to draw. Doesn't add the point if there is a }
  210. { buffer overflow. }
  211. {********************************************************}
  212. Begin
  213. If Buffer.WordIndex<(StdBufferSize DIV 2)-3 then
  214. Begin
  215. Buffer.Words[Buffer.WordIndex]:=x;
  216. Buffer.Words[Buffer.WordIndex+1]:=y;
  217. Inc (Buffer.WordIndex,2);
  218. End
  219. End;
  220. Procedure PopPoint (Var x, y : smallint);
  221. {********************************************************}
  222. { Removes a point from the list of points to check, if }
  223. { we try to access an illegal point, then the routine }
  224. { returns -1,-1 as a coordinate pair. }
  225. {********************************************************}
  226. Begin
  227. If Buffer.WordIndex>1 then
  228. Begin
  229. x:=Buffer.Words[Buffer.WordIndex-2];
  230. y:=Buffer.Words[Buffer.WordIndex-1];
  231. Dec (Buffer.WordIndex,2);
  232. End
  233. Else
  234. Begin
  235. x:=-1;
  236. y:=-1;
  237. End;
  238. End;
  239. {********************************************************}
  240. { Procedure AddLinePoints() }
  241. {--------------------------------------------------------}
  242. { Adds a line segment to the list of lines which will be }
  243. { drawn to the screen. The line added is on the specified}
  244. { Y axis, from the x1 to x2 coordinates. }
  245. {********************************************************}
  246. Procedure AddLinePoints(x1,x2,y: smallint);
  247. var temp: PFloodLine;
  248. begin
  249. new(temp);
  250. temp^.x1 := x1;
  251. temp^.x2 := x2;
  252. temp^.y := y;
  253. temp^.next := DrawnList[y div YResDiv];
  254. DrawnList[y div YResDiv] := temp;
  255. end;
  256. {********************************************************}
  257. { Procedure AlreadyDrawn() }
  258. {--------------------------------------------------------}
  259. { This routine searches through the list of segments }
  260. { which will be drawn to the screen, and determines if }
  261. { the specified point (x,y) will already be drawn. }
  262. { i.e : Checks if the x,y point lies within a known }
  263. { segment which will be drawn to the screen. This makes }
  264. { sure that we don't draw some segments two times. }
  265. { Return TRUE if the point is already in the segment list}
  266. { to draw, otherwise returns FALSE. }
  267. {********************************************************}
  268. Function AlreadyDrawn(x, y: smallint): boolean;
  269. var
  270. temp : PFloodLine;
  271. begin
  272. AlreadyDrawn := false;
  273. temp := DrawnList[y div YResDiv];
  274. while assigned(temp) do
  275. begin
  276. if (temp^.y = y) and
  277. (temp^.x1 <= x) and
  278. (temp^.x2 >= x) then
  279. begin
  280. AlreadyDrawn := true;
  281. exit;
  282. end;
  283. temp := temp^.next;
  284. end;
  285. end;
  286. {********************************************************}
  287. { Procedure CleanUpDrawnList }
  288. {--------------------------------------------------------}
  289. { removes all elements from the DrawnList. Doesn't init }
  290. { elements of it with NILL }
  291. {********************************************************}
  292. Procedure CleanUpDrawnList;
  293. var
  294. l: longint;
  295. temp1, temp2: PFloodLine;
  296. begin
  297. for l := 0 to high(DrawnList) do
  298. begin
  299. temp1 := DrawnList[l];
  300. while assigned(temp1) do
  301. begin
  302. temp2 := temp1;
  303. temp1 := temp1^.next;
  304. dispose(temp2);
  305. end;
  306. end;
  307. end;
  308. Procedure FloodFill (x, y : smallint; Border: word);
  309. {********************************************************}
  310. { Procedure FloodFill() }
  311. {--------------------------------------------------------}
  312. { This routine fills a region of the screen bounded by }
  313. { the <Border> color. It uses the current fillsettings }
  314. { for the flood filling. Clipping is supported, and }
  315. { coordinates are local/viewport relative. }
  316. {********************************************************}
  317. Var
  318. stemp: PWordArray;
  319. Beginx : smallint;
  320. d, e : Byte;
  321. Cont : Boolean;
  322. BackupColor : Word;
  323. x1, x2, prevy: smallint;
  324. Begin
  325. FillChar(DrawnList,sizeof(DrawnList),0);
  326. { init prevy }
  327. prevy := 32767;
  328. { Save current drawing color }
  329. BackupColor := CurrentColor;
  330. CurrentColor := FillSettings.Color;
  331. { MaxX is based on zero index }
  332. GetMem (s1,(ViewWidth+1)*2); { A pixel color represents a word }
  333. GetMem (s2,(ViewWidth+1)*2); { A pixel color represents a word }
  334. GetMem (s3,(ViewWidth+1)*2); { A pixel color represents a word }
  335. if (not assigned(s1)) or (not assigned(s2)) or (not assigned(s3)) then
  336. begin
  337. _GraphResult := grNoFloodMem;
  338. exit;
  339. end;
  340. If (x<0) Or (y<0) Or
  341. (x>ViewWidth) Or (y>ViewHeight) then Exit;
  342. { Index of points to check }
  343. Buffer.WordIndex:=0;
  344. PushPoint (x,y);
  345. While Buffer.WordIndex>0 Do
  346. Begin
  347. PopPoint (x,y);
  348. { Get the complete lines for the following }
  349. If y <> prevy then
  350. begin
  351. If (prevy - y = 1) then
  352. { previous line was one below the new one, so the previous s2 }
  353. { = new s1 }
  354. Begin
  355. stemp := s3;
  356. s3 := s1;
  357. s1 := s2;
  358. s2 := stemp;
  359. GetScanline(0,ViewWidth,y-1,s2^);
  360. End
  361. Else If (y - prevy = 1) then
  362. { previous line was one above the new one, so the previous s3 }
  363. { = new s1 }
  364. Begin
  365. stemp := s2;
  366. s2 := s1;
  367. s1 := s3;
  368. s3 := stemp;
  369. GetScanline(0,ViewWidth,y+1,s3^);
  370. End
  371. Else
  372. begin
  373. GetScanline(0,ViewWidth,y-1,s2^);
  374. GetScanline(0,ViewWidth,y,s1^);
  375. GetScanline(0,ViewWidth,y+1,s3^);
  376. end;
  377. end;
  378. prevy := y;
  379. { check the current scan line }
  380. While (s1^[x]<>Border) And (x<=ViewWidth) Do Inc (x);
  381. d:=0;
  382. e:=0;
  383. dec(x);
  384. Beginx:=x;
  385. REPEAT
  386. { check the above line }
  387. If y<ViewHeight then
  388. Begin
  389. Cont:=(s3^[x]<>Border) and (not AlreadyDrawn(x,y+1));
  390. If (e=0) And Cont then
  391. Begin
  392. PushPoint (x,y+1);
  393. e:=1;
  394. End
  395. Else
  396. If (e=1) And Not Cont then e:=0;
  397. End;
  398. { check the line below }
  399. If (y>0) then
  400. Begin
  401. Cont:=(s2^[x]<>Border) and (not AlreadyDrawn(x,y-1));
  402. If (d=0) And Cont then
  403. Begin
  404. PushPoint (x,y-1);
  405. d:=1;
  406. End
  407. Else
  408. If (d=1) And Not Cont then d:=0;
  409. End;
  410. Dec (x);
  411. Until (x<0) Or (s1^[x]=Border);
  412. { swap the values }
  413. x1:=x+1;
  414. x2:=BeginX;
  415. if x1 > x2 then
  416. Begin
  417. x:=x1;
  418. x1:=x2;
  419. x2:=x;
  420. end;
  421. { Add to the list of drawn lines }
  422. AddLinePoints(x1,x2,y);
  423. PatternLine (x1,x2,y);
  424. End; { end while }
  425. FreeMem (s1,(ViewWidth+1)*2);
  426. FreeMem (s2,(ViewWidth+1)*2);
  427. FreeMem (s3,(ViewWidth+1)*2);
  428. CleanUpDrawnList;
  429. CurrentColor := BackUpColor;
  430. End;
  431. {
  432. $Log$
  433. Revision 1.18 2000-02-27 14:41:25 peter
  434. * removed warnings/notes
  435. Revision 1.17 2000/02/12 13:39:19 jonas
  436. + new, faster fillpoly from Thomas Schatzl
  437. * some logging commands in vesa.inc disabled
  438. Revision 1.16 2000/01/07 16:41:37 daniel
  439. * copyright 2000
  440. Revision 1.15 2000/01/07 16:32:25 daniel
  441. * copyright 2000 added
  442. Revision 1.14 2000/01/02 19:01:32 jonas
  443. * made floodfill a *LOT* faster (better DrawnPoints management)
  444. Revision 1.13 1999/12/20 11:22:36 peter
  445. * integer -> smallint to overcome -S2 switch needed for ggi version
  446. Revision 1.12 1999/12/11 23:41:38 jonas
  447. * changed definition of getscanlineproc to "getscanline(x1,x2,y:
  448. smallint; var data);" so it can be used by getimage too
  449. * changed getimage so it uses getscanline
  450. * changed floodfill, getscanline16 and definitions in Linux
  451. include files so they use this new format
  452. + getscanlineVESA256 for 256 color VESA modes (banked)
  453. Revision 1.11 1999/09/27 23:34:40 peter
  454. * new graph unit is default for go32v2
  455. * removed warnings/notes
  456. Revision 1.10 1999/09/24 22:52:38 jonas
  457. * optimized patternline a bit (always use hline when possible)
  458. * isgraphmode stuff cleanup
  459. * vesainfo.modelist now gets disposed in cleanmode instead of in
  460. closegraph (required moving of some declarations from vesa.inc to
  461. new vesah.inc)
  462. * queryadapter gets no longer called from initgraph (is called from
  463. initialization of graph unit)
  464. * bugfix for notput in 32k and 64k vesa modes
  465. * a div replaced by / in fillpoly
  466. Revision 1.9 1999/09/24 14:23:08 jonas
  467. * floodfill uses scanline data from previous loop if line is adjacent
  468. Revision 1.8 1999/09/18 22:21:09 jonas
  469. + hlinevesa256 and vlinevesa256
  470. + support for not/xor/or/andput in vesamodes with 32k/64k colors
  471. * lots of changes to avoid warnings under FPC
  472. Revision 1.7 1999/09/17 13:58:31 jonas
  473. * another fix for a case where internalellipsedefault went haywire
  474. * sector() and pieslice() fully implemented!
  475. * small change to prevent buffer overflow with floodfill
  476. Revision 1.6 1999/09/12 17:28:59 jonas
  477. * several changes to internalellipse to make it faster
  478. and to make sure it updates the ArcCall correctly
  479. (not yet done for width = 3)
  480. * Arc mostly works now, only sometimes an endless loop, don't know
  481. why
  482. Revision 1.5 1999/09/11 19:43:00 jonas
  483. * FloodFill: did not take into account current viewport settings
  484. * GetScanLine: only get line inside viewport, data outside of it
  485. is not used anyway
  486. * InternalEllipseDefault: fix for when xradius or yradius = 0 and
  487. increase xradius and yradius always by one (TP does this too)
  488. * fixed conlict in vesa.inc from last update
  489. * some conditionals to avoid range check and overflow errors in
  490. places where it doesn't matter
  491. Revision 1.4 1999/07/12 14:52:52 jonas
  492. * fixed procvar syntax error and ceil and floor functions
  493. Revision 1.3 1999/07/12 13:27:11 jonas
  494. + added Log and Id tags
  495. * added first FPC support, only VGA works to some extend for now
  496. * use -dasmgraph to use assembler routines, otherwise Pascal
  497. equivalents are used
  498. * use -dsupportVESA to support VESA (crashes under FPC for now)
  499. * only dispose vesainfo at closegrph if a vesa card was detected
  500. * changed int32 to longint (int32 is not declared under FPC)
  501. * changed the declaration of almost every procedure in graph.inc to
  502. "far;" becquse otherwise you can't assign them to procvars under TP
  503. real mode (but unexplainable "data segnment too large" errors prevent
  504. it from working under real mode anyway)
  505. }