123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692 |
- unit GR32_VPR2;
- (* ***** 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) 2012
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * ***** END LICENSE BLOCK ***** *)
- interface
- {$include GR32.inc}
- uses
- GR32, GR32_Polygons, GR32_OrdinalMaps;
- type
- PIntSpan = ^TIntSpan;
- TIntSpan = record
- Min, Max: Integer;
- end;
- const
- STARTSPAN: TIntSpan = (Min: MAXINT; Max: 0);
- type
- TPolygonRenderer32VPR2 = class(TPolygonRenderer32)
- private
- FOpacityMap: TFloatMap;
- FXSpan: array of TIntSpan;
- FYSpan: TIntSpan;
- procedure AddLineSegment(X1, Y1, X2, Y2: TFloat); overload;
- procedure DrawBitmap;
- public
- constructor Create; override;
- destructor Destroy; override;
- procedure PolyPolygonFS(const Points: TArrayOfArrayOfFloatPoint;
- const ClipRect: TFloatRect); override;
- end;
- { TPolygonRenderer32VPR2X }
- TPolygonRenderer32VPR2X = class(TPolygonRenderer32)
- private
- FOpacityMap: TIntegerMap;
- FXSpan: array of TIntSpan;
- FYSpan: TIntSpan;
- procedure AddLineSegment(X1, Y1, X2, Y2: TFixed); overload;
- procedure DrawBitmap;
- public
- constructor Create; override;
- destructor Destroy; override;
- procedure PolyPolygonFS(const Points: TArrayOfArrayOfFloatPoint;
- const ClipRect: TFloatRect); override;
- end;
- implementation
- uses
- Math,
- Types,
- GR32_VectorUtils,
- GR32_Math,
- GR32_LowLevel,
- GR32_Blend;
- // FastFloor is slow on x86 due to call overhead
- {$if (not defined(PUREPASCAL)) and defined(CPUx86_64)}
- // Use of FastFloor 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; inline;
- begin
- {$if defined(USE_POLYFLOOR)}
- Result := FastFloorSingle(Value);
- {$else}
- Result := Round(Value);
- {$ifend}
- end;
- function PolyFloor(Value: Double): integer; overload; inline;
- begin
- {$if defined(USE_POLYFLOOR)}
- Result := FastFloorDouble(Value);
- {$else}
- Result := Round(Value);
- {$ifend}
- end;
- { TPolygonRenderer32VPR2 }
- procedure UpdateSpan(var Span: TIntSpan; Value: Integer); {$IFDEF USEINLINING} inline; {$ENDIF}
- begin
- if Value < Span.Min then
- Span.Min := Value;
- if Value > Span.Max then
- Span.Max := Value;
- end;
- procedure TPolygonRenderer32VPR2.AddLineSegment(X1, Y1, X2, Y2: TFloat);
- type
- PFloatArray = ^TFloatArray;
- TFloatArray = array [0..1] of TFloat;
- const
- SGN: array [0..1] of Integer = (1, -1);
- EPSILON: TFloat = 0.0001;
- EPSILON_Dx: Double = 1 / 255;
- var
- P: PFloatArray;
- procedure AddSegment(X1, Y1, X2, Y2: TFloat);
- var
- dX, dY: TFloat;
- begin
- dX := (X1 + X2) * 0.5;
- dX := dX - PolyFloor(dX);
- dY := Y2 - Y1;
- dX := dX * dY;
- P[0] := P[0] + dY - dX;
- P[1] := P[1] + dX;
- end;
- var
- dX, dY, dYdX, dXdY: Double;
- PrevX, PrevY, NextX, NextY: Double;
- Delta, DeltaX, DeltaY: Double;
- X, Y, StepX, StepY: Integer;
- MaxDelta: Double;
- begin
- {$ifndef FPC}
- // Same as (Y2 = Y1)
- if (PCardinal(@Y2)^ = PCardinal(@Y1)^) then
- Exit;
- {$else} // Above optimization fails on FPC
- if (Y2 = Y1) then
- Exit;
- {$endif}
- {$ifndef FPC}
- dY := Double(Y2) - Double(Y1);
- dX := Double(X2) - Double(X1);
- {$else}
- dY := Y2 - Y1;
- dX := X2 - X1;
- {$endif}
- X := PolyFloor(X1);
- Y := PolyFloor(Y1);
- UpdateSpan(FYSpan, Y);
- StepX := Ord(dX < 0);
- StepY := Ord(dY < 0);
- X1 := X1 - StepX;
- Y1 := Y1 - StepY;
- X2 := X2 - StepX;
- Y2 := Y2 - StepY;
- StepX := SGN[StepX];
- StepY := SGN[StepY];
- if (Abs(dX) <= EPSILON_Dx) then
- begin
- MaxDelta := Abs(dY) - EPSILON;
- NextY := Y1;
- repeat
- UpdateSpan(FXSpan[Y], X);
- P := PFloatArray(FOpacityMap.ValPtr[X, Y]);
- PrevY := NextY;
- Inc(Y, StepY);
- NextY := Y;
- AddSegment(X1, PrevY, X1, NextY);
- until (Abs(Y1 - NextY) >= MaxDelta);
- AddSegment(X1, NextY, X1, Y2);
- end else
- begin
- dYdX := dY/dX;
- dXdY := dX/dY;
- DeltaX := X + StepX - X1;
- DeltaY := (Y + StepY - Y1) * dXdY;
- MaxDelta := Abs(dX) - EPSILON;
- NextX := X1;
- NextY := Y1;
- repeat
- PrevX := NextX;
- PrevY := NextY;
- UpdateSpan(FXSpan[Y], X);
- P := PFloatArray(FOpacityMap.ValPtr[X, Y]);
- if (Abs(DeltaX) <= Abs(DeltaY)) then
- begin
- Inc(X, StepX);
- Delta := DeltaX;
- DeltaX := DeltaX + StepX;
- end else
- begin
- Inc(Y, StepY);
- Delta := DeltaY;
- DeltaY := DeltaY + StepY * dXdY;
- end;
- NextX := X1 + Delta;
- NextY := Y1 + Delta * dYdX;
- AddSegment(PrevX, PrevY, NextX, NextY);
- until (Abs(Delta) >= MaxDelta);
- AddSegment(NextX, NextY, X2, Y2);
- end;
- end;
- constructor TPolygonRenderer32VPR2.Create;
- begin
- inherited Create;
- FOpacityMap := TFloatMap.Create;
- end;
- destructor TPolygonRenderer32VPR2.Destroy;
- begin
- FOpacityMap.Free;
- inherited;
- end;
- procedure MakeAlphaNonZeroUP(Coverage: PSingleArray; AlphaValues: PColor32Array;
- Count: Integer; Color: TColor32);
- var
- I: Integer;
- M, V: Cardinal;
- Last: TFloat;
- C: TColor32Entry absolute Color;
- begin
- M := C.A * $101;
- Last := Infinity;
- for I := 0 to Count - 1 do
- begin
- if PInteger(@Last)^ <> PInteger(@Coverage[I])^ then
- begin
- Last := Coverage[I];
- V := Abs(PolyFloor(Last * $10000)); // TODO : Is Floor the correct operator here?
- if V > $10000 then V := $10000;
- V := V * M shr 24;
- C.A := V;
- end;
- AlphaValues[I] := Color;
- end;
- end;
- procedure MakeAlphaEvenOddUP(Coverage: PSingleArray; AlphaValues: PColor32Array;
- Count: Integer; Color: TColor32);
- var
- I: Integer;
- M, V: Cardinal;
- Last: TFloat;
- C: TColor32Entry absolute Color;
- begin
- M := C.A * $101;
- Last := Infinity;
- for I := 0 to Count - 1 do
- begin
- if PInteger(@Last)^ <> PInteger(@Coverage[I])^ then
- begin
- Last := Coverage[I];
- V := Abs(PolyFloor(Coverage[I] * $10000)); // TODO : Is Floor the correct operator here?
- V := V and $01ffff;
- if V >= $10000 then V := V xor $1ffff;
- V := V * M shr 24;
- C.A := V;
- end;
- AlphaValues[I] := Color;
- end;
- end;
- {$IFDEF UseStackAlloc}{$W+}{$ENDIF}
- procedure TPolygonRenderer32VPR2.DrawBitmap;
- const
- FillProcs: array [TPolyFillMode] of TFillProc = (MakeAlphaEvenOddUP, MakeAlphaNonZeroUP);
- var
- I, N: Integer;
- Dst: PColor32Array;
- Src: PFloatArray;
- P: PIntSpan;
- FillProc: TFillProc;
- FG: PColor32Array;
- begin
- {$IFDEF UseStackAlloc}
- FG := StackAlloc(Bitmap.Width * SizeOf(TColor32));
- {$ELSE}
- GetMem(FG, Bitmap.Width * SizeOf(TColor32));
- {$ENDIF}
- FillProc := FillProcs[FillMode];
- FYSpan.Max := Min(FYSpan.Max, Bitmap.Height - 1);
- Assert(FYSpan.Min >= 0);
- Assert(FYSpan.Max < Bitmap.Height);
- for I := FYSpan.Min to FYSpan.Max do
- begin
- P := @FXSpan[I];
- P.Max := Min(P.Max + 1, Bitmap.Width - 1);
- if P.Max < P.Min then Continue;
- N := P.Max - P.Min + 1;
- Dst := Bitmap.Scanline[I];
- Src := PFloatArray(FOpacityMap.ValPtr[0, I]);
- // 1. Cumulative sum
- CumSum(@Src[P.Min], N);
- // 2. Convert opacity to colors
- FillProc(@Src[P.Min], @FG[P.Min], N, Color);
- // 3. Blend colors
- BlendLine(@FG[P.Min], @Dst[P.Min], N);
- // 4. Clear opacity map
- FillLongWord(Src[P.Min], N, 0);
- end;
- {$IFDEF UseStackAlloc}
- StackFree(FG);
- {$ELSE}
- FreeMem(FG);
- {$ENDIF}
- end;
- {$IFDEF UseStackAlloc}{$W-}{$ENDIF}
- {$ifdef FPC}
- type
- TRoundingMode = Math.TFPURoundingMode;
- {$endif}
- procedure TPolygonRenderer32VPR2.PolyPolygonFS(
- const Points: TArrayOfArrayOfFloatPoint; const ClipRect: TFloatRect);
- var
- APoints: TArrayOfFloatPoint;
- I, J, H: Integer;
- R: TFloatRect;
- {$if not defined(USE_POLYFLOOR)}
- SavedRoundingMode: TRoundingMode;
- {$ifend}
- begin
- FYSpan := STARTSPAN;
- {$if not defined(USE_POLYFLOOR)}
- SavedRoundingMode := SetRoundMode(rmDown);
- try
- {$ifend}
- FOpacityMap.SetSize(Bitmap.Width + 1, Bitmap.Height);
- // temporary fix for floating point rounding errors
- R := ClipRect;
- R.Right := R.Right - 0.0001;
- R.Bottom := R.Bottom - 0.0001;
- SetLength(FXSpan, Bitmap.Height);
- for I := 0 to High(FXSpan) do
- FXSpan[I] := STARTSPAN;
- for I := 0 to High(Points) do
- begin
- APoints := ClipPolygon(Points[I], R);
- H := High(APoints);
- if H <= 0 then Continue;
- for J := 0 to H - 1 do
- AddLineSegment(APoints[J].X, APoints[J].Y, APoints[J + 1].X, APoints[J + 1].Y);
- AddLineSegment(APoints[H].X, APoints[H].Y, APoints[0].X, APoints[0].Y);
- end;
- DrawBitmap;
- {$if not defined(USE_POLYFLOOR)}
- finally
- SetRoundMode(SavedRoundingMode);
- end
- {$ifend}
- end;
- //============================================================================//
- procedure TPolygonRenderer32VPR2X.AddLineSegment(X1, Y1, X2, Y2: TFixed);
- type
- PFixedArray = ^TFixedArray;
- TFixedArray = array [0..1] of TFixed;
- const
- SGN: array [0..1] of Integer = (1, -1);
- var
- Dx, Dy, DyDx, DxDy, t, tX, tY, Xm, Ym, Xn, Yn: TFixed;
- X, Y, StepX, StepY: Integer;
- P: PFixedArray;
- procedure AddSegment(X1, Y1, X2, Y2: TFixed);
- var
- Dx, Dy: TFixed;
- begin
- Dx := (X1 + X2) shr 1;
- Dx := Dx and $ffff;
- Dy := Y2 - Y1;
- Dx := FixedMul(Dx, Dy);
- P[0] := P[0] + Dy - Dx;
- P[1] := P[1] + Dx;
- end;
- begin
- Dx := X2 - X1;
- Dy := Y2 - Y1;
- if Dy = 0 then Exit;
- X := FixedFloor(X1);
- Y := FixedFloor(Y1);
- UpdateSpan(FYSpan, Y);
- StepX := Ord(Dx < 0);
- StepY := Ord(Dy < 0);
- X1 := X1 - StepX * FixedOne;
- Y1 := Y1 - StepY * FixedOne;
- X2 := X2 - StepX * FixedOne;
- Y2 := Y2 - StepY * FixedOne;
- StepX := SGN[StepX];
- StepY := SGN[StepY];
- if Dx = 0 then
- begin
- Yn := Y1;
- repeat
- UpdateSpan(FXSpan[Y], X);
- P := PFixedArray(FOpacityMap.ValPtr[X, Y]);
- Ym := Yn;
- Inc(Y, StepY);
- Yn := Y * FixedOne;
- AddSegment(X1, Ym, X1, Yn);
- until Abs(Y1 - Yn) >= Abs(Dy);
- AddSegment(X1, Yn, X1, Y2);
- end
- else
- begin
- DyDx := FixedDiv(Dy, Dx);
- DxDy := FixedDiv(Dx, Dy);
- tX := (X + StepX) * FixedOne - X1;
- tY := FixedMul((Y + StepY) * FixedOne - Y1, DxDy);
- Xn := X1;
- Yn := Y1;
- repeat
- Xm := Xn;
- Ym := Yn;
- UpdateSpan(FXSpan[Y], X);
- P := PFixedArray(FOpacityMap.ValPtr[X, Y]);
- if Abs(tX) <= Abs(tY) then
- begin
- Inc(X, StepX);
- t := tX;
- tX := tX + StepX*FixedOne;
- end
- else
- begin
- Inc(Y, StepY);
- t := tY;
- tY := tY + StepY * DxDy;
- end;
- Xn := X1 + t;
- Yn := Y1 + FixedMul(t, DyDx);
- AddSegment(Xm, Ym, Xn, Yn);
- until Abs(t) >= Abs(Dx);
- AddSegment(Xn, Yn, X2, Y2);
- end;
- end;
- procedure CumSumX(PSrc: PFixedArray; N: Integer);
- var
- I: Integer;
- begin
- for I := 1 to N - 1 do
- Inc(PSrc[I], PSrc[I - 1]);
- end;
- procedure MakeAlphaNonZeroUPX(Coverage: PFixedArray; AlphaValues: PColor32Array;
- Count: Integer; Color: TColor32);
- var
- I, V, M, Last: Integer;
- C: TColor32Entry absolute Color;
- begin
- M := C.A * $101;
- Last := MaxInt;
- for I := 0 to Count - 1 do
- begin
- if Last <> Coverage[I] then
- begin
- V := Abs(Coverage[I]);
- if V > $ffff then V := $ffff;
- V := V * M shr 24;
- C.A := V;
- end;
- AlphaValues[I] := Color;
- end;
- end;
- procedure MakeAlphaEvenOddUPX(Coverage: PFixedArray; AlphaValues: PColor32Array;
- Count: Integer; Color: TColor32);
- var
- I, V, M, Last: Integer;
- C: TColor32Entry absolute Color;
- begin
- M := C.A * $101;
- Last := MaxInt;
- for I := 0 to Count - 1 do
- begin
- if Last <> Coverage[I] then
- begin
- V := Abs(Coverage[I]);
- V := V and $01ffff;
- if V >= $10000 then V := V xor $1ffff;
- V := V * M shr 24;
- C.A := V;
- end;
- AlphaValues[I] := Color;
- end;
- end;
- {$IFDEF UseStackAlloc}{$W+}{$ENDIF}
- procedure TPolygonRenderer32VPR2X.DrawBitmap;
- type
- TFillProcX = procedure(Coverage: PFixedArray; AlphaValues: PColor32Array; Count: Integer; Color: TColor32);
- const
- FillProcs: array [TPolyFillMode] of TFillProcX = (MakeAlphaEvenOddUPX, MakeAlphaNonZeroUPX);
- var
- I, N: Integer;
- Dst: PColor32Array;
- Src: PFixedArray;
- P: PIntSpan;
- FillProc: TFillProcX;
- FG: PColor32Array;
- begin
- {$IFDEF UseStackAlloc}
- FG := StackAlloc(Bitmap.Width * SizeOf(TColor32));
- {$ELSE}
- GetMem(FG, Bitmap.Width * SizeOf(TColor32));
- {$ENDIF}
- FillProc := FillProcs[FillMode];
- FYSpan.Max := Min(FYSpan.Max, Bitmap.Height - 1);
- Assert(FYSpan.Min >= 0);
- Assert(FYSpan.Max < Bitmap.Height);
- for I := FYSpan.Min to FYSpan.Max do
- begin
- P := @FXSpan[I];
- P.Max := Min(P.Max + 1, Bitmap.Width - 1);
- if P.Max < P.Min then Continue;
- N := P.Max - P.Min + 1;
- Dst := Bitmap.Scanline[I];
- Src := PFixedArray(FOpacityMap.ValPtr[0, I]);
- // 1. Cumulative sum
- CumSumX(@Src[P.Min], N);
- // 2. Convert opacity to colors
- FillProc(@Src[P.Min], @FG[P.Min], N, Color);
- // 3. Blend colors
- BlendLine(@FG[P.Min], @Dst[P.Min], N);
- // 4. Clear opacity map
- FillLongWord(Src[P.Min], N, 0);
- end;
- {$IFDEF UseStackAlloc}
- StackFree(FG);
- {$ELSE}
- FreeMem(FG);
- {$ENDIF}
- end;
- {$IFDEF UseStackAlloc}{$W-}{$ENDIF}
- procedure TPolygonRenderer32VPR2X.PolyPolygonFS(
- const Points: TArrayOfArrayOfFloatPoint; const ClipRect: TFloatRect);
- var
- APoints: TArrayOfFloatPoint;
- I, J, H: Integer;
- R: TFloatRect;
- begin
- FYSpan := STARTSPAN;
- FOpacityMap.SetSize(Bitmap.Width + 1, Bitmap.Height);
- // temporary fix for floating point rounding errors
- R := ClipRect;
- {$ifndef FPC}
- R.Inflate(-0.05, -0.05);
- {$else}
- GR32.InflateRect(R, -0.05, -0.05);
- {$endif}
- SetLength(FXSpan, Bitmap.Height);
- for I := 0 to High(FXSpan) do
- FXSpan[I] := STARTSPAN;
- for I := 0 to High(Points) do
- begin
- APoints := ClipPolygon(Points[I], R);
- H := High(APoints);
- if H <= 0 then Continue;
- for J := 0 to H - 1 do
- AddLineSegment(Fixed(APoints[J].X), Fixed(APoints[J].Y), Fixed(APoints[J + 1].X), Fixed(APoints[J + 1].Y));
- AddLineSegment(Fixed(APoints[H].X), Fixed(APoints[H].Y), Fixed(APoints[0].X), Fixed(APoints[0].Y));
- end;
- DrawBitmap;
- end;
- constructor TPolygonRenderer32VPR2X.Create;
- begin
- inherited Create;
- FOpacityMap := TIntegerMap.Create;
- end;
- destructor TPolygonRenderer32VPR2X.Destroy;
- begin
- FOpacityMap.Free;
- inherited Destroy;
- end;
- initialization
- RegisterPolygonRenderer(TPolygonRenderer32VPR2);
- // TPolygonRenderer32VPR2X has been disabled as it's incomplete.
- // It causes AVs - and always have.
- // RegisterPolygonRenderer(TPolygonRenderer32VPR2X);
- end.
|