123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762 |
- unit GR32_VPR;
- (* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1 or LGPL 2.1 with linking exception
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * Alternatively, the contents of this file may be used under the terms of the
- * Free Pascal modified version of the GNU Lesser General Public License
- * Version 2.1 (the "FPC modified LGPL License"), in which case the provisions
- * of this license are applicable instead of those above.
- * Please see the file LICENSE.txt for additional information concerning this
- * license.
- *
- * The Original Code is Vectorial Polygon Rasterizer for Graphics32
- *
- * The Initial Developer of the Original Code is
- * Mattias Andersson <[email protected]>
- *
- * Portions created by the Initial Developer are Copyright (C) 2008-2012
- * the Initial Developer. All Rights Reserved.
- *
- * ***** END LICENSE BLOCK ***** *)
- interface
- {$include GR32.inc}
- uses
- GR32;
- type
- PSingleArray = GR32.PSingleArray;
- TValueSpan = record
- LowX, HighX: Integer;
- Values: PSingleArray;
- end;
- TRenderSpanEvent = procedure(const Span: TValueSpan; DstY: Integer) of object;
- TRenderSpanProc = procedure(Data: Pointer; const Span: TValueSpan; DstY: Integer);
- procedure RenderPolyPolygon(const Points: TArrayOfArrayOfFloatPoint;
- const ClipRect: TFloatRect; const RenderProc: TRenderSpanProc; Data: Pointer = nil); overload;
- procedure RenderPolygon(const Points: TArrayOfFloatPoint;
- const ClipRect: TFloatRect; const RenderProc: TRenderSpanProc; Data: Pointer = nil); overload;
- procedure RenderPolyPolygon(const Points: TArrayOfArrayOfFloatPoint;
- const ClipRect: TFloatRect; const RenderProc: TRenderSpanEvent); overload;
- procedure RenderPolygon(const Points: TArrayOfFloatPoint;
- const ClipRect: TFloatRect; const RenderProc: TRenderSpanEvent); overload;
- implementation
- {$if defined(FPC) and defined(CPUx86_64) }
- // Must apply work around for negative array index on FPC 64-bit.
- // See:
- // - https://github.com/graphics32/graphics32/issues/51
- // - https://forum.lazarus.freepascal.org/index.php/topic,44655.0.html
- {$define NEGATIVE_INDEX_64}
- {$ifend}
- uses
- Math,
- GR32_Math,
- GR32_LowLevel,
- GR32_VectorUtils;
- // FastFloor is slow on x86 due to call overhead
- {$if (not defined(PUREPASCAL)) and defined(CPUx86_64)}
- // Use of FastFloor in VPR currently corrupts the memory manager of FPC
- // so temporarily disabled there.
- {$if (not defined(FPC))}
- {$define USE_POLYFLOOR}
- {$ifend}
- {$ifend}
- function PolyFloor(Value: Single): integer; overload; {$ifndef DEBUG} inline; {$endif}
- begin
- {$if defined(USE_POLYFLOOR)}
- Result := FastFloorSingle(Value);
- {$else}
- Result := Round(Value);
- {$ifend}
- end;
- function PolyFloor(Value: Double): integer; overload; {$ifndef DEBUG} inline; {$endif}
- begin
- {$if defined(USE_POLYFLOOR)}
- Result := FastFloorDouble(Value);
- {$else}
- Result := Round(Value);
- {$ifend}
- end;
- function PolyCeil(Value: Single): integer; overload; {$ifndef DEBUG} inline; {$endif}
- begin
- {$if defined(USE_POLYFLOOR)}
- Result := FastCeilSingle(Value);
- {$else}
- Result := -Round(-Value);
- {$ifend}
- end;
- function PolyCeil(Value: Double): integer; overload; {$ifndef DEBUG} inline; {$endif}
- begin
- {$if defined(USE_POLYFLOOR)}
- Result := FastCeilDouble(Value);
- {$else}
- Result := -Round(-Value);
- {$ifend}
- end;
- (* Mattias Andersson (from [email protected]):
- > Which algorithm are you using for coverage calculation?
- I don't have any references, since it's entirely my own design. Here is
- a basic outline of how I compute the coverage values:
- 1. split each line segment into smaller segments in a vertical buffer,
- such that y-values are between 0 and 1;
- 2. poly-polygons involves a merge step for vertical buffers;
- 3. Extract spans of coverage values for each scanline:
- (a) set the length of the span to the horizontal range of that row;
- (b) if a line segment goes from row Y to row Y + 1 then we need to add
- or subtract 1 from the (X, X + 1) indexes at the crossing (depending on
- line orientation);
- (c) compute cumulative sum of span values (expensive!);
- (d) integrate each line segment and accumulate span buffer.
- The rendering step takes the coverage values and transforms that into an
- alpha buffer that is blended onto the target bitmap (here we use the
- non-zero and even-odd fill rules).
- Initially I was sorting the crossing points of each scanline, but I
- realized that by performing a cumulative sum, this would be completely
- redundant.
- Currently I only compute a single span of coverage values for each
- scanline, but I think I should also implement a case where I compute
- multiple RLE encoded spans (which I think could be faster in some cases).
- There is a tricky case that might not always yield an accurate coverage
- value (when we have positively and negatively oriented lines of two
- different polygons/faces in the same pixel). The only way to overcome
- this would be by preprocessing the polygons and remove intersections. I
- believe this very problem exists in AGG and FreeType too.
- *)
- type
- PLineSegment = ^TLineSegment;
- TLineSegment = array [0..1] of TFloatPoint;
- PLineSegmentArray = ^TLineSegmentArray;
- TLineSegmentArray = array [0..0] of TLineSegment;
- TScanLine = record
- Segments: PLineSegmentArray;
- Count: Integer;
- Y: Integer;
- end;
- TScanLines = array of TScanLine;
- PScanLineArray = ^TScanLineArray;
- TScanLineArray = array [0..0] of TScanLine;
- procedure IntegrateSegment(const P1, P2: TFloatPoint; Values: PSingleArray);
- var
- {$if defined(NEGATIVE_INDEX_64) }
- X1, X2: Int64;
- {$else}
- X1, X2: Integer;
- {$ifend}
- i: Integer;
- Dx, Dy, DyDx, Y: TFloat;
- fracX1, fracX2: TFloat;
- begin
- (*
- ** We have a line segment going from (X1,Y1) to (X2,Y2):
- **
- ** X1 X2
- ** +---------
- ** Y1 | *
- ** | *
- ** | *
- ** | *
- ** | *
- ** | *
- ** Y2 | *
- **
- ** The Y values in the segment belongs to a single scanline so the line segment is 1 pixel high.
- ** Additionally, we know that the Y values are in the range [0..1].
- ** In the example below, we have a segment where X2-X1=6. Each box is a pixel.
- **
- ** X1 X2
- ** +---+---+---+---+---+---+---+
- ** Y1 | * | | | | | | |
- ** | | * | | | | | |
- ** | | | * | | | | |
- ** | | | | * | | | |
- ** | | | | | * | | |
- ** | | | | | | * | |
- ** Y2 | | | | | | | * |
- ** +---+---+---+---+---+---+---+
- **
- ** For each X, we need to calculate the area below (or above) the line segment.
- ** We do this by calculating the slope of the line, and from that we can find the Y value
- ** given an X value.
- ** Once we have an X and an Y value we calculate the area as X*Y/2.
- **
- ** X1 X2
- ** +---+---+---+---+---+---+---+
- ** Y1 | * | | | | | | |
- ** | * | * | | | | | |
- ** | * | * | * | | | | |
- ** | * | * | * | * | | | |
- ** | * | * | * | * | * | | |
- ** | * | * | * | * | * | * | |
- ** Y2 | * | * | * | * | * | * | * |
- ** +---+---+---+---+---+---+---+
- **
- *)
- X1 := PolyFloor(P1.X);
- X2 := PolyFloor(P2.X);
- // Vertical segment (within one pixel)
- if X1 = X2 then
- begin
- Values[X1] := Values[X1] + 0.5 * (P2.X - P1.X) * (P1.Y + P2.Y);
- end else
- // Everything else
- begin
- Dx := P2.X - P1.X;
- Dy := P2.Y - P1.Y;
- DyDx := Dy/Dx; // For each X, how much does Y increment
- if X1 < X2 then
- begin
- fracX1 := 1 - (P1.X - X1);
- fracX2 := P2.X - X2;
- Y := P1.Y + fracX1 * DyDx;
- // First fractional X (fracX1..1)
- Values[X1] := Values[X1] + 0.5 * (P1.Y + Y) * fracX1;
- // Whole Xs (1..1)
- for i := X1 + 1 to X2 - 1 do
- begin
- Values[i] := Values[i] + (Y + DyDx * 0.5); // N: Sx = 1
- Y := Y + DyDx;
- end;
- // Last fractional X (1..fracX2)
- Values[X2] := Values[X2] + 0.5 * (Y + P2.Y) * fracX2;
- end else // X1 > X2
- begin
- fracX1 := P1.X - X1;
- fracX2 := 1 - (P2.X - X2);
- Y := P1.Y - fracX1 * DyDx;
- // First fractional X (fracX1..1)
- Values[X1] := Values[X1] - 0.5 * (P1.Y + Y) * fracX1;
- // Whole Xs (1..1)
- for i := X1 - 1 downto X2 + 1 do
- begin
- Values[i] := Values[i] - (Y - DyDx * 0.5); // N: Sx = -1
- Y := Y - DyDx;
- end;
- // Last fractional X (1..fracX2)
- Values[X2] := Values[X2] - 0.5 * (Y + P2.Y) * fracX2;
- end;
- end;
- end;
- procedure ExtractSingleSpan(const ScanLine: TScanLine; out Span: TValueSpan; SpanData: PSingleArray);
- var
- i: Integer;
- {$if defined(NEGATIVE_INDEX_64) }
- X: Int64;
- {$else}
- X: Integer;
- {$ifend}
- P: PFloatPoint;
- Segment: PLineSegment;
- fracX: TFloat;
- Points: PFloatPointArray;
- N: Integer;
- begin
- (*
- ** Extract spans of coverage values for a scanline.
- **
- ** We do this by looking at the scanline segments. Each segment indicates
- ** where on the X-axis the line, that the segment was extracted from,
- ** crosses the scanline.
- **
- ** At the point where the line crosses, we update the coverage value.
- ** For example, four crossings could produce the following coverage values:
- ** [ 1 -1 1 -1 ]
- ** Note that the actual coverage values will be [0..1].
- **
- ** When all segments has been processed like this, we convert the values
- ** to a sequence of values using the CumSum function:
- ** [ 11111111 111111 ]
- **
- *)
- N := ScanLine.Count * 2; // Pairs of TFloatPoint, so double the count
- Points := @ScanLine.Segments[0];
- // Low/High bound of span
- Span.LowX := High(Integer);
- Span.HighX := Low(Integer);
- (*
- ** (a) set the length of the span to the horizontal range of that row.
- **
- ** (b) if a line segment goes from row Y to row Y + 1 then we need to add
- ** or subtract 1 from the (X, X + 1) indexes at the crossing (depending on
- ** line orientation).
- *)
- P := @Points[0];
- for i := 0 to N - 1 do
- begin
- // Since we know X >= 0 we could have used Trunc here but unfortunately
- // Delphi's Trunc is much slower than Round because it modifies the FPU
- // control word.
- // Note: We're using FastFloor now so the above comment is no longer relevant.
- X := PolyFloor(P.X);
- // (a1) Find the lower bound of the horizontal span
- if X < Span.LowX then
- Span.LowX := X;
- // (b) if a line segment goes from row Y to row Y + 1 then...
- if P.Y = 1 then
- begin
- fracX := P.X - X;
- if Odd(i) then
- begin // Right edge
- SpanData[X] := SpanData[X] + (1 - fracX);
- Inc(X);
- SpanData[X] := SpanData[X] + fracX;
- end else
- begin // Left edge
- SpanData[X] := SpanData[X] - (1 - fracX);
- Inc(X);
- SpanData[X] := SpanData[X] - fracX;
- end;
- end;
- // (a2) Find the upper bound of the horizontal span
- if X > Span.HighX then
- Span.HighX := X;
- inc(P);
- end;
- (*
- ** (c) compute cumulative sum of span values.
- *)
- X := Span.LowX; // Use X so NEGATIVE_INDEX_64 is handled
- Span.Values := @SpanData[X];
- CumSum(Span.Values, Span.HighX - Span.LowX + 1);
- (*
- ** (d) integrate each line segment and accumulate span buffer.
- *)
- for i := 0 to ScanLine.Count - 1 do
- begin
- Segment := @ScanLine.Segments[i];
- IntegrateSegment(Segment[0], Segment[1], SpanData);
- end;
- end;
- procedure AddSegment(const X1, Y1, X2, Y2: TFloat; var ScanLine: TScanLine);// {$IFDEF USEINLINING} inline; {$ENDIF}
- var
- S: PLineSegment;
- Y1bin: Cardinal absolute Y1;
- Y2bin: Cardinal absolute Y2;
- begin
- // Fast way of checking a Single = 0.
- if (Y1bin shl 1 = 0) and (Y2bin shl 1 = 0) then
- // if (Y1 = 0) and (Y2 = 0) then
- Exit; { needed for proper clipping }
- // Add segment to the scanline's list of segments
- S := @ScanLine.Segments[ScanLine.Count];
- Inc(ScanLine.Count);
- S[0].X := X1;
- S[0].Y := Y1;
- S[1].X := X2;
- S[1].Y := Y2;
- end;
- procedure DivideSegment(var P1, P2: TFloatPoint; const ScanLines: PScanLineArray);
- var
- Y, Y1, Y2: Integer;
- X, X2: TFloat;
- k: TFloat;
- n: TFloat;
- begin
- (*
- ** Split each line segment into smaller segments in a vertical buffer,
- ** such that y-values are between 0 and 1.
- *)
- Y1 := PolyFloor(P1.Y);
- Y2 := PolyFloor(P2.Y);
- // Special case for horizontal line; It just produces a single segment.
- if Y1 = Y2 then // TODO : Should also handle "Y1 almost equal Y2" ?
- begin
- AddSegment(P1.X, P1.Y - Y1, P2.X, P2.Y - Y1, ScanLines[Y1]);
- end else
- begin
- // k: Inverse slope; For each change in Y, how much does X change
- // k is expanded below to limit rounding errors.
- k := (P2.X - P1.X) / (P2.Y - P1.Y);
- // TODO : We should also special case "P1.X almost equal P2.X" ?
- if Y1 < Y2 then // Y is increasing
- begin
- X := P1.X + (Y1 + 1 - P1.Y) * { k } (P2.X - P1.X) / (P2.Y - P1.Y);
- // First fractional scanline (n..1)
- n := P1.Y - Y1;
- AddSegment(P1.X, n, X, 1, ScanLines[Y1]);
- // Whole scanlines (0..1)
- for Y := Y1 + 1 to Y2 - 1 do
- begin
- // Note: Iteratively calculating the next X value based on the previous value and an
- // increment accumulates the rounding error.
- // Ideally we would repeat the calculation of X from Y for each Y to avoid this but
- // that is too expensive.
- // Because of the rounding error we can end up with a tiny negative X value (when X
- // almost equals k) and, because we've set the rounding mode to rmDown, this negative
- // X value will later be rounded down to -1 in ExtractSingleSpan.
- // This is the cause of issue #272.
- // The Max(0, ...) below works around this problem.
- X2 := Max(0, X + k);
- AddSegment(X, 0, X2, 1, ScanLines[Y]);
- X := X2;
- end;
- // Last fractional scanline (0..n)
- n := P2.Y - Y2;
- AddSegment(X, 0, P2.X, n, ScanLines[Y2]);
- end else
- begin
- X := P1.X + (Y1 - P1.Y) * { k } (P2.X - P1.X) / (P2.Y - P1.Y);
- // First fractional scanline (n..0)
- n := P1.Y - Y1;
- AddSegment(P1.X, n, X, 0, ScanLines[Y1]);
- // Whole scanlines (1..0)
- for Y := Y1 - 1 downto Y2 + 1 do
- begin
- X2 := Max(0, X - k);
- AddSegment(X, 1, X2, 0, ScanLines[Y]);
- X := X2;
- end;
- // Last fractional scanline (1..n)
- n := P2.Y - Y2;
- AddSegment(X, 1, P2.X, n, ScanLines[Y2]);
- end;
- end;
- end;
- procedure BuildScanLines(const Points: TArrayOfArrayOfFloatPoint;
- out ScanLines: TScanLines);
- var
- PolygonIndex, MaxPolygon, MaxVertex: Integer;
- i, Y0,Y1,Y, YMin,YMax: Integer;
- SegmentCount: Integer;
- pY: PSingle;
- pPoint1, PPoint2: PFloatPoint;
- pScanLines: PScanLineArray;
- begin
- (*
- ** Determine range of Y values (i.e. number of scanlines)
- *)
- YMin := MaxInt;
- YMax := -MaxInt;
- MaxPolygon := High(Points);
- for PolygonIndex := 0 to MaxPolygon do
- begin
- MaxVertex := High(Points[PolygonIndex]);
- if MaxVertex < 2 then
- Continue;
- pY := @Points[PolygonIndex][0].Y;
- for i := 0 to MaxVertex do
- begin
- Y := PolyFloor(pY^);
- if YMin > Y then
- YMin := Y;
- if YMax < Y then
- YMax := Y;
- inc(PFloatPoint(pY)); // skips X value
- end;
- end;
- if YMin > YMax then
- Exit;
- SetLength(ScanLines, YMax - YMin + 2);
- // Offset scanline pointer so we don't have to offset the Y coordinate
- pScanLines := @ScanLines[-YMin];
- (*
- ** Compute array sizes for each scanline
- *)
- // For each polygon...
- for PolygonIndex := 0 to MaxPolygon do
- begin
- MaxVertex := High(Points[PolygonIndex]);
- if MaxVertex < 2 then
- Continue; // No line segments in this polygon
- // Start with the line segment going from the last vertex to the first
- Y0 := PolyFloor(Points[PolygonIndex][MaxVertex].Y);
- pY := @Points[PolygonIndex][0].Y;
- // For each line of the polygon...
- for i := 0 to MaxVertex do
- begin
- // Calculate the max fragment count; Start of line vertex increments
- // the running fragment count for the start scanline and the end of
- // line vertex decrements the running fragment count for the end
- // scanline.
- //
- // Polygon Scanline Lines(Y0, Y1) Count Sum
- // (4, 0) (0, 2) (2, 1) (1, 3) (3, 7) (7, 4)
- //
- // * 0 1 1 2 2
- // /\ 1 1 1 2 4
- // / \/\ 2 0 4
- // / \ 3 -1 -1 1 -1 3
- // / / 4 -1 1 0 3
- // \ / 5 -1 -1 2
- // \ / 6 0 2
- // \ / 7 0 2
- // \/ 8 -1 -1 -2 0
- //
- Y1 := PolyFloor(pY^);
- // Line has positive slope
- if Y0 <= Y1 then
- begin
- Inc(pScanLines[Y0].Count);
- Dec(pScanLines[Y1 + 1].Count);
- end
- else
- // Line has negative slope
- begin
- Inc(pScanLines[Y1].Count);
- Dec(pScanLines[Y0 + 1].Count);
- end;
- // Move to next line
- Y0 := Y1;
- inc(PFloatPoint(pY)); // skips X value
- end;
- end;
- (*
- ** Allocate memory
- *)
- SegmentCount := 0;
- for i := 0 to High(ScanLines) do
- begin
- // Adjust running fragment count
- Inc(SegmentCount, ScanLines[i].Count);
- GetMem(ScanLines[i].Segments, SegmentCount * SizeOf(TLineSegment));
- ScanLines[i].Count := 0;
- ScanLines[i].Y := YMin + i;
- end;
- (*
- ** Divide all segments of the polygon into scanline fragments
- *)
- for PolygonIndex := 0 to MaxPolygon do
- begin
- MaxVertex := High(Points[PolygonIndex]);
- if MaxVertex < 2 then
- Continue;
- // Start with the line segment going from the last vertex to the first
- pPoint1 := @Points[PolygonIndex][MaxVertex];
- PPoint2 := @Points[PolygonIndex][0];
- for i := 0 to MaxVertex do
- begin
- DivideSegment(pPoint1^, PPoint2^, pScanLines);
- // Move on to the next segment
- pPoint1 := PPoint2;
- Inc(PPoint2);
- end;
- end;
- end;
- procedure RenderScanline(var ScanLine: TScanLine;
- RenderProc: TRenderSpanProc; Data: Pointer; SpanData: PSingleArray; ClipX1, ClipX2: Integer);
- var
- Span: TValueSpan;
- {$if defined(NEGATIVE_INDEX_64) }
- X: Int64;
- {$else}
- X: Integer;
- {$ifend}
- begin
- if ScanLine.Count = 0 then
- exit;
- ExtractSingleSpan(ScanLine, Span, SpanData);
- // Clip
- if Span.LowX < ClipX1 then
- Span.LowX := ClipX1;
- if Span.HighX > ClipX2 then
- Span.HighX := ClipX2;
- if Span.HighX < Span.LowX then
- Exit;
- RenderProc(Data, Span, ScanLine.Y);
- X := Span.LowX;
- FillLongWord(SpanData[X], Span.HighX - Span.LowX + 1, 0);
- end;
- {$ifdef FPC}
- type
- TRoundingMode = Math.TFPURoundingMode;
- {$endif}
- procedure RenderPolyPolygon(const Points: TArrayOfArrayOfFloatPoint;
- const ClipRect: TFloatRect; const RenderProc: TRenderSpanProc; Data: Pointer);
- var
- ScanLines: TScanLines;
- I, Len: Integer;
- Poly: TArrayOfArrayOfFloatPoint;
- CX1, CX2: Integer;
- SpanData: PSingleArray;
- {$if not defined(USE_POLYFLOOR)}
- SavedRoundingMode: TRoundingMode;
- {$ifend}
- begin
- Len := Length(Points);
- if Len = 0 then
- Exit;
- SetLength(Poly, Len);
- for i := 0 to Len -1 do
- Poly[i] := ClipPolygon(Points[i], ClipRect);
- {$if not defined(USE_POLYFLOOR)}
- SavedRoundingMode := SetRoundMode(rmDown);
- try
- {$ifend}
- BuildScanLines(Poly, ScanLines);
- if (Length(ScanLines) > 0) then
- begin
- CX1 := PolyFloor(ClipRect.Left);
- CX2 := PolyCeil(ClipRect.Right) - 1;
- I := CX2 - CX1 + 4;
- GetMem(SpanData, I * SizeOf(Single));
- FillLongWord(SpanData^, I, 0);
- for I := 0 to High(ScanLines) do
- begin
- RenderScanline(ScanLines[I], RenderProc, Data, @SpanData[-CX1 + 1], CX1, CX2);
- FreeMem(ScanLines[I].Segments);
- end;
- FreeMem(SpanData);
- end;
- {$if not defined(USE_POLYFLOOR)}
- finally
- SetRoundMode(SavedRoundingMode);
- end
- {$ifend}
- end;
- procedure RenderPolygon(const Points: TArrayOfFloatPoint;
- const ClipRect: TFloatRect; const RenderProc: TRenderSpanProc; Data: Pointer);
- begin
- RenderPolyPolygon(PolyPolygon(Points), ClipRect, RenderProc, Data);
- end;
- procedure RenderPolyPolygon(const Points: TArrayOfArrayOfFloatPoint;
- const ClipRect: TFloatRect; const RenderProc: TRenderSpanEvent);
- begin
- RenderPolyPolygon(Points, ClipRect, TRenderSpanProc(TMethod(RenderProc).Code), TMethod(RenderProc).Data);
- end;
- procedure RenderPolygon(const Points: TArrayOfFloatPoint;
- const ClipRect: TFloatRect; const RenderProc: TRenderSpanEvent);
- begin
- RenderPolygon(Points, ClipRect, TRenderSpanProc(TMethod(RenderProc).Code), TMethod(RenderProc).Data);
- end;
- end.
|