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. endy, 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. Index : smallint;
  325. Begin
  326. FillChar(DrawnList,sizeof(DrawnList),0);
  327. { init prevy }
  328. prevy := 32767;
  329. { Save current drawing color }
  330. BackupColor := CurrentColor;
  331. CurrentColor := FillSettings.Color;
  332. { MaxX is based on zero index }
  333. GetMem (s1,(ViewWidth+1)*2); { A pixel color represents a word }
  334. GetMem (s2,(ViewWidth+1)*2); { A pixel color represents a word }
  335. GetMem (s3,(ViewWidth+1)*2); { A pixel color represents a word }
  336. if (not assigned(s1)) or (not assigned(s2)) or (not assigned(s3)) then
  337. begin
  338. _GraphResult := grNoFloodMem;
  339. exit;
  340. end;
  341. If (x<0) Or (y<0) Or
  342. (x>ViewWidth) Or (y>ViewHeight) then Exit;
  343. { Some internal variables }
  344. Index := 0;
  345. { Index of points to check }
  346. Buffer.WordIndex:=0;
  347. PushPoint (x,y);
  348. While Buffer.WordIndex>0 Do
  349. Begin
  350. PopPoint (x,y);
  351. { Get the complete lines for the following }
  352. If y <> prevy then
  353. begin
  354. If (prevy - y = 1) then
  355. { previous line was one below the new one, so the previous s2 }
  356. { = new s1 }
  357. Begin
  358. stemp := s3;
  359. s3 := s1;
  360. s1 := s2;
  361. s2 := stemp;
  362. GetScanline(0,ViewWidth,y-1,s2^);
  363. End
  364. Else If (y - prevy = 1) then
  365. { previous line was one above the new one, so the previous s3 }
  366. { = new s1 }
  367. Begin
  368. stemp := s2;
  369. s2 := s1;
  370. s1 := s3;
  371. s3 := stemp;
  372. GetScanline(0,ViewWidth,y+1,s3^);
  373. End
  374. Else
  375. begin
  376. GetScanline(0,ViewWidth,y-1,s2^);
  377. GetScanline(0,ViewWidth,y,s1^);
  378. GetScanline(0,ViewWidth,y+1,s3^);
  379. end;
  380. end;
  381. prevy := y;
  382. { check the current scan line }
  383. While (s1^[x]<>Border) And (x<=ViewWidth) Do Inc (x);
  384. d:=0;
  385. e:=0;
  386. dec(x);
  387. Beginx:=x;
  388. REPEAT
  389. { check the above line }
  390. If y<ViewHeight then
  391. Begin
  392. Cont:=(s3^[x]<>Border) and (not AlreadyDrawn(x,y+1));
  393. If (e=0) And Cont then
  394. Begin
  395. PushPoint (x,y+1);
  396. e:=1;
  397. End
  398. Else
  399. If (e=1) And Not Cont then e:=0;
  400. End;
  401. { check the line below }
  402. If (y>0) then
  403. Begin
  404. Cont:=(s2^[x]<>Border) and (not AlreadyDrawn(x,y-1));
  405. If (d=0) And Cont then
  406. Begin
  407. PushPoint (x,y-1);
  408. d:=1;
  409. End
  410. Else
  411. If (d=1) And Not Cont then d:=0;
  412. End;
  413. Dec (x);
  414. Until (x<0) Or (s1^[x]=Border);
  415. { swap the values }
  416. x1:=x+1;
  417. x2:=BeginX;
  418. if x1 > x2 then
  419. Begin
  420. x:=x1;
  421. x1:=x2;
  422. x2:=x;
  423. end;
  424. { Add to the list of drawn lines }
  425. AddLinePoints(x1,x2,y);
  426. PatternLine (x1,x2,y);
  427. End; { end while }
  428. FreeMem (s1,(ViewWidth+1)*2);
  429. FreeMem (s2,(ViewWidth+1)*2);
  430. FreeMem (s3,(ViewWidth+1)*2);
  431. CleanUpDrawnList;
  432. CurrentColor := BackUpColor;
  433. End;
  434. {
  435. $Log$
  436. Revision 1.17 2000-02-12 13:39:19 jonas
  437. + new, faster fillpoly from Thomas Schatzl
  438. * some logging commands in vesa.inc disabled
  439. Revision 1.16 2000/01/07 16:41:37 daniel
  440. * copyright 2000
  441. Revision 1.15 2000/01/07 16:32:25 daniel
  442. * copyright 2000 added
  443. Revision 1.14 2000/01/02 19:01:32 jonas
  444. * made floodfill a *LOT* faster (better DrawnPoints management)
  445. Revision 1.13 1999/12/20 11:22:36 peter
  446. * integer -> smallint to overcome -S2 switch needed for ggi version
  447. Revision 1.12 1999/12/11 23:41:38 jonas
  448. * changed definition of getscanlineproc to "getscanline(x1,x2,y:
  449. smallint; var data);" so it can be used by getimage too
  450. * changed getimage so it uses getscanline
  451. * changed floodfill, getscanline16 and definitions in Linux
  452. include files so they use this new format
  453. + getscanlineVESA256 for 256 color VESA modes (banked)
  454. Revision 1.11 1999/09/27 23:34:40 peter
  455. * new graph unit is default for go32v2
  456. * removed warnings/notes
  457. Revision 1.10 1999/09/24 22:52:38 jonas
  458. * optimized patternline a bit (always use hline when possible)
  459. * isgraphmode stuff cleanup
  460. * vesainfo.modelist now gets disposed in cleanmode instead of in
  461. closegraph (required moving of some declarations from vesa.inc to
  462. new vesah.inc)
  463. * queryadapter gets no longer called from initgraph (is called from
  464. initialization of graph unit)
  465. * bugfix for notput in 32k and 64k vesa modes
  466. * a div replaced by / in fillpoly
  467. Revision 1.9 1999/09/24 14:23:08 jonas
  468. * floodfill uses scanline data from previous loop if line is adjacent
  469. Revision 1.8 1999/09/18 22:21:09 jonas
  470. + hlinevesa256 and vlinevesa256
  471. + support for not/xor/or/andput in vesamodes with 32k/64k colors
  472. * lots of changes to avoid warnings under FPC
  473. Revision 1.7 1999/09/17 13:58:31 jonas
  474. * another fix for a case where internalellipsedefault went haywire
  475. * sector() and pieslice() fully implemented!
  476. * small change to prevent buffer overflow with floodfill
  477. Revision 1.6 1999/09/12 17:28:59 jonas
  478. * several changes to internalellipse to make it faster
  479. and to make sure it updates the ArcCall correctly
  480. (not yet done for width = 3)
  481. * Arc mostly works now, only sometimes an endless loop, don't know
  482. why
  483. Revision 1.5 1999/09/11 19:43:00 jonas
  484. * FloodFill: did not take into account current viewport settings
  485. * GetScanLine: only get line inside viewport, data outside of it
  486. is not used anyway
  487. * InternalEllipseDefault: fix for when xradius or yradius = 0 and
  488. increase xradius and yradius always by one (TP does this too)
  489. * fixed conlict in vesa.inc from last update
  490. * some conditionals to avoid range check and overflow errors in
  491. places where it doesn't matter
  492. Revision 1.4 1999/07/12 14:52:52 jonas
  493. * fixed procvar syntax error and ceil and floor functions
  494. Revision 1.3 1999/07/12 13:27:11 jonas
  495. + added Log and Id tags
  496. * added first FPC support, only VGA works to some extend for now
  497. * use -dasmgraph to use assembler routines, otherwise Pascal
  498. equivalents are used
  499. * use -dsupportVESA to support VESA (crashes under FPC for now)
  500. * only dispose vesainfo at closegrph if a vesa card was detected
  501. * changed int32 to longint (int32 is not declared under FPC)
  502. * changed the declaration of almost every procedure in graph.inc to
  503. "far;" becquse otherwise you can't assign them to procvars under TP
  504. real mode (but unexplainable "data segnment too large" errors prevent
  505. it from working under real mode anyway)
  506. }