fills.inc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  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 (x > 0) and (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 (x < 0) and (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; far;
  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; far;
  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) div (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. var
  268. i: integer;
  269. Begin
  270. If Buffer.WordIndex<(StdBufferSize DIV 2)-3 then
  271. Begin
  272. Buffer.Words[Buffer.WordIndex]:=x;
  273. Buffer.Words[Buffer.WordIndex+1]:=y;
  274. Inc (Buffer.WordIndex,2);
  275. End
  276. End;
  277. Procedure PopPoint (Var x, y : Integer);
  278. {********************************************************}
  279. { Removes a point from the list of points to check, if }
  280. { we try to access an illegal point, then the routine }
  281. { returns -1,-1 as a coordinate pair. }
  282. {********************************************************}
  283. Begin
  284. If Buffer.WordIndex>1 then
  285. Begin
  286. x:=Buffer.Words[Buffer.WordIndex-2];
  287. y:=Buffer.Words[Buffer.WordIndex-1];
  288. Dec (Buffer.WordIndex,2);
  289. End
  290. Else
  291. Begin
  292. x:=-1;
  293. y:=-1;
  294. End;
  295. End;
  296. {********************************************************}
  297. { Procedure AddLinePoints() }
  298. {--------------------------------------------------------}
  299. { Adds a line segment to the list of lines which will be }
  300. { drawn to the screen. The line added is on the specified}
  301. { Y axis, from the x1 to x2 coordinates. }
  302. {********************************************************}
  303. Procedure AddLinePoints(x1,x2,y: integer);
  304. begin
  305. DrawnList[DrawnIndex].x1 := x1;
  306. DrawnList[DrawnIndex].x2 := x2;
  307. DrawnList[DrawnIndex].y := y;
  308. Inc(DrawnIndex);
  309. end;
  310. {********************************************************}
  311. { Procedure AlreadyDrawn() }
  312. {--------------------------------------------------------}
  313. { This routine searches through the list of segments }
  314. { which will be drawn to the screen, and determines if }
  315. { the specified point (x,y) will already be drawn. }
  316. { i.e : Checks if the x,y point lies within a known }
  317. { segment which will be drawn to the screen. This makes }
  318. { sure that we don't draw some segments two times. }
  319. { Return TRUE if the point is already in the segment list}
  320. { to draw, otherwise returns FALSE. }
  321. {********************************************************}
  322. Function AlreadyDrawn(x, y: integer): boolean;
  323. var
  324. LocalIndex : integer;
  325. begin
  326. AlreadyDrawn := FALSE;
  327. LocalIndex := 0;
  328. while LocalIndex < DrawnIndex do
  329. Begin
  330. { if vertical val is equal to our y point ... }
  331. if DrawnList[LocalIndex].y = y then
  332. Begin
  333. { then check if x >< ... }
  334. if (x >= DrawnList[LocalIndex].x1) and
  335. (x <= DrawnList[LocalIndex].x2) then
  336. Begin
  337. AlreadyDrawn := TRUE;
  338. exit;
  339. end;
  340. end;
  341. Inc(LocalIndex);
  342. end;
  343. end;
  344. Procedure FloodFill (x, y : Integer; Border: word);
  345. {********************************************************}
  346. { Procedure FloodFill() }
  347. {--------------------------------------------------------}
  348. { This routine fills a region of the screen bounded by }
  349. { the <Border> color. It uses the current fillsettings }
  350. { for the flood filling. Clipping is supported, and }
  351. { coordinates are local/viewport relative. }
  352. {********************************************************}
  353. Var
  354. Beginx : Integer;
  355. d, e, a : Byte;
  356. Cont : Boolean;
  357. BackupColor : Word;
  358. x1, x2: integer;
  359. Index : Integer;
  360. Begin
  361. { Save current drawing color }
  362. BackupColor := CurrentColor;
  363. CurrentColor := FillSettings.Color;
  364. { MaxX is based on zero index }
  365. GetMem (s1,(ViewWidth+1)*2); { A pixel color represents a word }
  366. GetMem (s2,(ViewWidth+1)*2); { A pixel color represents a word }
  367. GetMem (s3,(ViewWidth+1)*2); { A pixel color represents a word }
  368. if (not assigned(s1)) or (not assigned(s2)) or (not assigned(s3)) then
  369. begin
  370. _GraphResult := grNoFloodMem;
  371. exit;
  372. end;
  373. If (x<0) Or (y<0) Or
  374. (x>ViewWidth) Or (y>ViewHeight) then Exit;
  375. { Some internal variables }
  376. Index := 0;
  377. { Index of segments to draw }
  378. DrawnIndex := 0;
  379. { Index of points to check }
  380. Buffer.WordIndex:=0;
  381. PushPoint (x,y);
  382. While Buffer.WordIndex>0 Do
  383. Begin
  384. PopPoint (x,y);
  385. { Get the complete lines for the following }
  386. GetScanline (y-1,s2^);
  387. GetScanline (y,s1^);
  388. GetScanline (y+1,s3^);
  389. { check the current scan line }
  390. While (s1^[x]<>Border) And (x<ViewWidth) Do Inc (x);
  391. d:=0;
  392. e:=0;
  393. dec(x);
  394. Beginx:=x;
  395. REPEAT
  396. { check the above line }
  397. If y<ViewHeight then
  398. Begin
  399. Cont:=(s3^[x]<>Border) and (not AlreadyDrawn(x,y+1));
  400. If (e=0) And Cont then
  401. Begin
  402. PushPoint (x,y+1);
  403. e:=1;
  404. End
  405. Else
  406. If (e=1) And Not Cont then e:=0;
  407. End;
  408. { check the line below }
  409. If (y>0) then
  410. Begin
  411. Cont:=(s2^[x]<>Border) and (not AlreadyDrawn(x,y-1));
  412. If (d=0) And Cont then
  413. Begin
  414. PushPoint (x,y-1);
  415. d:=1;
  416. End
  417. Else
  418. If (d=1) And Not Cont then d:=0;
  419. End;
  420. Dec (x);
  421. Until (x<0) Or (s1^[x]=Border);
  422. { swap the values }
  423. x1:=x+1;
  424. x2:=BeginX;
  425. if x1 > x2 then
  426. Begin
  427. x:=x1;
  428. x1:=x2;
  429. x2:=x;
  430. end;
  431. { Add to the list of drawn lines }
  432. AddLinePoints(x1,x2,y);
  433. PatternLine (x1,x2,y);
  434. End; { end while }
  435. FreeMem (s1,(ViewWidth+1)*2);
  436. FreeMem (s2,(ViewWidth+1)*2);
  437. FreeMem (s3,(ViewWidth+1)*2);
  438. CurrentColor := BackUpColor;
  439. End;
  440. {
  441. $Log$
  442. Revision 1.7 1999-09-17 13:58:31 jonas
  443. * another fix for a case where internalellipsedefault went haywire
  444. * sector() and pieslice() fully implemented!
  445. * small change to prevent buffer overflow with floodfill
  446. Revision 1.6 1999/09/12 17:28:59 jonas
  447. * several changes to internalellipse to make it faster
  448. and to make sure it updates the ArcCall correctly
  449. (not yet done for width = 3)
  450. * Arc mostly works now, only sometimes an endless loop, don't know
  451. why
  452. Revision 1.5 1999/09/11 19:43:00 jonas
  453. * FloodFill: did not take into account current viewport settings
  454. * GetScanLine: only get line inside viewport, data outside of it
  455. is not used anyway
  456. * InternalEllipseDefault: fix for when xradius or yradius = 0 and
  457. increase xradius and yradius always by one (TP does this too)
  458. * fixed conlict in vesa.inc from last update
  459. * some conditionals to avoid range check and overflow errors in
  460. places where it doesn't matter
  461. Revision 1.4 1999/07/12 14:52:52 jonas
  462. * fixed procvar syntax error and ceil and floor functions
  463. Revision 1.3 1999/07/12 13:27:11 jonas
  464. + added Log and Id tags
  465. * added first FPC support, only VGA works to some extend for now
  466. * use -dasmgraph to use assembler routines, otherwise Pascal
  467. equivalents are used
  468. * use -dsupportVESA to support VESA (crashes under FPC for now)
  469. * only dispose vesainfo at closegrph if a vesa card was detected
  470. * changed int32 to longint (int32 is not declared under FPC)
  471. * changed the declaration of almost every procedure in graph.inc to
  472. "far;" becquse otherwise you can't assign them to procvars under TP
  473. real mode (but unexplainable "data segnment too large" errors prevent
  474. it from working under real mode anyway)
  475. }