fills.inc 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629
  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. {$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.16 2000-01-07 16:41:37 daniel
  500. * copyright 2000
  501. Revision 1.15 2000/01/07 16:32:25 daniel
  502. * copyright 2000 added
  503. Revision 1.14 2000/01/02 19:01:32 jonas
  504. * made floodfill a *LOT* faster (better DrawnPoints management)
  505. Revision 1.13 1999/12/20 11:22:36 peter
  506. * integer -> smallint to overcome -S2 switch needed for ggi version
  507. Revision 1.12 1999/12/11 23:41:38 jonas
  508. * changed definition of getscanlineproc to "getscanline(x1,x2,y:
  509. smallint; var data);" so it can be used by getimage too
  510. * changed getimage so it uses getscanline
  511. * changed floodfill, getscanline16 and definitions in Linux
  512. include files so they use this new format
  513. + getscanlineVESA256 for 256 color VESA modes (banked)
  514. Revision 1.11 1999/09/27 23:34:40 peter
  515. * new graph unit is default for go32v2
  516. * removed warnings/notes
  517. Revision 1.10 1999/09/24 22:52:38 jonas
  518. * optimized patternline a bit (always use hline when possible)
  519. * isgraphmode stuff cleanup
  520. * vesainfo.modelist now gets disposed in cleanmode instead of in
  521. closegraph (required moving of some declarations from vesa.inc to
  522. new vesah.inc)
  523. * queryadapter gets no longer called from initgraph (is called from
  524. initialization of graph unit)
  525. * bugfix for notput in 32k and 64k vesa modes
  526. * a div replaced by / in fillpoly
  527. Revision 1.9 1999/09/24 14:23:08 jonas
  528. * floodfill uses scanline data from previous loop if line is adjacent
  529. Revision 1.8 1999/09/18 22:21:09 jonas
  530. + hlinevesa256 and vlinevesa256
  531. + support for not/xor/or/andput in vesamodes with 32k/64k colors
  532. * lots of changes to avoid warnings under FPC
  533. Revision 1.7 1999/09/17 13:58:31 jonas
  534. * another fix for a case where internalellipsedefault went haywire
  535. * sector() and pieslice() fully implemented!
  536. * small change to prevent buffer overflow with floodfill
  537. Revision 1.6 1999/09/12 17:28:59 jonas
  538. * several changes to internalellipse to make it faster
  539. and to make sure it updates the ArcCall correctly
  540. (not yet done for width = 3)
  541. * Arc mostly works now, only sometimes an endless loop, don't know
  542. why
  543. Revision 1.5 1999/09/11 19:43:00 jonas
  544. * FloodFill: did not take into account current viewport settings
  545. * GetScanLine: only get line inside viewport, data outside of it
  546. is not used anyway
  547. * InternalEllipseDefault: fix for when xradius or yradius = 0 and
  548. increase xradius and yradius always by one (TP does this too)
  549. * fixed conlict in vesa.inc from last update
  550. * some conditionals to avoid range check and overflow errors in
  551. places where it doesn't matter
  552. Revision 1.4 1999/07/12 14:52:52 jonas
  553. * fixed procvar syntax error and ceil and floor functions
  554. Revision 1.3 1999/07/12 13:27:11 jonas
  555. + added Log and Id tags
  556. * added first FPC support, only VGA works to some extend for now
  557. * use -dasmgraph to use assembler routines, otherwise Pascal
  558. equivalents are used
  559. * use -dsupportVESA to support VESA (crashes under FPC for now)
  560. * only dispose vesainfo at closegrph if a vesa card was detected
  561. * changed int32 to longint (int32 is not declared under FPC)
  562. * changed the declaration of almost every procedure in graph.inc to
  563. "far;" becquse otherwise you can't assign them to procvars under TP
  564. real mode (but unexplainable "data segnment too large" errors prevent
  565. it from working under real mode anyway)
  566. }