fills.inc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  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. {$IFDEF FPC}
  15. graph_int = longint; { platform specific integer used for indexes;
  16. should be 16 bits on TP/BP and 32 bits on every-
  17. thing else for speed reasons }
  18. graph_float = double; { the platform's preferred floating point size }
  19. {$ELSE}
  20. graph_int = integer; { platform specific integer used for indexes;
  21. should be 16 bits on TP/BP and 32 bits on every-
  22. thing else for speed reasons }
  23. graph_float = real; { the platform's preferred floating point size }
  24. {$ENDIF}
  25. pedge = ^edge;
  26. edge = packed record { an edge structure }
  27. x, { current x-coordinate on the edge }
  28. dx : graph_float; { deltax of the edge }
  29. i : graph_int; { index to which points this edge belongs to
  30. always [i] and [i+1] }
  31. end;
  32. { used for typecasting because TP/BP is more strict here than FPC }
  33. pedgearray = ^edgearray;
  34. { 0..0 }
  35. edgearray = array[0..0] of edge;
  36. pint = ^graph_int;
  37. pintarray = ^intarray;
  38. { 0..0 }
  39. intarray = array[0..0] of graph_int;
  40. ppointtype = ^pointtype;
  41. ppointarray = ^pointarray;
  42. pointarray = array[0..0] of pointtype;
  43. { definition of the called compare routine for the sort process. Returns -1 if
  44. the two parameters should be swapped }
  45. type
  46. compareproc = function (a, b : pointer) : graph_int;
  47. { simple bubblesort, since it is expected that the edges themselves are not
  48. too mixed, it is fastest (?). Rather than sorting the active edge table
  49. this way, it is recommened to implement this using a linked list (not
  50. nearly as much memory is transfered then) }
  51. procedure bsort(p : pointer; number : integer; sizeelem :
  52. integer; c : compareproc);
  53. var i : graph_int;
  54. swap : boolean;
  55. temp : pointer;
  56. curp, nextp : pointer;
  57. begin
  58. getmem(temp, sizeelem);
  59. repeat
  60. curp := p;
  61. nextp := pointer(longint(p) + sizeelem);
  62. swap := false;
  63. for i := 0 to (number-2) do begin
  64. if (c(curp, nextp)=1) then begin
  65. { swap elements, you can't do it slower ;( }
  66. move(curp^, temp^, sizeelem);
  67. move(nextp^, curp^, sizeelem);
  68. move(temp^, nextp^, sizeelem);
  69. swap := true;
  70. end;
  71. inc(longint(curp), sizeelem);
  72. inc(longint(nextp), sizeelem);
  73. end;
  74. until swap = false;
  75. freemem(temp, sizeelem);
  76. end;
  77. { guess what this does }
  78. function ceil(x : graph_float) : graph_int;
  79. var t : graph_int;
  80. begin
  81. t:=Trunc(x);
  82. If (x > 0) and (frac(x)>0) then inc(t);
  83. ceil := t;
  84. end;
  85. { guess what this does too }
  86. function floor(x : graph_float) : graph_int;
  87. var t : graph_int;
  88. begin
  89. t:=Trunc(x);
  90. If (x < 0) and (frac(x)>0) then dec(t);
  91. floor := t;
  92. end;
  93. { simple descriptive name }
  94. function max(a, b : graph_int) : graph_int;
  95. begin
  96. if (a > b) then max := a
  97. else max := b;
  98. end;
  99. { here too }
  100. function min(a, b : graph_int) : graph_int;
  101. begin
  102. if (a < b) then min := a
  103. else min := b;
  104. end;
  105. { needed for the compare functions; should NOT be used for anything else }
  106. var
  107. ptable : ppointarray; { pointer to points list }
  108. function compare_ind(u, v : pointer) : graph_int; far;
  109. begin
  110. if (ptable^[pint(u)^].y <= ptable^[pint(v)^].y) then compare_ind := -1
  111. else compare_ind := 1;
  112. end;
  113. function compare_active(u, v : pointer) : graph_int; far;
  114. begin
  115. if (pedge(u)^.x <= pedge(v)^.x) then compare_active := -1
  116. else compare_active := 1;
  117. end;
  118. procedure fillpoly(numpoints : word; var PolyPoints);
  119. { variables needed within the helper procedures too }
  120. var
  121. activetable : pedgearray; { active edge table, e.g. edges crossing current scanline }
  122. activepoints : graph_int; { number of points in active edge table }
  123. { remove edge i from active edge table }
  124. procedure cdelete(index : graph_int);
  125. var
  126. j : graph_int;
  127. begin
  128. j := 0;
  129. while (j < activepoints) and (pedgearray(activetable)^[j].i <> index) do inc(j);
  130. if (j >= activepoints) then exit;
  131. dec(activepoints);
  132. move(pedgearray(activetable)^[j+1], pedgearray(activetable)^[j],
  133. (activepoints-j) * sizeof(edge));
  134. end;
  135. { insert edge index into active edge table (at the last position) }
  136. procedure cinsert(index, y : graph_int);
  137. var
  138. j : graph_int;
  139. deltax : graph_float;
  140. p, q : ppointtype;
  141. begin
  142. if (index < (numpoints-1)) then j := index + 1 else j := 0;
  143. if (ptable^[index].y < ptable^[j].y) then begin
  144. p := @ptable^[index];
  145. q := @ptable^[j];
  146. end else begin
  147. p := @ptable^[j];
  148. q := @ptable^[index];
  149. end;
  150. deltax := (q^.x-p^.x)/(q^.y-p^.y);
  151. with activetable^[activepoints] do begin
  152. dx := deltax;
  153. x := dx * (y { + 0.5} - p^.y) + p^.x;
  154. i := index;
  155. end;
  156. inc(activepoints);
  157. end;
  158. { variables for the main procedure }
  159. var
  160. k, i, j : graph_int;
  161. starty, endy, y, xl, xr : graph_int;
  162. oldcolor : word;
  163. var
  164. indextable : pintarray; { list of vertex indices, sorted by y }
  165. begin
  166. oldcolor := CurrentColor;
  167. CurrentColor := FillSettings.Color;
  168. ptable := @PolyPoints;
  169. if (numpoints<=0) then exit;
  170. getmem(indextable, sizeof(graph_int) * numpoints);
  171. getmem(activetable, sizeof(edge) * numpoints);
  172. if (not assigned(activetable)) or (not assigned(indextable)) then
  173. begin
  174. _GraphResult := grNoScanMem;
  175. exit;
  176. end;
  177. {$R-}
  178. { create y-sorted array of indices indextable[k] into vertex list }
  179. for k := 0 to (numpoints-1) do
  180. indextable^[k] := k;
  181. { sort the indextable by points[indextable[k]].y }
  182. {$ifndef fpc}
  183. bsort(indextable, numpoints, sizeof(graph_int), compare_ind);
  184. {$else fpc}
  185. bsort(indextable, numpoints, sizeof(graph_int), @compare_ind);
  186. {$endif fpc}
  187. { start with empty active edge table }
  188. activepoints := 0;
  189. { indextable[k] is the next vertex to process }
  190. k := 0;
  191. { ymin of polygon }
  192. starty := ceil(pointarray(polypoints)[indextable^[0]].y-0.5);
  193. { ymax of polygon }
  194. endy := floor(pointarray(polypoints)[indextable^[numpoints-1]].y-0.5);
  195. { step through scanlines }
  196. for y := starty to endy do begin
  197. { check vertices between previous scanline and current one, if any }
  198. while (k < numpoints) and
  199. (pointarray(polypoints)[indextable^[k]].y<=(y+0.5)) do begin
  200. i := indextable^[k];
  201. { insert or delete edges before and after points[i] ((i-1) to i and
  202. i to (i+1)) from active edge table if they cross scanline y }
  203. { point previous to i }
  204. if (i > 0) then j := i-1 else j := numpoints-1;
  205. { old edge, remove from list }
  206. if (pointarray(polypoints)[j].y <= (y-0.5)) then cdelete(j)
  207. { new edge, add to active edges }
  208. else if (pointarray(polypoints)[j].y > (y + 0.5)) then cinsert(j, y);
  209. { point next after i }
  210. if (i < (numpoints-1)) then j := i+1 else j := 0;
  211. { old edge, remove from active edge table }
  212. if (pointarray(polypoints)[j].y <= (y - 0.5)) then cdelete(i)
  213. { new edge, add to active edges }
  214. else if (pointarray(polypoints)[j].y > (y + 0.5)) then cinsert(i, y);
  215. inc(k);
  216. end;
  217. { sort active edges list by active[j].x }
  218. {$ifndef fpc}
  219. bsort(activetable, activepoints, sizeof(edge), compare_active);
  220. {$else fpc}
  221. bsort(activetable, activepoints, sizeof(edge),@compare_active);
  222. {$endif fpc}
  223. j := 0;
  224. { draw horizontal segments for scanline y }
  225. while (j < activepoints) do begin
  226. {xl := ceil(activetable^[j].x-0.5);}
  227. xl := trunc(activetable^[j].x-0.5);
  228. if frac(activetable^[j].x-0.5)>0 then inc(xl);
  229. xr := trunc(activetable^[j+1].x-0.5);
  230. if frac(activetable^[j+1].x-0.5)<0 then dec(xr);
  231. if (xl <= xr) then
  232. PatternLine(xl,xr,y);
  233. { line(xl, y, xr+1, y);}
  234. { increment both edges' coordinates }
  235. with activetable^[j] do begin
  236. x := x + dx;
  237. end;
  238. with activetable^[j+1] do begin
  239. x := x + dx;
  240. end;
  241. inc(j, 2);
  242. end;
  243. end;
  244. {$ifdef debug}
  245. {$R+}
  246. {$endif debug}
  247. freemem(activetable, sizeof(edge) * numpoints);
  248. freemem(indextable, sizeof(graph_int) * numpoints);
  249. { restore the old color }
  250. CurrentColor := OldColor;
  251. { now let's draw the outline of this polygon }
  252. DrawPoly(NumPoints, PolyPoints);
  253. end;
  254. type
  255. TFloodLine = record
  256. x1 : Integer;
  257. x2 : Integer;
  258. y : Integer;
  259. end;
  260. TDrawnList = Array[0..StdBuffersize] of TFloodLine;
  261. var
  262. DrawnIndex : Word;
  263. DrawnList : TDrawnList;
  264. Buffer : Record { Union for byte and word addressing of buffer }
  265. ByteIndex : Word;
  266. WordIndex : Word;
  267. Case Boolean Of
  268. False : (Bytes : Array [0..StdBufferSize-1] Of Byte);
  269. True : (Words : Array [0..(StdBufferSize DIV 2)-1] Of Word);
  270. End;
  271. s1, s2, s3 : PWordArray; { Three buffers for scanlines }
  272. Procedure PushPoint (x, y : Integer);
  273. {********************************************************}
  274. { Adds a point to the list of points to check if we }
  275. { need to draw. Doesn't add the point if there is a }
  276. { buffer overflow. }
  277. {********************************************************}
  278. var
  279. i: integer;
  280. Begin
  281. If Buffer.WordIndex<(StdBufferSize DIV 2) then
  282. Begin
  283. Buffer.Words[Buffer.WordIndex]:=x;
  284. Buffer.Words[Buffer.WordIndex+1]:=y;
  285. Inc (Buffer.WordIndex,2);
  286. End
  287. End;
  288. Procedure PopPoint (Var x, y : Integer);
  289. {********************************************************}
  290. { Removes a point from the list of points to check, if }
  291. { we try to access an illegal point, then the routine }
  292. { returns -1,-1 as a coordinate pair. }
  293. {********************************************************}
  294. Begin
  295. If Buffer.WordIndex>1 then
  296. Begin
  297. x:=Buffer.Words[Buffer.WordIndex-2];
  298. y:=Buffer.Words[Buffer.WordIndex-1];
  299. Dec (Buffer.WordIndex,2);
  300. End
  301. Else
  302. Begin
  303. x:=-1;
  304. y:=-1;
  305. End;
  306. End;
  307. {********************************************************}
  308. { Procedure AddLinePoints() }
  309. {--------------------------------------------------------}
  310. { Adds a line segment to the list of lines which will be }
  311. { drawn to the screen. The line added is on the specified}
  312. { Y axis, from the x1 to x2 coordinates. }
  313. {********************************************************}
  314. Procedure AddLinePoints(x1,x2,y: integer);
  315. begin
  316. DrawnList[DrawnIndex].x1 := x1;
  317. DrawnList[DrawnIndex].x2 := x2;
  318. DrawnList[DrawnIndex].y := y;
  319. Inc(DrawnIndex);
  320. end;
  321. {********************************************************}
  322. { Procedure AlreadyDrawn() }
  323. {--------------------------------------------------------}
  324. { This routine searches through the list of segments }
  325. { which will be drawn to the screen, and determines if }
  326. { the specified point (x,y) will already be drawn. }
  327. { i.e : Checks if the x,y point lies within a known }
  328. { segment which will be drawn to the screen. This makes }
  329. { sure that we don't draw some segments two times. }
  330. { Return TRUE if the point is already in the segment list}
  331. { to draw, otherwise returns FALSE. }
  332. {********************************************************}
  333. Function AlreadyDrawn(x, y: integer): boolean;
  334. var
  335. LocalIndex : integer;
  336. begin
  337. AlreadyDrawn := FALSE;
  338. LocalIndex := 0;
  339. while LocalIndex < DrawnIndex do
  340. Begin
  341. { if vertical val is equal to our y point ... }
  342. if DrawnList[LocalIndex].y = y then
  343. Begin
  344. { then check if x >< ... }
  345. if (x >= DrawnList[LocalIndex].x1) and
  346. (x <= DrawnList[LocalIndex].x2) then
  347. Begin
  348. AlreadyDrawn := TRUE;
  349. exit;
  350. end;
  351. end;
  352. Inc(LocalIndex);
  353. end;
  354. end;
  355. Procedure FloodFill (x, y : Integer; Border: word);
  356. {********************************************************}
  357. { Procedure FloodFill() }
  358. {--------------------------------------------------------}
  359. { This routine fills a region of the screen bounded by }
  360. { the <Border> color. It uses the current fillsettings }
  361. { for the flood filling. Clipping is supported, and }
  362. { coordinates are local/viewport relative. }
  363. {********************************************************}
  364. Var
  365. Beginx : Integer;
  366. d, e, a : Byte;
  367. Cont : Boolean;
  368. BackupColor : Word;
  369. x1, x2: integer;
  370. Index : Integer;
  371. Begin
  372. { Save current drawing color }
  373. BackupColor := CurrentColor;
  374. CurrentColor := FillSettings.Color;
  375. { MaxX is based on zero index }
  376. GetMem (s1,(MaxX+1)*2); { A pixel color represents a word }
  377. GetMem (s2,(MaxX+1)*2); { A pixel color represents a word }
  378. GetMem (s3,(MaxX+1)*2); { A pixel color represents a word }
  379. if (not assigned(s1)) or (not assigned(s2)) or (not assigned(s3)) then
  380. begin
  381. _GraphResult := grNoFloodMem;
  382. exit;
  383. end;
  384. If (x<0) Or (y<0) Or (x>MaxX) Or (y>MaxY) then Exit;
  385. { Some internal variables }
  386. Index := 0;
  387. { Index of segments to draw }
  388. DrawnIndex := 0;
  389. { Index of points to check }
  390. Buffer.WordIndex:=0;
  391. PushPoint (x,y);
  392. While Buffer.WordIndex>0 Do
  393. Begin
  394. PopPoint (x,y);
  395. { Get the complete lines for the following }
  396. GetScanline (y-1,s2^);
  397. GetScanline (y,s1^);
  398. GetScanline (y+1,s3^);
  399. { check the current scan line }
  400. While (s1^[x]<>Border) And (x<=MaxX) Do Inc (x);
  401. d:=0;
  402. e:=0;
  403. Dec (x);
  404. Beginx:=x;
  405. REPEAT
  406. { check the above line }
  407. If y<MaxY then
  408. Begin
  409. Cont:=(s3^[x]<>Border) and (not AlreadyDrawn(x,y+1));
  410. If (e=0) And Cont then
  411. Begin
  412. PushPoint (x,y+1);
  413. e:=1;
  414. End
  415. Else
  416. If (e=1) And Not Cont then e:=0;
  417. End;
  418. { check the line below }
  419. If (y>0) then
  420. Begin
  421. Cont:=(s2^[x]<>Border) and (not AlreadyDrawn(x,y-1));
  422. If (d=0) And Cont then
  423. Begin
  424. PushPoint (x,y-1);
  425. d:=1;
  426. End
  427. Else
  428. If (d=1) And Not Cont then d:=0;
  429. End;
  430. Dec (x);
  431. Until (x<0) Or (s1^[x]=Border);
  432. { swap the values }
  433. x1:=x+1;
  434. x2:=BeginX;
  435. if x1 > x2 then
  436. Begin
  437. x:=x1;
  438. x1:=x2;
  439. x2:=x;
  440. end;
  441. { Add to the list of drawn lines }
  442. AddLinePoints(x1,x2,y);
  443. PatternLine (x1,x2,y);
  444. End; { end while }
  445. FreeMem (s1,(MaxX+1)*2);
  446. FreeMem (s2,(MaxX+1)*2);
  447. FreeMem (s3,(MaxX+1)*2);
  448. CurrentColor := BackUpColor;
  449. End;
  450. {
  451. $Log$
  452. Revision 1.4 1999-07-12 14:52:52 jonas
  453. * fixed procvar syntax error and ceil and floor functions
  454. Revision 1.3 1999/07/12 13:27:11 jonas
  455. + added Log and Id tags
  456. * added first FPC support, only VGA works to some extend for now
  457. * use -dasmgraph to use assembler routines, otherwise Pascal
  458. equivalents are used
  459. * use -dsupportVESA to support VESA (crashes under FPC for now)
  460. * only dispose vesainfo at closegrph if a vesa card was detected
  461. * changed int32 to longint (int32 is not declared under FPC)
  462. * changed the declaration of almost every procedure in graph.inc to
  463. "far;" becquse otherwise you can't assign them to procvars under TP
  464. real mode (but unexplainable "data segnment too large" errors prevent
  465. it from working under real mode anyway)
  466. }