fills.inc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 1993,99 by Thomas Schatzl and Carl Eric Codere
  4. This include implements polygon filling and flood filling.
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$R-} { No range checking here, because we do some special typecasts }
  12. type
  13. {$IFDEF FPC}
  14. graph_int = int32; { platform specific integer used for indexes;
  15. should be 16 bits on TP/BP and 32 bits on every-
  16. thing else for speed reasons }
  17. graph_float = double; { the platform's preferred floating point size }
  18. {$ELSE}
  19. graph_int = integer; { platform specific integer used for indexes;
  20. should be 16 bits on TP/BP and 32 bits on every-
  21. thing else for speed reasons }
  22. graph_float = real; { the platform's preferred floating point size }
  23. {$ENDIF}
  24. pedge = ^edge;
  25. edge = packed record { an edge structure }
  26. x, { current x-coordinate on the edge }
  27. dx : graph_float; { deltax of the edge }
  28. i : graph_int; { index to which points this edge belongs to
  29. always [i] and [i+1] }
  30. end;
  31. { used for typecasting because TP/BP is more strict here than FPC }
  32. pedgearray = ^edgearray;
  33. { 0..0 }
  34. edgearray = array[0..0] of edge;
  35. pint = ^graph_int;
  36. pintarray = ^intarray;
  37. { 0..0 }
  38. intarray = array[0..0] of graph_int;
  39. ppointtype = ^pointtype;
  40. ppointarray = ^pointarray;
  41. pointarray = array[0..0] of pointtype;
  42. { definition of the called compare routine for the sort process. Returns -1 if
  43. the two parameters should be swapped }
  44. type
  45. compareproc = function (a, b : pointer) : graph_int;
  46. { simple bubblesort, since it is expected that the edges themselves are not
  47. too mixed, it is fastest (?). Rather than sorting the active edge table
  48. this way, it is recommened to implement this using a linked list (not
  49. nearly as much memory is transfered then) }
  50. procedure bsort(p : pointer; number : integer; sizeelem :
  51. integer; c : compareproc);
  52. var i : graph_int;
  53. swap : boolean;
  54. temp : pointer;
  55. curp, nextp : pointer;
  56. begin
  57. getmem(temp, sizeelem);
  58. repeat
  59. curp := p;
  60. nextp := pointer(longint(p) + sizeelem);
  61. swap := false;
  62. for i := 0 to (number-2) do begin
  63. if (c(curp, nextp)=1) then begin
  64. { swap elements, you can't do it slower ;( }
  65. move(curp^, temp^, sizeelem);
  66. move(nextp^, curp^, sizeelem);
  67. move(temp^, nextp^, sizeelem);
  68. swap := true;
  69. end;
  70. inc(longint(curp), sizeelem);
  71. inc(longint(nextp), sizeelem);
  72. end;
  73. until swap = false;
  74. freemem(temp, sizeelem);
  75. end;
  76. { guess what this does }
  77. function ceil(x : graph_float) : graph_int;
  78. var t : graph_int;
  79. begin
  80. t:=Trunc(x);
  81. If frac(x)>0 then inc(t);
  82. ceil := t;
  83. end;
  84. { guess what this does too }
  85. function floor(x : graph_float) : graph_int;
  86. var t : graph_int;
  87. begin
  88. t:=Trunc(x);
  89. If frac(x)<0 then dec(t);
  90. floor := t;
  91. end;
  92. { simple descriptive name }
  93. function max(a, b : graph_int) : graph_int;
  94. begin
  95. if (a > b) then max := a
  96. else max := b;
  97. end;
  98. { here too }
  99. function min(a, b : graph_int) : graph_int;
  100. begin
  101. if (a < b) then min := a
  102. else min := b;
  103. end;
  104. { needed for the compare functions; should NOT be used for anything else }
  105. var
  106. ptable : ppointarray; { pointer to points list }
  107. function compare_ind(u, v : pointer) : graph_int; far;
  108. begin
  109. if (ptable^[pint(u)^].y <= ptable^[pint(v)^].y) then compare_ind := -1
  110. else compare_ind := 1;
  111. end;
  112. function compare_active(u, v : pointer) : graph_int; far;
  113. begin
  114. if (pedge(u)^.x <= pedge(v)^.x) then compare_active := -1
  115. else compare_active := 1;
  116. end;
  117. procedure fillpoly(numpoints : word; var PolyPoints);
  118. { variables needed within the helper procedures too }
  119. var
  120. activetable : pedgearray; { active edge table, e.g. edges crossing current scanline }
  121. activepoints : graph_int; { number of points in active edge table }
  122. { remove edge i from active edge table }
  123. procedure cdelete(index : graph_int);
  124. var
  125. j : graph_int;
  126. begin
  127. j := 0;
  128. while (j < activepoints) and (pedgearray(activetable)^[j].i <> index) do inc(j);
  129. if (j >= activepoints) then exit;
  130. dec(activepoints);
  131. move(pedgearray(activetable)^[j+1], pedgearray(activetable)^[j],
  132. (activepoints-j) * sizeof(edge));
  133. end;
  134. { insert edge index into active edge table (at the last position) }
  135. procedure cinsert(index, y : graph_int);
  136. var
  137. j : graph_int;
  138. deltax : graph_float;
  139. p, q : ppointtype;
  140. begin
  141. if (index < (numpoints-1)) then j := index + 1 else j := 0;
  142. if (ptable^[index].y < ptable^[j].y) then begin
  143. p := @ptable^[index];
  144. q := @ptable^[j];
  145. end else begin
  146. p := @ptable^[j];
  147. q := @ptable^[index];
  148. end;
  149. deltax := (q^.x-p^.x)/(q^.y-p^.y);
  150. with activetable^[activepoints] do begin
  151. dx := deltax;
  152. x := dx * (y { + 0.5} - p^.y) + p^.x;
  153. i := index;
  154. end;
  155. inc(activepoints);
  156. end;
  157. { variables for the main procedure }
  158. var
  159. k, i, j : graph_int;
  160. starty, endy, y, xl, xr : graph_int;
  161. oldcolor : word;
  162. var
  163. indextable : pintarray; { list of vertex indices, sorted by y }
  164. begin
  165. oldcolor := CurrentColor;
  166. CurrentColor := FillSettings.Color;
  167. ptable := @PolyPoints;
  168. if (numpoints<=0) then exit;
  169. getmem(indextable, sizeof(graph_int) * numpoints);
  170. getmem(activetable, sizeof(edge) * numpoints);
  171. if (not assigned(activetable)) or (not assigned(indextable)) then
  172. begin
  173. _GraphResult := grNoScanMem;
  174. exit;
  175. end;
  176. {$R-}
  177. { create y-sorted array of indices indextable[k] into vertex list }
  178. for k := 0 to (numpoints-1) do
  179. indextable^[k] := k;
  180. { sort the indextable by points[indextable[k]].y }
  181. bsort(indextable, numpoints, sizeof(graph_int), compare_ind);
  182. { start with empty active edge table }
  183. activepoints := 0;
  184. { indextable[k] is the next vertex to process }
  185. k := 0;
  186. { ymin of polygon }
  187. starty := ceil(pointarray(polypoints)[indextable^[0]].y-0.5);
  188. { ymax of polygon }
  189. endy := floor(pointarray(polypoints)[indextable^[numpoints-1]].y-0.5);
  190. { step through scanlines }
  191. for y := starty to endy do begin
  192. { check vertices between previous scanline and current one, if any }
  193. while (k < numpoints) and
  194. (pointarray(polypoints)[indextable^[k]].y<=(y+0.5)) do begin
  195. i := indextable^[k];
  196. { insert or delete edges before and after points[i] ((i-1) to i and
  197. i to (i+1)) from active edge table if they cross scanline y }
  198. { point previous to i }
  199. if (i > 0) then j := i-1 else j := numpoints-1;
  200. { old edge, remove from list }
  201. if (pointarray(polypoints)[j].y <= (y-0.5)) then cdelete(j)
  202. { new edge, add to active edges }
  203. else if (pointarray(polypoints)[j].y > (y + 0.5)) then cinsert(j, y);
  204. { point next after i }
  205. if (i < (numpoints-1)) then j := i+1 else j := 0;
  206. { old edge, remove from active edge table }
  207. if (pointarray(polypoints)[j].y <= (y - 0.5)) then cdelete(i)
  208. { new edge, add to active edges }
  209. else if (pointarray(polypoints)[j].y > (y + 0.5)) then cinsert(i, y);
  210. inc(k);
  211. end;
  212. { sort active edges list by active[j].x }
  213. bsort(activetable, activepoints, sizeof(edge), compare_active);
  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. {$R+}
  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) 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,(MaxX+1)*2); { A pixel color represents a word }
  366. GetMem (s2,(MaxX+1)*2); { A pixel color represents a word }
  367. GetMem (s3,(MaxX+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 (x>MaxX) Or (y>MaxY) then Exit;
  374. { Some internal variables }
  375. Index := 0;
  376. { Index of segments to draw }
  377. DrawnIndex := 0;
  378. { Index of points to check }
  379. Buffer.WordIndex:=0;
  380. PushPoint (x,y);
  381. While Buffer.WordIndex>0 Do
  382. Begin
  383. PopPoint (x,y);
  384. { Get the complete lines for the following }
  385. GetScanline (y-1,s2^);
  386. GetScanline (y,s1^);
  387. GetScanline (y+1,s3^);
  388. { check the current scan line }
  389. While (s1^[x]<>Border) And (x<=MaxX) Do Inc (x);
  390. d:=0;
  391. e:=0;
  392. Dec (x);
  393. Beginx:=x;
  394. REPEAT
  395. { check the above line }
  396. If y<MaxY then
  397. Begin
  398. Cont:=(s3^[x]<>Border) and (not AlreadyDrawn(x,y+1));
  399. If (e=0) And Cont then
  400. Begin
  401. PushPoint (x,y+1);
  402. e:=1;
  403. End
  404. Else
  405. If (e=1) And Not Cont then e:=0;
  406. End;
  407. { check the line below }
  408. If (y>0) then
  409. Begin
  410. Cont:=(s2^[x]<>Border) and (not AlreadyDrawn(x,y-1));
  411. If (d=0) And Cont then
  412. Begin
  413. PushPoint (x,y-1);
  414. d:=1;
  415. End
  416. Else
  417. If (d=1) And Not Cont then d:=0;
  418. End;
  419. Dec (x);
  420. Until (x<0) Or (s1^[x]=Border);
  421. { swap the values }
  422. x1:=x+1;
  423. x2:=BeginX;
  424. if x1 > x2 then
  425. Begin
  426. x:=x1;
  427. x1:=x2;
  428. x2:=x;
  429. end;
  430. { Add to the list of drawn lines }
  431. AddLinePoints(x1,x2,y);
  432. PatternLine (x1,x2,y);
  433. End; { end while }
  434. FreeMem (s1,(MaxX+1)*2);
  435. FreeMem (s2,(MaxX+1)*2);
  436. FreeMem (s3,(MaxX+1)*2);
  437. CurrentColor := BackUpColor;
  438. End;