fills.inc 19 KB

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