2
$Id: fills.inc,v 1.4 2005/02/14 17:13:30 peter Exp $
4
This file is part of the Free Pascal run time library.
5
Copyright (c) 1999-2000 by Thomas Schatzl and Carl Eric Codere
7
This include implements polygon filling and flood filling.
9
See the file COPYING.FPC, included in this distribution,
10
for details about the copyright.
12
This program is distributed in the hope that it will be useful,
13
but WITHOUT ANY WARRANTY; without even the implied warranty of
14
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16
**********************************************************************}
18
{ simple descriptive name }
19
function max(a, b : Longint) : Longint;
22
if (a > b) then max := a;
26
function min(a, b : Longint) : Longint;
29
if (a < b) then min := a;
32
procedure fillpoly(numpoints : Word; var polypoints);
34
{ disable range check mode }
36
{$define OPT_R_WAS_ON}
42
yMin, yMax, x, dX, dY, frac : Longint;
45
pedgearray = ^tedgearray;
46
tedgearray = array[0..0] of tedge;
48
ppedgearray = ^tpedgearray;
49
tpedgearray = array[0..0] of pedge;
52
nActive, nNextEdge : Longint;
54
i, j, gap, x0, x1, y, nEdges : Longint;
56
GET, AET : ppedgearray;
63
{ /********************************************************************
64
* Add entries to the global edge table. The global edge table has a
65
* bucket for each scan line in the polygon. Each bucket contains all
66
* the edges whose yMin == yScanline. Each bucket contains the yMax,
67
* the x coordinate at yMax, and the denominator of the slope (dX)
69
getmem(et, sizeof(tedge) * numpoints);
70
getmem(get, sizeof(pedge) * numpoints);
71
getmem(aet, sizeof(pedge) * numpoints);
73
ptable := @polypoints;
75
{ check for getmem success }
78
for i := 0 to (numpoints-1) do begin
80
if (i+1) >= numpoints then p1 := ptable[0]
81
else p1 := ptable[i+1];
82
{ ignore if this is a horizontal edge}
83
if (p0.y = p1.y) then continue;
84
{swap ptable if necessary to ensure p0 contains yMin}
85
if (p0.y > p1.y) then begin
89
{ create the new edge }
90
et^[nEdges].ymin := p0.y;
91
et^[nEdges].ymax := p1.y;
92
et^[nEdges].x := p0.x;
93
et^[nEdges].dX := p1.x-p0.x;
94
et^[nEdges].dy := p1.y-p0.y;
95
et^[nEdges].frac := 0;
96
get^[nEdges] := @et^[nEdges];
99
{ sort the GET on ymin }
101
while (gap < nEdges) do gap := 3*gap+1;
103
while (gap > 0) do begin
104
for i := gap to (nEdges-1) do begin
106
while (j >= 0) do begin
107
if (GET^[j]^.ymin <= GET^[j+gap]^.yMin) then break;
109
GET^[j] := GET^[j+gap];
116
{ initialize the active edge table, and set y to first entering edge}
120
y := GET^[nNextEdge]^.ymin;
121
{ Now process the edges using the scan line algorithm. Active edges
122
will be added to the Active Edge Table (AET), and inactive edges will
123
be deleted. X coordinates will be updated with incremental integer
124
arithmetic using the slope (dY / dX) of the edges. }
125
while (nNextEdge < nEdges) or (nActive <> 0) do begin
126
{Move from the ET bucket y to the AET those edges whose yMin == y
128
while (nNextEdge < nEdges) and (GET^[nNextEdge]^.ymin = y) do begin
129
AET^[nActive] := GET^[nNextEdge];
133
{ Remove from the AET those entries for which yMax == y (leaving
136
while (i < nActive) do begin
137
if (AET^[i]^.yMax = y) then begin
139
move(AET^[i+1], AET^[i], (nActive-i)*sizeof(pedge));
144
if (y >= 0) then begin
145
{Now sort the AET on x. Since the list is usually quite small,
146
the sort is implemented as a simple non-recursive shell sort }
149
while (gap < nActive) do gap := 3*gap+1;
152
while (gap > 0) do begin
153
for i := gap to (nActive-1) do begin
155
while (j >= 0) do begin
156
if (AET^[j]^.x <= AET^[j+gap]^.x) then break;
158
AET^[j] := AET^[j+gap];
166
{ Fill in desired pixels values on scan line y by using pairs of x
167
coordinates from the AET }
169
while (i < nActive) do begin
172
{Left edge adjustment for positive fraction. 0 is interior. }
173
if (AET^[i]^.frac > 0) then inc(x0);
174
{Right edge adjustment for negative fraction. 0 is exterior. }
175
if (AET^[i+1]^.frac <= 0) then dec(x1);
178
x1 := min(x1, viewWidth);
179
{ Draw interior spans}
180
if (x1 >= x0) then begin
181
PatternLine(x0, x1, y);
189
{ Update all the x coordinates. Edges are scan converted using a
190
modified midpoint algorithm (Bresenham's algorithm reduces to the
191
midpoint algorithm for two dimensional lines) }
192
for i := 0 to (nActive-1) do begin
194
{ update the fraction by dX}
198
while ( -(t^.frac) >= t^.dY) do begin
203
while (t^.frac >= t^.dY) do begin
209
if (y >= ViewHeight) then break;
211
freemem(et, sizeof(tedge) * numpoints);
212
freemem(get, sizeof(pedge) * numpoints);
213
freemem(aet, sizeof(pedge) * numpoints);
217
{ maximum supported Y resultion }
220
{ changing this to 1 or 2 doesn't improve performance noticably }
224
PFloodLine = ^TFloodLine;
232
TDrawnList = Array[0..(MaxYRes - 1) div YResDiv] of PFloodLine;
235
DrawnList : TDrawnList;
236
Buffer : Record { Union for byte and word addressing of buffer }
240
False : (Bytes : Array [0..StdBufferSize-1] Of Byte);
241
True : (Words : Array [0..(StdBufferSize DIV 2)-1] Of Word);
244
s1, s2, s3 : PWordArray; { Three buffers for scanlines }
247
Procedure PushPoint (x, y : smallint);
248
{********************************************************}
249
{ Adds a point to the list of points to check if we }
250
{ need to draw. Doesn't add the point if there is a }
252
{********************************************************}
254
If Buffer.WordIndex<(StdBufferSize DIV 2)-3 then
256
Buffer.Words[Buffer.WordIndex]:=x;
257
Buffer.Words[Buffer.WordIndex+1]:=y;
258
Inc (Buffer.WordIndex,2);
262
Procedure PopPoint (Var x, y : smallint);
263
{********************************************************}
264
{ Removes a point from the list of points to check, if }
265
{ we try to access an illegal point, then the routine }
266
{ returns -1,-1 as a coordinate pair. }
267
{********************************************************}
269
If Buffer.WordIndex>1 then
271
x:=Buffer.Words[Buffer.WordIndex-2];
272
y:=Buffer.Words[Buffer.WordIndex-1];
273
Dec (Buffer.WordIndex,2);
287
{********************************************************}
288
{ Procedure AddLinePoints() }
289
{--------------------------------------------------------}
290
{ Adds a line segment to the list of lines which will be }
291
{ drawn to the screen. The line added is on the specified}
292
{ Y axis, from the x1 to x2 coordinates. }
293
{********************************************************}
294
Procedure AddLinePoints(x1,x2,y: smallint);
295
var temp: PFloodLine;
301
temp^.next := DrawnList[y div YResDiv];
302
DrawnList[y div YResDiv] := temp;
305
{********************************************************}
306
{ Procedure AlreadyDrawn() }
307
{--------------------------------------------------------}
308
{ This routine searches through the list of segments }
309
{ which will be drawn to the screen, and determines if }
310
{ the specified point (x,y) will already be drawn. }
311
{ i.e : Checks if the x,y point lies within a known }
312
{ segment which will be drawn to the screen. This makes }
313
{ sure that we don't draw some segments two times. }
314
{ Return TRUE if the point is already in the segment list}
315
{ to draw, otherwise returns FALSE. }
316
{********************************************************}
317
Function AlreadyDrawn(x, y: smallint): boolean;
321
AlreadyDrawn := false;
322
temp := DrawnList[y div YResDiv];
323
while assigned(temp) do
329
AlreadyDrawn := true;
336
{********************************************************}
337
{ Procedure CleanUpDrawnList }
338
{--------------------------------------------------------}
339
{ removes all elements from the DrawnList. Doesn't init }
340
{ elements of it with NILL }
341
{********************************************************}
342
Procedure CleanUpDrawnList;
345
temp1, temp2: PFloodLine;
347
for l := 0 to high(DrawnList) do
349
temp1 := DrawnList[l];
350
while assigned(temp1) do
353
temp1 := temp1^.next;
360
Procedure FloodFill (x, y : smallint; Border: word);
361
{********************************************************}
362
{ Procedure FloodFill() }
363
{--------------------------------------------------------}
364
{ This routine fills a region of the screen bounded by }
365
{ the <Border> color. It uses the current fillsettings }
366
{ for the flood filling. Clipping is supported, and }
367
{ coordinates are local/viewport relative. }
368
{********************************************************}
375
x1, x2, prevy: smallint;
377
FillChar(DrawnList,sizeof(DrawnList),0);
380
{ Save current drawing color }
381
BackupColor := CurrentColor;
382
CurrentColor := FillSettings.Color;
383
{ MaxX is based on zero index }
384
GetMem (s1,(ViewWidth+1)*2); { A pixel color represents a word }
385
GetMem (s2,(ViewWidth+1)*2); { A pixel color represents a word }
386
GetMem (s3,(ViewWidth+1)*2); { A pixel color represents a word }
387
if (not assigned(s1)) or (not assigned(s2)) or (not assigned(s3)) then
389
_GraphResult := grNoFloodMem;
393
(x>ViewWidth) Or (y>ViewHeight) then Exit;
394
{ Index of points to check }
397
While Buffer.WordIndex>0 Do
400
{ Get the complete lines for the following }
403
If (prevy - y = 1) then
404
{ previous line was one below the new one, so the previous s2 }
411
GetScanline(0,ViewWidth,y-1,s2^);
413
Else If (y - prevy = 1) then
414
{ previous line was one above the new one, so the previous s3 }
421
GetScanline(0,ViewWidth,y+1,s3^);
425
GetScanline(0,ViewWidth,y-1,s2^);
426
GetScanline(0,ViewWidth,y,s1^);
427
GetScanline(0,ViewWidth,y+1,s3^);
431
{ check the current scan line }
432
While (s1^[x]<>Border) And (x<=ViewWidth) Do Inc (x);
438
{ check the above line }
441
Cont:=(s3^[x]<>Border) and (not AlreadyDrawn(x,y+1));
442
If (e=0) And Cont then
448
If (e=1) And Not Cont then e:=0;
450
{ check the line below }
453
Cont:=(s2^[x]<>Border) and (not AlreadyDrawn(x,y-1));
454
If (d=0) And Cont then
460
If (d=1) And Not Cont then d:=0;
463
Until (x<0) Or (s1^[x]=Border);
473
{ Add to the list of drawn lines }
474
AddLinePoints(x1,x2,y);
475
PatternLine (x1,x2,y);
478
FreeMem (s1,(ViewWidth+1)*2);
479
FreeMem (s2,(ViewWidth+1)*2);
480
FreeMem (s3,(ViewWidth+1)*2);
482
CurrentColor := BackUpColor;
485
{ restore previous range check mode }
486
{$ifdef OPT_R_WAS_ON}
491
Revision 1.4 2005/02/14 17:13:30 peter