fills.inc 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. {
  2. $Id$
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 1998-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. {$R-} { No range checking here, because we do some special typecasts }
  13. type
  14. pedge = ^edge;
  15. edge = packed record { an edge structure }
  16. x, { current x-coordinate on the edge }
  17. dx : graph_float; { deltax of the edge }
  18. i : graph_int; { index to which points this edge belongs to
  19. always [i] and [i+1] }
  20. end;
  21. { used for typecasting because TP/BP is more strict here than FPC }
  22. pedgearray = ^edgearray;
  23. { 0..0 }
  24. edgearray = array[0..0] of edge;
  25. pint = ^graph_int;
  26. pintarray = ^intarray;
  27. { 0..0 }
  28. intarray = array[0..0] of graph_int;
  29. ppointtype = ^pointtype;
  30. ppointarray = ^pointarray;
  31. pointarray = array[0..0] of pointtype;
  32. { definition of the called compare routine for the sort process. Returns -1 if
  33. the two parameters should be swapped }
  34. type
  35. compareproc = function (a, b : pointer) : graph_int;
  36. { simple bubblesort, since it is expected that the edges themselves are not
  37. too mixed, it is fastest (?). Rather than sorting the active edge table
  38. this way, it is recommened to implement this using a linked list (not
  39. nearly as much memory is transfered then) }
  40. procedure bsort(p : pointer; number : smallint; sizeelem :
  41. smallint; c : compareproc);
  42. var i : graph_int;
  43. swap : boolean;
  44. temp : pointer;
  45. curp, nextp : pointer;
  46. begin
  47. getmem(temp, sizeelem);
  48. repeat
  49. curp := p;
  50. nextp := pointer(longint(p) + sizeelem);
  51. swap := false;
  52. for i := 0 to (number-2) do begin
  53. if (c(curp, nextp)=1) then begin
  54. { swap elements, you can't do it slower ;( }
  55. move(curp^, temp^, sizeelem);
  56. move(nextp^, curp^, sizeelem);
  57. move(temp^, nextp^, sizeelem);
  58. swap := true;
  59. end;
  60. inc(longint(curp), sizeelem);
  61. inc(longint(nextp), sizeelem);
  62. end;
  63. until swap = false;
  64. freemem(temp, sizeelem);
  65. end;
  66. { guess what this does }
  67. function ceil(x : graph_float) : graph_int;
  68. var t : graph_int;
  69. begin
  70. t:=Trunc(x);
  71. If (frac(x)>0) then inc(t);
  72. ceil := t;
  73. end;
  74. { guess what this does too }
  75. function floor(x : graph_float) : graph_int;
  76. var t : graph_int;
  77. begin
  78. t:=Trunc(x);
  79. If (frac(x)<0) then dec(t);
  80. floor := t;
  81. end;
  82. (*
  83. { simple descriptive name }
  84. function max(a, b : graph_int) : graph_int;
  85. begin
  86. if (a >= b) then max := a
  87. else max := b;
  88. end;
  89. { here too }
  90. function min(a, b : graph_int) : graph_int;
  91. begin
  92. if (a <= b) then min := a
  93. else min := b;
  94. end;
  95. *)
  96. { needed for the compare functions; should NOT be used for anything else }
  97. var
  98. ptable : ppointarray; { pointer to points list }
  99. function compare_ind(u, v : pointer) : graph_int; {$ifndef fpc} far; {$endif fpc}
  100. begin
  101. if (ptable^[pint(u)^].y <= ptable^[pint(v)^].y) then compare_ind := -1
  102. else compare_ind := 1;
  103. end;
  104. function compare_active(u, v : pointer) : graph_int; {$ifndef fpc} far; {$endif fpc}
  105. begin
  106. if (pedge(u)^.x <= pedge(v)^.x) then compare_active := -1
  107. else compare_active := 1;
  108. end;
  109. procedure fillpoly(numpoints : word; var PolyPoints);
  110. { variables needed within the helper procedures too }
  111. var
  112. activetable : pedgearray; { active edge table, e.g. edges crossing current scanline }
  113. activepoints : graph_int; { number of points in active edge table }
  114. { remove edge i from active edge table }
  115. procedure cdelete(index : graph_int);
  116. var
  117. j : graph_int;
  118. begin
  119. j := 0;
  120. while (j < activepoints) and (pedgearray(activetable)^[j].i <> index) do inc(j);
  121. if (j >= activepoints) then exit;
  122. dec(activepoints);
  123. move(pedgearray(activetable)^[j+1], pedgearray(activetable)^[j],
  124. (activepoints-j) * sizeof(edge));
  125. end;
  126. { insert edge index into active edge table (at the last position) }
  127. procedure cinsert(index, y : graph_int);
  128. var
  129. j : graph_int;
  130. deltax : graph_float;
  131. p, q : ppointtype;
  132. begin
  133. if (index < (numpoints-1)) then j := index + 1 else j := 0;
  134. if (ptable^[index].y < ptable^[j].y) then begin
  135. p := @ptable^[index];
  136. q := @ptable^[j];
  137. end else begin
  138. p := @ptable^[j];
  139. q := @ptable^[index];
  140. end;
  141. deltax := (q^.x-p^.x) / (q^.y-p^.y);
  142. with activetable^[activepoints] do begin
  143. dx := deltax;
  144. x := dx * (y { + 0.5} - p^.y) + p^.x;
  145. i := index;
  146. end;
  147. inc(activepoints);
  148. end;
  149. { variables for the main procedure }
  150. var
  151. k, i, j : graph_int;
  152. starty, endy, y, xl, xr : graph_int;
  153. oldcolor : word;
  154. var
  155. indextable : pintarray; { list of vertex indices, sorted by y }
  156. begin
  157. oldcolor := CurrentColor;
  158. CurrentColor := FillSettings.Color;
  159. ptable := @PolyPoints;
  160. if (numpoints<=0) then exit;
  161. getmem(indextable, sizeof(graph_int) * numpoints);
  162. getmem(activetable, sizeof(edge) * numpoints);
  163. if (not assigned(activetable)) or (not assigned(indextable)) then
  164. begin
  165. _GraphResult := grNoScanMem;
  166. exit;
  167. end;
  168. {$R-}
  169. { create y-sorted array of indices indextable[k] into vertex list }
  170. for k := 0 to (numpoints-1) do
  171. indextable^[k] := k;
  172. { sort the indextable by points[indextable[k]].y }
  173. {$ifndef fpc}
  174. bsort(indextable, numpoints, sizeof(graph_int), compare_ind);
  175. {$else fpc}
  176. bsort(indextable, numpoints, sizeof(graph_int), @compare_ind);
  177. {$endif fpc}
  178. { start with empty active edge table }
  179. activepoints := 0;
  180. { indextable[k] is the next vertex to process }
  181. k := 0;
  182. { ymin of polygon }
  183. starty := ceil(pointarray(polypoints)[indextable^[0]].y-0.5);
  184. { ymax of polygon }
  185. endy := floor(pointarray(polypoints)[indextable^[numpoints-1]].y-0.5);
  186. { step through scanlines }
  187. for y := starty to endy do begin
  188. { check vertices between previous scanline and current one, if any }
  189. while (k < numpoints) and
  190. (pointarray(polypoints)[indextable^[k]].y<=(y+0.5)) do begin
  191. i := indextable^[k];
  192. { insert or delete edges before and after points[i] ((i-1) to i and
  193. i to (i+1)) from active edge table if they cross scanline y }
  194. { point previous to i }
  195. if (i > 0) then j := i-1 else j := numpoints-1;
  196. { old edge, remove from list }
  197. if (pointarray(polypoints)[j].y <= (y-0.5)) then cdelete(j)
  198. { new edge, add to active edges }
  199. else if (pointarray(polypoints)[j].y > (y + 0.5)) then cinsert(j, y);
  200. { point next after i }
  201. if (i < (numpoints-1)) then j := i+1 else j := 0;
  202. { old edge, remove from active edge table }
  203. if (pointarray(polypoints)[j].y <= (y - 0.5)) then cdelete(i)
  204. { new edge, add to active edges }
  205. else if (pointarray(polypoints)[j].y > (y + 0.5)) then cinsert(i, y);
  206. inc(k);
  207. end;
  208. { sort active edges list by active[j].x }
  209. {$ifndef fpc}
  210. bsort(activetable, activepoints, sizeof(edge), compare_active);
  211. {$else fpc}
  212. bsort(activetable, activepoints, sizeof(edge),@compare_active);
  213. {$endif fpc}
  214. j := 0;
  215. { draw horizontal segments for scanline y }
  216. while (j < activepoints) do begin
  217. {xl := ceil(activetable^[j].x-0.5);}
  218. xl := trunc(activetable^[j].x-0.5);
  219. if frac(activetable^[j].x-0.5)>0 then inc(xl);
  220. xr := trunc(activetable^[j+1].x-0.5);
  221. if frac(activetable^[j+1].x-0.5)<0 then dec(xr);
  222. if (xl < xr) then
  223. PatternLine(xl,xr,y);
  224. { line(xl, y, xr+1, y);}
  225. { increment both edges' coordinates }
  226. with activetable^[j] do begin
  227. x := x + dx;
  228. end;
  229. with activetable^[j+1] do begin
  230. x := x + dx;
  231. end;
  232. inc(j, 2);
  233. end;
  234. end;
  235. {$ifdef debug}
  236. {$R+,Q+}
  237. {$endif debug}
  238. freemem(activetable, sizeof(edge) * numpoints);
  239. freemem(indextable, sizeof(graph_int) * numpoints);
  240. { restore the old color }
  241. CurrentColor := OldColor;
  242. { now let's draw the outline of this polygon }
  243. DrawPoly(NumPoints, PolyPoints);
  244. end;
  245. { maximum supported Y resultion }
  246. const
  247. MaxYRes = 2048;
  248. { changing this to 1 or 2 doesn't improve performance noticably }
  249. YResDiv = 4;
  250. type
  251. PFloodLine = ^TFloodLine;
  252. TFloodLine = record
  253. next: PFloodLine;
  254. x1 : smallint;
  255. x2 : smallint;
  256. y : smallint;
  257. end;
  258. TDrawnList = Array[0..(MaxYRes - 1) div 4] of PFloodLine;
  259. var
  260. DrawnList : TDrawnList;
  261. Buffer : Record { Union for byte and word addressing of buffer }
  262. ByteIndex : Word;
  263. WordIndex : Word;
  264. Case Boolean Of
  265. False : (Bytes : Array [0..StdBufferSize-1] Of Byte);
  266. True : (Words : Array [0..(StdBufferSize DIV 2)-1] Of Word);
  267. End;
  268. s1, s2, s3 : PWordArray; { Three buffers for scanlines }
  269. Procedure PushPoint (x, y : smallint);
  270. {********************************************************}
  271. { Adds a point to the list of points to check if we }
  272. { need to draw. Doesn't add the point if there is a }
  273. { buffer overflow. }
  274. {********************************************************}
  275. Begin
  276. If Buffer.WordIndex<(StdBufferSize DIV 2)-3 then
  277. Begin
  278. Buffer.Words[Buffer.WordIndex]:=x;
  279. Buffer.Words[Buffer.WordIndex+1]:=y;
  280. Inc (Buffer.WordIndex,2);
  281. End
  282. End;
  283. Procedure PopPoint (Var x, y : smallint);
  284. {********************************************************}
  285. { Removes a point from the list of points to check, if }
  286. { we try to access an illegal point, then the routine }
  287. { returns -1,-1 as a coordinate pair. }
  288. {********************************************************}
  289. Begin
  290. If Buffer.WordIndex>1 then
  291. Begin
  292. x:=Buffer.Words[Buffer.WordIndex-2];
  293. y:=Buffer.Words[Buffer.WordIndex-1];
  294. Dec (Buffer.WordIndex,2);
  295. End
  296. Else
  297. Begin
  298. x:=-1;
  299. y:=-1;
  300. End;
  301. End;
  302. {********************************************************}
  303. { Procedure AddLinePoints() }
  304. {--------------------------------------------------------}
  305. { Adds a line segment to the list of lines which will be }
  306. { drawn to the screen. The line added is on the specified}
  307. { Y axis, from the x1 to x2 coordinates. }
  308. {********************************************************}
  309. Procedure AddLinePoints(x1,x2,y: smallint);
  310. var temp: PFloodLine;
  311. begin
  312. new(temp);
  313. temp^.x1 := x1;
  314. temp^.x2 := x2;
  315. temp^.y := y;
  316. temp^.next := DrawnList[y div YResDiv];
  317. DrawnList[y div YResDiv] := temp;
  318. end;
  319. {********************************************************}
  320. { Procedure AlreadyDrawn() }
  321. {--------------------------------------------------------}
  322. { This routine searches through the list of segments }
  323. { which will be drawn to the screen, and determines if }
  324. { the specified point (x,y) will already be drawn. }
  325. { i.e : Checks if the x,y point lies within a known }
  326. { segment which will be drawn to the screen. This makes }
  327. { sure that we don't draw some segments two times. }
  328. { Return TRUE if the point is already in the segment list}
  329. { to draw, otherwise returns FALSE. }
  330. {********************************************************}
  331. Function AlreadyDrawn(x, y: smallint): boolean;
  332. var
  333. temp : PFloodLine;
  334. begin
  335. AlreadyDrawn := false;
  336. temp := DrawnList[y div YResDiv];
  337. while assigned(temp) do
  338. begin
  339. if (temp^.y = y) and
  340. (temp^.x1 <= x) and
  341. (temp^.x2 >= x) then
  342. begin
  343. AlreadyDrawn := true;
  344. exit;
  345. end;
  346. temp := temp^.next;
  347. end;
  348. end;
  349. {********************************************************}
  350. { Procedure CleanUpDrawnList }
  351. {--------------------------------------------------------}
  352. { removes all elements from the DrawnList. Doesn't init }
  353. { elements of it with NILL }
  354. {********************************************************}
  355. Procedure CleanUpDrawnList;
  356. var
  357. l: longint;
  358. temp1, temp2: PFloodLine;
  359. begin
  360. for l := 0 to high(DrawnList) do
  361. begin
  362. temp1 := DrawnList[l];
  363. while assigned(temp1) do
  364. begin
  365. temp2 := temp1;
  366. temp1 := temp1^.next;
  367. dispose(temp2);
  368. end;
  369. end;
  370. end;
  371. Procedure FloodFill (x, y : smallint; Border: word);
  372. {********************************************************}
  373. { Procedure FloodFill() }
  374. {--------------------------------------------------------}
  375. { This routine fills a region of the screen bounded by }
  376. { the <Border> color. It uses the current fillsettings }
  377. { for the flood filling. Clipping is supported, and }
  378. { coordinates are local/viewport relative. }
  379. {********************************************************}
  380. Var
  381. stemp: PWordArray;
  382. Beginx : smallint;
  383. d, e : Byte;
  384. Cont : Boolean;
  385. BackupColor : Word;
  386. x1, x2, prevy: smallint;
  387. Index : smallint;
  388. Begin
  389. FillChar(DrawnList,sizeof(DrawnList),0);
  390. { init prevy }
  391. prevy := 32767;
  392. { Save current drawing color }
  393. BackupColor := CurrentColor;
  394. CurrentColor := FillSettings.Color;
  395. { MaxX is based on zero index }
  396. GetMem (s1,(ViewWidth+1)*2); { A pixel color represents a word }
  397. GetMem (s2,(ViewWidth+1)*2); { A pixel color represents a word }
  398. GetMem (s3,(ViewWidth+1)*2); { A pixel color represents a word }
  399. if (not assigned(s1)) or (not assigned(s2)) or (not assigned(s3)) then
  400. begin
  401. _GraphResult := grNoFloodMem;
  402. exit;
  403. end;
  404. If (x<0) Or (y<0) Or
  405. (x>ViewWidth) Or (y>ViewHeight) then Exit;
  406. { Some internal variables }
  407. Index := 0;
  408. { Index of points to check }
  409. Buffer.WordIndex:=0;
  410. PushPoint (x,y);
  411. While Buffer.WordIndex>0 Do
  412. Begin
  413. PopPoint (x,y);
  414. { Get the complete lines for the following }
  415. If y <> prevy then
  416. begin
  417. If (prevy - y = 1) then
  418. { previous line was one below the new one, so the previous s2 }
  419. { = new s1 }
  420. Begin
  421. stemp := s3;
  422. s3 := s1;
  423. s1 := s2;
  424. s2 := stemp;
  425. GetScanline(0,ViewWidth,y-1,s2^);
  426. End
  427. Else If (y - prevy = 1) then
  428. { previous line was one above the new one, so the previous s3 }
  429. { = new s1 }
  430. Begin
  431. stemp := s2;
  432. s2 := s1;
  433. s1 := s3;
  434. s3 := stemp;
  435. GetScanline(0,ViewWidth,y+1,s3^);
  436. End
  437. Else
  438. begin
  439. GetScanline(0,ViewWidth,y-1,s2^);
  440. GetScanline(0,ViewWidth,y,s1^);
  441. GetScanline(0,ViewWidth,y+1,s3^);
  442. end;
  443. end;
  444. prevy := y;
  445. { check the current scan line }
  446. While (s1^[x]<>Border) And (x<=ViewWidth) Do Inc (x);
  447. d:=0;
  448. e:=0;
  449. dec(x);
  450. Beginx:=x;
  451. REPEAT
  452. { check the above line }
  453. If y<ViewHeight then
  454. Begin
  455. Cont:=(s3^[x]<>Border) and (not AlreadyDrawn(x,y+1));
  456. If (e=0) And Cont then
  457. Begin
  458. PushPoint (x,y+1);
  459. e:=1;
  460. End
  461. Else
  462. If (e=1) And Not Cont then e:=0;
  463. End;
  464. { check the line below }
  465. If (y>0) then
  466. Begin
  467. Cont:=(s2^[x]<>Border) and (not AlreadyDrawn(x,y-1));
  468. If (d=0) And Cont then
  469. Begin
  470. PushPoint (x,y-1);
  471. d:=1;
  472. End
  473. Else
  474. If (d=1) And Not Cont then d:=0;
  475. End;
  476. Dec (x);
  477. Until (x<0) Or (s1^[x]=Border);
  478. { swap the values }
  479. x1:=x+1;
  480. x2:=BeginX;
  481. if x1 > x2 then
  482. Begin
  483. x:=x1;
  484. x1:=x2;
  485. x2:=x;
  486. end;
  487. { Add to the list of drawn lines }
  488. AddLinePoints(x1,x2,y);
  489. PatternLine (x1,x2,y);
  490. End; { end while }
  491. FreeMem (s1,(ViewWidth+1)*2);
  492. FreeMem (s2,(ViewWidth+1)*2);
  493. FreeMem (s3,(ViewWidth+1)*2);
  494. CleanUpDrawnList;
  495. CurrentColor := BackUpColor;
  496. End;
  497. {
  498. $Log$
  499. Revision 1.15 2000-01-07 16:32:25 daniel
  500. * copyright 2000 added
  501. Revision 1.14 2000/01/02 19:01:32 jonas
  502. * made floodfill a *LOT* faster (better DrawnPoints management)
  503. Revision 1.13 1999/12/20 11:22:36 peter
  504. * integer -> smallint to overcome -S2 switch needed for ggi version
  505. Revision 1.12 1999/12/11 23:41:38 jonas
  506. * changed definition of getscanlineproc to "getscanline(x1,x2,y:
  507. smallint; var data);" so it can be used by getimage too
  508. * changed getimage so it uses getscanline
  509. * changed floodfill, getscanline16 and definitions in Linux
  510. include files so they use this new format
  511. + getscanlineVESA256 for 256 color VESA modes (banked)
  512. Revision 1.11 1999/09/27 23:34:40 peter
  513. * new graph unit is default for go32v2
  514. * removed warnings/notes
  515. Revision 1.10 1999/09/24 22:52:38 jonas
  516. * optimized patternline a bit (always use hline when possible)
  517. * isgraphmode stuff cleanup
  518. * vesainfo.modelist now gets disposed in cleanmode instead of in
  519. closegraph (required moving of some declarations from vesa.inc to
  520. new vesah.inc)
  521. * queryadapter gets no longer called from initgraph (is called from
  522. initialization of graph unit)
  523. * bugfix for notput in 32k and 64k vesa modes
  524. * a div replaced by / in fillpoly
  525. Revision 1.9 1999/09/24 14:23:08 jonas
  526. * floodfill uses scanline data from previous loop if line is adjacent
  527. Revision 1.8 1999/09/18 22:21:09 jonas
  528. + hlinevesa256 and vlinevesa256
  529. + support for not/xor/or/andput in vesamodes with 32k/64k colors
  530. * lots of changes to avoid warnings under FPC
  531. Revision 1.7 1999/09/17 13:58:31 jonas
  532. * another fix for a case where internalellipsedefault went haywire
  533. * sector() and pieslice() fully implemented!
  534. * small change to prevent buffer overflow with floodfill
  535. Revision 1.6 1999/09/12 17:28:59 jonas
  536. * several changes to internalellipse to make it faster
  537. and to make sure it updates the ArcCall correctly
  538. (not yet done for width = 3)
  539. * Arc mostly works now, only sometimes an endless loop, don't know
  540. why
  541. Revision 1.5 1999/09/11 19:43:00 jonas
  542. * FloodFill: did not take into account current viewport settings
  543. * GetScanLine: only get line inside viewport, data outside of it
  544. is not used anyway
  545. * InternalEllipseDefault: fix for when xradius or yradius = 0 and
  546. increase xradius and yradius always by one (TP does this too)
  547. * fixed conlict in vesa.inc from last update
  548. * some conditionals to avoid range check and overflow errors in
  549. places where it doesn't matter
  550. Revision 1.4 1999/07/12 14:52:52 jonas
  551. * fixed procvar syntax error and ceil and floor functions
  552. Revision 1.3 1999/07/12 13:27:11 jonas
  553. + added Log and Id tags
  554. * added first FPC support, only VGA works to some extend for now
  555. * use -dasmgraph to use assembler routines, otherwise Pascal
  556. equivalents are used
  557. * use -dsupportVESA to support VESA (crashes under FPC for now)
  558. * only dispose vesainfo at closegrph if a vesa card was detected
  559. * changed int32 to longint (int32 is not declared under FPC)
  560. * changed the declaration of almost every procedure in graph.inc to
  561. "far;" becquse otherwise you can't assign them to procvars under TP
  562. real mode (but unexplainable "data segnment too large" errors prevent
  563. it from working under real mode anyway)
  564. }