fills.inc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  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.1 2000-07-13 06:30:50 michael
  443. + Initial import
  444. Revision 1.19 2000/05/23 20:34:26 pierre
  445. * avoid problems with Range Check
  446. Revision 1.18 2000/02/27 14:41:25 peter
  447. * removed warnings/notes
  448. Revision 1.17 2000/02/12 13:39:19 jonas
  449. + new, faster fillpoly from Thomas Schatzl
  450. * some logging commands in vesa.inc disabled
  451. Revision 1.16 2000/01/07 16:41:37 daniel
  452. * copyright 2000
  453. Revision 1.15 2000/01/07 16:32:25 daniel
  454. * copyright 2000 added
  455. Revision 1.14 2000/01/02 19:01:32 jonas
  456. * made floodfill a *LOT* faster (better DrawnPoints management)
  457. Revision 1.13 1999/12/20 11:22:36 peter
  458. * integer -> smallint to overcome -S2 switch needed for ggi version
  459. Revision 1.12 1999/12/11 23:41:38 jonas
  460. * changed definition of getscanlineproc to "getscanline(x1,x2,y:
  461. smallint; var data);" so it can be used by getimage too
  462. * changed getimage so it uses getscanline
  463. * changed floodfill, getscanline16 and definitions in Linux
  464. include files so they use this new format
  465. + getscanlineVESA256 for 256 color VESA modes (banked)
  466. Revision 1.11 1999/09/27 23:34:40 peter
  467. * new graph unit is default for go32v2
  468. * removed warnings/notes
  469. Revision 1.10 1999/09/24 22:52:38 jonas
  470. * optimized patternline a bit (always use hline when possible)
  471. * isgraphmode stuff cleanup
  472. * vesainfo.modelist now gets disposed in cleanmode instead of in
  473. closegraph (required moving of some declarations from vesa.inc to
  474. new vesah.inc)
  475. * queryadapter gets no longer called from initgraph (is called from
  476. initialization of graph unit)
  477. * bugfix for notput in 32k and 64k vesa modes
  478. * a div replaced by / in fillpoly
  479. Revision 1.9 1999/09/24 14:23:08 jonas
  480. * floodfill uses scanline data from previous loop if line is adjacent
  481. Revision 1.8 1999/09/18 22:21:09 jonas
  482. + hlinevesa256 and vlinevesa256
  483. + support for not/xor/or/andput in vesamodes with 32k/64k colors
  484. * lots of changes to avoid warnings under FPC
  485. Revision 1.7 1999/09/17 13:58:31 jonas
  486. * another fix for a case where internalellipsedefault went haywire
  487. * sector() and pieslice() fully implemented!
  488. * small change to prevent buffer overflow with floodfill
  489. Revision 1.6 1999/09/12 17:28:59 jonas
  490. * several changes to internalellipse to make it faster
  491. and to make sure it updates the ArcCall correctly
  492. (not yet done for width = 3)
  493. * Arc mostly works now, only sometimes an endless loop, don't know
  494. why
  495. Revision 1.5 1999/09/11 19:43:00 jonas
  496. * FloodFill: did not take into account current viewport settings
  497. * GetScanLine: only get line inside viewport, data outside of it
  498. is not used anyway
  499. * InternalEllipseDefault: fix for when xradius or yradius = 0 and
  500. increase xradius and yradius always by one (TP does this too)
  501. * fixed conlict in vesa.inc from last update
  502. * some conditionals to avoid range check and overflow errors in
  503. places where it doesn't matter
  504. Revision 1.4 1999/07/12 14:52:52 jonas
  505. * fixed procvar syntax error and ceil and floor functions
  506. Revision 1.3 1999/07/12 13:27:11 jonas
  507. + added Log and Id tags
  508. * added first FPC support, only VGA works to some extend for now
  509. * use -dasmgraph to use assembler routines, otherwise Pascal
  510. equivalents are used
  511. * use -dsupportVESA to support VESA (crashes under FPC for now)
  512. * only dispose vesainfo at closegrph if a vesa card was detected
  513. * changed int32 to longint (int32 is not declared under FPC)
  514. * changed the declaration of almost every procedure in graph.inc to
  515. "far;" becquse otherwise you can't assign them to procvars under TP
  516. real mode (but unexplainable "data segnment too large" errors prevent
  517. it from working under real mode anyway)
  518. }