GR32_VPR.pas 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762
  1. unit GR32_VPR;
  2. (* ***** BEGIN LICENSE BLOCK *****
  3. * Version: MPL 1.1 or LGPL 2.1 with linking exception
  4. *
  5. * The contents of this file are subject to the Mozilla Public License Version
  6. * 1.1 (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. * http://www.mozilla.org/MPL/
  9. *
  10. * Software distributed under the License is distributed on an "AS IS" basis,
  11. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. * for the specific language governing rights and limitations under the
  13. * License.
  14. *
  15. * Alternatively, the contents of this file may be used under the terms of the
  16. * Free Pascal modified version of the GNU Lesser General Public License
  17. * Version 2.1 (the "FPC modified LGPL License"), in which case the provisions
  18. * of this license are applicable instead of those above.
  19. * Please see the file LICENSE.txt for additional information concerning this
  20. * license.
  21. *
  22. * The Original Code is Vectorial Polygon Rasterizer for Graphics32
  23. *
  24. * The Initial Developer of the Original Code is
  25. * Mattias Andersson <[email protected]>
  26. *
  27. * Portions created by the Initial Developer are Copyright (C) 2008-2012
  28. * the Initial Developer. All Rights Reserved.
  29. *
  30. * ***** END LICENSE BLOCK ***** *)
  31. interface
  32. {$include GR32.inc}
  33. uses
  34. GR32;
  35. type
  36. PSingleArray = GR32.PSingleArray;
  37. TValueSpan = record
  38. LowX, HighX: Integer;
  39. Values: PSingleArray;
  40. end;
  41. TRenderSpanEvent = procedure(const Span: TValueSpan; DstY: Integer) of object;
  42. TRenderSpanProc = procedure(Data: Pointer; const Span: TValueSpan; DstY: Integer);
  43. procedure RenderPolyPolygon(const Points: TArrayOfArrayOfFloatPoint;
  44. const ClipRect: TFloatRect; const RenderProc: TRenderSpanProc; Data: Pointer = nil); overload;
  45. procedure RenderPolygon(const Points: TArrayOfFloatPoint;
  46. const ClipRect: TFloatRect; const RenderProc: TRenderSpanProc; Data: Pointer = nil); overload;
  47. procedure RenderPolyPolygon(const Points: TArrayOfArrayOfFloatPoint;
  48. const ClipRect: TFloatRect; const RenderProc: TRenderSpanEvent); overload;
  49. procedure RenderPolygon(const Points: TArrayOfFloatPoint;
  50. const ClipRect: TFloatRect; const RenderProc: TRenderSpanEvent); overload;
  51. implementation
  52. {$if defined(FPC) and defined(CPUx86_64) }
  53. // Must apply work around for negative array index on FPC 64-bit.
  54. // See:
  55. // - https://github.com/graphics32/graphics32/issues/51
  56. // - https://forum.lazarus.freepascal.org/index.php/topic,44655.0.html
  57. {$define NEGATIVE_INDEX_64}
  58. {$ifend}
  59. uses
  60. Math,
  61. GR32_Math,
  62. GR32_LowLevel,
  63. GR32_VectorUtils;
  64. // FastFloor is slow on x86 due to call overhead
  65. {$if (not defined(PUREPASCAL)) and defined(CPUx86_64)}
  66. // Use of FastFloor in VPR currently corrupts the memory manager of FPC
  67. // so temporarily disabled there.
  68. {$if (not defined(FPC))}
  69. {$define USE_POLYFLOOR}
  70. {$ifend}
  71. {$ifend}
  72. function PolyFloor(Value: Single): integer; overload; {$ifndef DEBUG} inline; {$endif}
  73. begin
  74. {$if defined(USE_POLYFLOOR)}
  75. Result := FastFloorSingle(Value);
  76. {$else}
  77. Result := Round(Value);
  78. {$ifend}
  79. end;
  80. function PolyFloor(Value: Double): integer; overload; {$ifndef DEBUG} inline; {$endif}
  81. begin
  82. {$if defined(USE_POLYFLOOR)}
  83. Result := FastFloorDouble(Value);
  84. {$else}
  85. Result := Round(Value);
  86. {$ifend}
  87. end;
  88. function PolyCeil(Value: Single): integer; overload; {$ifndef DEBUG} inline; {$endif}
  89. begin
  90. {$if defined(USE_POLYFLOOR)}
  91. Result := FastCeilSingle(Value);
  92. {$else}
  93. Result := -Round(-Value);
  94. {$ifend}
  95. end;
  96. function PolyCeil(Value: Double): integer; overload; {$ifndef DEBUG} inline; {$endif}
  97. begin
  98. {$if defined(USE_POLYFLOOR)}
  99. Result := FastCeilDouble(Value);
  100. {$else}
  101. Result := -Round(-Value);
  102. {$ifend}
  103. end;
  104. (* Mattias Andersson (from [email protected]):
  105. > Which algorithm are you using for coverage calculation?
  106. I don't have any references, since it's entirely my own design. Here is
  107. a basic outline of how I compute the coverage values:
  108. 1. split each line segment into smaller segments in a vertical buffer,
  109. such that y-values are between 0 and 1;
  110. 2. poly-polygons involves a merge step for vertical buffers;
  111. 3. Extract spans of coverage values for each scanline:
  112. (a) set the length of the span to the horizontal range of that row;
  113. (b) if a line segment goes from row Y to row Y + 1 then we need to add
  114. or subtract 1 from the (X, X + 1) indexes at the crossing (depending on
  115. line orientation);
  116. (c) compute cumulative sum of span values (expensive!);
  117. (d) integrate each line segment and accumulate span buffer.
  118. The rendering step takes the coverage values and transforms that into an
  119. alpha buffer that is blended onto the target bitmap (here we use the
  120. non-zero and even-odd fill rules).
  121. Initially I was sorting the crossing points of each scanline, but I
  122. realized that by performing a cumulative sum, this would be completely
  123. redundant.
  124. Currently I only compute a single span of coverage values for each
  125. scanline, but I think I should also implement a case where I compute
  126. multiple RLE encoded spans (which I think could be faster in some cases).
  127. There is a tricky case that might not always yield an accurate coverage
  128. value (when we have positively and negatively oriented lines of two
  129. different polygons/faces in the same pixel). The only way to overcome
  130. this would be by preprocessing the polygons and remove intersections. I
  131. believe this very problem exists in AGG and FreeType too.
  132. *)
  133. type
  134. PLineSegment = ^TLineSegment;
  135. TLineSegment = array [0..1] of TFloatPoint;
  136. PLineSegmentArray = ^TLineSegmentArray;
  137. TLineSegmentArray = array [0..0] of TLineSegment;
  138. TScanLine = record
  139. Segments: PLineSegmentArray;
  140. Count: Integer;
  141. Y: Integer;
  142. end;
  143. TScanLines = array of TScanLine;
  144. PScanLineArray = ^TScanLineArray;
  145. TScanLineArray = array [0..0] of TScanLine;
  146. procedure IntegrateSegment(const P1, P2: TFloatPoint; Values: PSingleArray);
  147. var
  148. {$if defined(NEGATIVE_INDEX_64) }
  149. X1, X2: Int64;
  150. {$else}
  151. X1, X2: Integer;
  152. {$ifend}
  153. i: Integer;
  154. Dx, Dy, DyDx, Y: TFloat;
  155. fracX1, fracX2: TFloat;
  156. begin
  157. (*
  158. ** We have a line segment going from (X1,Y1) to (X2,Y2):
  159. **
  160. ** X1 X2
  161. ** +---------
  162. ** Y1 | *
  163. ** | *
  164. ** | *
  165. ** | *
  166. ** | *
  167. ** | *
  168. ** Y2 | *
  169. **
  170. ** The Y values in the segment belongs to a single scanline so the line segment is 1 pixel high.
  171. ** Additionally, we know that the Y values are in the range [0..1].
  172. ** In the example below, we have a segment where X2-X1=6. Each box is a pixel.
  173. **
  174. ** X1 X2
  175. ** +---+---+---+---+---+---+---+
  176. ** Y1 | * | | | | | | |
  177. ** | | * | | | | | |
  178. ** | | | * | | | | |
  179. ** | | | | * | | | |
  180. ** | | | | | * | | |
  181. ** | | | | | | * | |
  182. ** Y2 | | | | | | | * |
  183. ** +---+---+---+---+---+---+---+
  184. **
  185. ** For each X, we need to calculate the area below (or above) the line segment.
  186. ** We do this by calculating the slope of the line, and from that we can find the Y value
  187. ** given an X value.
  188. ** Once we have an X and an Y value we calculate the area as X*Y/2.
  189. **
  190. ** X1 X2
  191. ** +---+---+---+---+---+---+---+
  192. ** Y1 | * | | | | | | |
  193. ** | * | * | | | | | |
  194. ** | * | * | * | | | | |
  195. ** | * | * | * | * | | | |
  196. ** | * | * | * | * | * | | |
  197. ** | * | * | * | * | * | * | |
  198. ** Y2 | * | * | * | * | * | * | * |
  199. ** +---+---+---+---+---+---+---+
  200. **
  201. *)
  202. X1 := PolyFloor(P1.X);
  203. X2 := PolyFloor(P2.X);
  204. // Vertical segment (within one pixel)
  205. if X1 = X2 then
  206. begin
  207. Values[X1] := Values[X1] + 0.5 * (P2.X - P1.X) * (P1.Y + P2.Y);
  208. end else
  209. // Everything else
  210. begin
  211. Dx := P2.X - P1.X;
  212. Dy := P2.Y - P1.Y;
  213. DyDx := Dy/Dx; // For each X, how much does Y increment
  214. if X1 < X2 then
  215. begin
  216. fracX1 := 1 - (P1.X - X1);
  217. fracX2 := P2.X - X2;
  218. Y := P1.Y + fracX1 * DyDx;
  219. // First fractional X (fracX1..1)
  220. Values[X1] := Values[X1] + 0.5 * (P1.Y + Y) * fracX1;
  221. // Whole Xs (1..1)
  222. for i := X1 + 1 to X2 - 1 do
  223. begin
  224. Values[i] := Values[i] + (Y + DyDx * 0.5); // N: Sx = 1
  225. Y := Y + DyDx;
  226. end;
  227. // Last fractional X (1..fracX2)
  228. Values[X2] := Values[X2] + 0.5 * (Y + P2.Y) * fracX2;
  229. end else // X1 > X2
  230. begin
  231. fracX1 := P1.X - X1;
  232. fracX2 := 1 - (P2.X - X2);
  233. Y := P1.Y - fracX1 * DyDx;
  234. // First fractional X (fracX1..1)
  235. Values[X1] := Values[X1] - 0.5 * (P1.Y + Y) * fracX1;
  236. // Whole Xs (1..1)
  237. for i := X1 - 1 downto X2 + 1 do
  238. begin
  239. Values[i] := Values[i] - (Y - DyDx * 0.5); // N: Sx = -1
  240. Y := Y - DyDx;
  241. end;
  242. // Last fractional X (1..fracX2)
  243. Values[X2] := Values[X2] - 0.5 * (Y + P2.Y) * fracX2;
  244. end;
  245. end;
  246. end;
  247. procedure ExtractSingleSpan(const ScanLine: TScanLine; out Span: TValueSpan; SpanData: PSingleArray);
  248. var
  249. i: Integer;
  250. {$if defined(NEGATIVE_INDEX_64) }
  251. X: Int64;
  252. {$else}
  253. X: Integer;
  254. {$ifend}
  255. P: PFloatPoint;
  256. Segment: PLineSegment;
  257. fracX: TFloat;
  258. Points: PFloatPointArray;
  259. N: Integer;
  260. begin
  261. (*
  262. ** Extract spans of coverage values for a scanline.
  263. **
  264. ** We do this by looking at the scanline segments. Each segment indicates
  265. ** where on the X-axis the line, that the segment was extracted from,
  266. ** crosses the scanline.
  267. **
  268. ** At the point where the line crosses, we update the coverage value.
  269. ** For example, four crossings could produce the following coverage values:
  270. ** [ 1 -1 1 -1 ]
  271. ** Note that the actual coverage values will be [0..1].
  272. **
  273. ** When all segments has been processed like this, we convert the values
  274. ** to a sequence of values using the CumSum function:
  275. ** [ 11111111 111111 ]
  276. **
  277. *)
  278. N := ScanLine.Count * 2; // Pairs of TFloatPoint, so double the count
  279. Points := @ScanLine.Segments[0];
  280. // Low/High bound of span
  281. Span.LowX := High(Integer);
  282. Span.HighX := Low(Integer);
  283. (*
  284. ** (a) set the length of the span to the horizontal range of that row.
  285. **
  286. ** (b) if a line segment goes from row Y to row Y + 1 then we need to add
  287. ** or subtract 1 from the (X, X + 1) indexes at the crossing (depending on
  288. ** line orientation).
  289. *)
  290. P := @Points[0];
  291. for i := 0 to N - 1 do
  292. begin
  293. // Since we know X >= 0 we could have used Trunc here but unfortunately
  294. // Delphi's Trunc is much slower than Round because it modifies the FPU
  295. // control word.
  296. // Note: We're using FastFloor now so the above comment is no longer relevant.
  297. X := PolyFloor(P.X);
  298. // (a1) Find the lower bound of the horizontal span
  299. if X < Span.LowX then
  300. Span.LowX := X;
  301. // (b) if a line segment goes from row Y to row Y + 1 then...
  302. if P.Y = 1 then
  303. begin
  304. fracX := P.X - X;
  305. if Odd(i) then
  306. begin // Right edge
  307. SpanData[X] := SpanData[X] + (1 - fracX);
  308. Inc(X);
  309. SpanData[X] := SpanData[X] + fracX;
  310. end else
  311. begin // Left edge
  312. SpanData[X] := SpanData[X] - (1 - fracX);
  313. Inc(X);
  314. SpanData[X] := SpanData[X] - fracX;
  315. end;
  316. end;
  317. // (a2) Find the upper bound of the horizontal span
  318. if X > Span.HighX then
  319. Span.HighX := X;
  320. inc(P);
  321. end;
  322. (*
  323. ** (c) compute cumulative sum of span values.
  324. *)
  325. X := Span.LowX; // Use X so NEGATIVE_INDEX_64 is handled
  326. Span.Values := @SpanData[X];
  327. CumSum(Span.Values, Span.HighX - Span.LowX + 1);
  328. (*
  329. ** (d) integrate each line segment and accumulate span buffer.
  330. *)
  331. for i := 0 to ScanLine.Count - 1 do
  332. begin
  333. Segment := @ScanLine.Segments[i];
  334. IntegrateSegment(Segment[0], Segment[1], SpanData);
  335. end;
  336. end;
  337. procedure AddSegment(const X1, Y1, X2, Y2: TFloat; var ScanLine: TScanLine);// {$IFDEF USEINLINING} inline; {$ENDIF}
  338. var
  339. S: PLineSegment;
  340. Y1bin: Cardinal absolute Y1;
  341. Y2bin: Cardinal absolute Y2;
  342. begin
  343. // Fast way of checking a Single = 0.
  344. if (Y1bin shl 1 = 0) and (Y2bin shl 1 = 0) then
  345. // if (Y1 = 0) and (Y2 = 0) then
  346. Exit; { needed for proper clipping }
  347. // Add segment to the scanline's list of segments
  348. S := @ScanLine.Segments[ScanLine.Count];
  349. Inc(ScanLine.Count);
  350. S[0].X := X1;
  351. S[0].Y := Y1;
  352. S[1].X := X2;
  353. S[1].Y := Y2;
  354. end;
  355. procedure DivideSegment(var P1, P2: TFloatPoint; const ScanLines: PScanLineArray);
  356. var
  357. Y, Y1, Y2: Integer;
  358. X, X2: TFloat;
  359. k: TFloat;
  360. n: TFloat;
  361. begin
  362. (*
  363. ** Split each line segment into smaller segments in a vertical buffer,
  364. ** such that y-values are between 0 and 1.
  365. *)
  366. Y1 := PolyFloor(P1.Y);
  367. Y2 := PolyFloor(P2.Y);
  368. // Special case for horizontal line; It just produces a single segment.
  369. if Y1 = Y2 then // TODO : Should also handle "Y1 almost equal Y2" ?
  370. begin
  371. AddSegment(P1.X, P1.Y - Y1, P2.X, P2.Y - Y1, ScanLines[Y1]);
  372. end else
  373. begin
  374. // k: Inverse slope; For each change in Y, how much does X change
  375. // k is expanded below to limit rounding errors.
  376. k := (P2.X - P1.X) / (P2.Y - P1.Y);
  377. // TODO : We should also special case "P1.X almost equal P2.X" ?
  378. if Y1 < Y2 then // Y is increasing
  379. begin
  380. X := P1.X + (Y1 + 1 - P1.Y) * { k } (P2.X - P1.X) / (P2.Y - P1.Y);
  381. // First fractional scanline (n..1)
  382. n := P1.Y - Y1;
  383. AddSegment(P1.X, n, X, 1, ScanLines[Y1]);
  384. // Whole scanlines (0..1)
  385. for Y := Y1 + 1 to Y2 - 1 do
  386. begin
  387. // Note: Iteratively calculating the next X value based on the previous value and an
  388. // increment accumulates the rounding error.
  389. // Ideally we would repeat the calculation of X from Y for each Y to avoid this but
  390. // that is too expensive.
  391. // Because of the rounding error we can end up with a tiny negative X value (when X
  392. // almost equals k) and, because we've set the rounding mode to rmDown, this negative
  393. // X value will later be rounded down to -1 in ExtractSingleSpan.
  394. // This is the cause of issue #272.
  395. // The Max(0, ...) below works around this problem.
  396. X2 := Max(0, X + k);
  397. AddSegment(X, 0, X2, 1, ScanLines[Y]);
  398. X := X2;
  399. end;
  400. // Last fractional scanline (0..n)
  401. n := P2.Y - Y2;
  402. AddSegment(X, 0, P2.X, n, ScanLines[Y2]);
  403. end else
  404. begin
  405. X := P1.X + (Y1 - P1.Y) * { k } (P2.X - P1.X) / (P2.Y - P1.Y);
  406. // First fractional scanline (n..0)
  407. n := P1.Y - Y1;
  408. AddSegment(P1.X, n, X, 0, ScanLines[Y1]);
  409. // Whole scanlines (1..0)
  410. for Y := Y1 - 1 downto Y2 + 1 do
  411. begin
  412. X2 := Max(0, X - k);
  413. AddSegment(X, 1, X2, 0, ScanLines[Y]);
  414. X := X2;
  415. end;
  416. // Last fractional scanline (1..n)
  417. n := P2.Y - Y2;
  418. AddSegment(X, 1, P2.X, n, ScanLines[Y2]);
  419. end;
  420. end;
  421. end;
  422. procedure BuildScanLines(const Points: TArrayOfArrayOfFloatPoint;
  423. out ScanLines: TScanLines);
  424. var
  425. PolygonIndex, MaxPolygon, MaxVertex: Integer;
  426. i, Y0,Y1,Y, YMin,YMax: Integer;
  427. SegmentCount: Integer;
  428. pY: PSingle;
  429. pPoint1, PPoint2: PFloatPoint;
  430. pScanLines: PScanLineArray;
  431. begin
  432. (*
  433. ** Determine range of Y values (i.e. number of scanlines)
  434. *)
  435. YMin := MaxInt;
  436. YMax := -MaxInt;
  437. MaxPolygon := High(Points);
  438. for PolygonIndex := 0 to MaxPolygon do
  439. begin
  440. MaxVertex := High(Points[PolygonIndex]);
  441. if MaxVertex < 2 then
  442. Continue;
  443. pY := @Points[PolygonIndex][0].Y;
  444. for i := 0 to MaxVertex do
  445. begin
  446. Y := PolyFloor(pY^);
  447. if YMin > Y then
  448. YMin := Y;
  449. if YMax < Y then
  450. YMax := Y;
  451. inc(PFloatPoint(pY)); // skips X value
  452. end;
  453. end;
  454. if YMin > YMax then
  455. Exit;
  456. SetLength(ScanLines, YMax - YMin + 2);
  457. // Offset scanline pointer so we don't have to offset the Y coordinate
  458. pScanLines := @ScanLines[-YMin];
  459. (*
  460. ** Compute array sizes for each scanline
  461. *)
  462. // For each polygon...
  463. for PolygonIndex := 0 to MaxPolygon do
  464. begin
  465. MaxVertex := High(Points[PolygonIndex]);
  466. if MaxVertex < 2 then
  467. Continue; // No line segments in this polygon
  468. // Start with the line segment going from the last vertex to the first
  469. Y0 := PolyFloor(Points[PolygonIndex][MaxVertex].Y);
  470. pY := @Points[PolygonIndex][0].Y;
  471. // For each line of the polygon...
  472. for i := 0 to MaxVertex do
  473. begin
  474. // Calculate the max fragment count; Start of line vertex increments
  475. // the running fragment count for the start scanline and the end of
  476. // line vertex decrements the running fragment count for the end
  477. // scanline.
  478. //
  479. // Polygon Scanline Lines(Y0, Y1) Count Sum
  480. // (4, 0) (0, 2) (2, 1) (1, 3) (3, 7) (7, 4)
  481. //
  482. // * 0 1 1 2 2
  483. // /\ 1 1 1 2 4
  484. // / \/\ 2 0 4
  485. // / \ 3 -1 -1 1 -1 3
  486. // / / 4 -1 1 0 3
  487. // \ / 5 -1 -1 2
  488. // \ / 6 0 2
  489. // \ / 7 0 2
  490. // \/ 8 -1 -1 -2 0
  491. //
  492. Y1 := PolyFloor(pY^);
  493. // Line has positive slope
  494. if Y0 <= Y1 then
  495. begin
  496. Inc(pScanLines[Y0].Count);
  497. Dec(pScanLines[Y1 + 1].Count);
  498. end
  499. else
  500. // Line has negative slope
  501. begin
  502. Inc(pScanLines[Y1].Count);
  503. Dec(pScanLines[Y0 + 1].Count);
  504. end;
  505. // Move to next line
  506. Y0 := Y1;
  507. inc(PFloatPoint(pY)); // skips X value
  508. end;
  509. end;
  510. (*
  511. ** Allocate memory
  512. *)
  513. SegmentCount := 0;
  514. for i := 0 to High(ScanLines) do
  515. begin
  516. // Adjust running fragment count
  517. Inc(SegmentCount, ScanLines[i].Count);
  518. GetMem(ScanLines[i].Segments, SegmentCount * SizeOf(TLineSegment));
  519. ScanLines[i].Count := 0;
  520. ScanLines[i].Y := YMin + i;
  521. end;
  522. (*
  523. ** Divide all segments of the polygon into scanline fragments
  524. *)
  525. for PolygonIndex := 0 to MaxPolygon do
  526. begin
  527. MaxVertex := High(Points[PolygonIndex]);
  528. if MaxVertex < 2 then
  529. Continue;
  530. // Start with the line segment going from the last vertex to the first
  531. pPoint1 := @Points[PolygonIndex][MaxVertex];
  532. PPoint2 := @Points[PolygonIndex][0];
  533. for i := 0 to MaxVertex do
  534. begin
  535. DivideSegment(pPoint1^, PPoint2^, pScanLines);
  536. // Move on to the next segment
  537. pPoint1 := PPoint2;
  538. Inc(PPoint2);
  539. end;
  540. end;
  541. end;
  542. procedure RenderScanline(var ScanLine: TScanLine;
  543. RenderProc: TRenderSpanProc; Data: Pointer; SpanData: PSingleArray; ClipX1, ClipX2: Integer);
  544. var
  545. Span: TValueSpan;
  546. {$if defined(NEGATIVE_INDEX_64) }
  547. X: Int64;
  548. {$else}
  549. X: Integer;
  550. {$ifend}
  551. begin
  552. if ScanLine.Count = 0 then
  553. exit;
  554. ExtractSingleSpan(ScanLine, Span, SpanData);
  555. // Clip
  556. if Span.LowX < ClipX1 then
  557. Span.LowX := ClipX1;
  558. if Span.HighX > ClipX2 then
  559. Span.HighX := ClipX2;
  560. if Span.HighX < Span.LowX then
  561. Exit;
  562. RenderProc(Data, Span, ScanLine.Y);
  563. X := Span.LowX;
  564. FillLongWord(SpanData[X], Span.HighX - Span.LowX + 1, 0);
  565. end;
  566. {$ifdef FPC}
  567. type
  568. TRoundingMode = Math.TFPURoundingMode;
  569. {$endif}
  570. procedure RenderPolyPolygon(const Points: TArrayOfArrayOfFloatPoint;
  571. const ClipRect: TFloatRect; const RenderProc: TRenderSpanProc; Data: Pointer);
  572. var
  573. ScanLines: TScanLines;
  574. I, Len: Integer;
  575. Poly: TArrayOfArrayOfFloatPoint;
  576. CX1, CX2: Integer;
  577. SpanData: PSingleArray;
  578. {$if not defined(USE_POLYFLOOR)}
  579. SavedRoundingMode: TRoundingMode;
  580. {$ifend}
  581. begin
  582. Len := Length(Points);
  583. if Len = 0 then
  584. Exit;
  585. SetLength(Poly, Len);
  586. for i := 0 to Len -1 do
  587. Poly[i] := ClipPolygon(Points[i], ClipRect);
  588. {$if not defined(USE_POLYFLOOR)}
  589. SavedRoundingMode := SetRoundMode(rmDown);
  590. try
  591. {$ifend}
  592. BuildScanLines(Poly, ScanLines);
  593. if (Length(ScanLines) > 0) then
  594. begin
  595. CX1 := PolyFloor(ClipRect.Left);
  596. CX2 := PolyCeil(ClipRect.Right) - 1;
  597. I := CX2 - CX1 + 4;
  598. GetMem(SpanData, I * SizeOf(Single));
  599. FillLongWord(SpanData^, I, 0);
  600. for I := 0 to High(ScanLines) do
  601. begin
  602. RenderScanline(ScanLines[I], RenderProc, Data, @SpanData[-CX1 + 1], CX1, CX2);
  603. FreeMem(ScanLines[I].Segments);
  604. end;
  605. FreeMem(SpanData);
  606. end;
  607. {$if not defined(USE_POLYFLOOR)}
  608. finally
  609. SetRoundMode(SavedRoundingMode);
  610. end
  611. {$ifend}
  612. end;
  613. procedure RenderPolygon(const Points: TArrayOfFloatPoint;
  614. const ClipRect: TFloatRect; const RenderProc: TRenderSpanProc; Data: Pointer);
  615. begin
  616. RenderPolyPolygon(PolyPolygon(Points), ClipRect, RenderProc, Data);
  617. end;
  618. procedure RenderPolyPolygon(const Points: TArrayOfArrayOfFloatPoint;
  619. const ClipRect: TFloatRect; const RenderProc: TRenderSpanEvent);
  620. begin
  621. RenderPolyPolygon(Points, ClipRect, TRenderSpanProc(TMethod(RenderProc).Code), TMethod(RenderProc).Data);
  622. end;
  623. procedure RenderPolygon(const Points: TArrayOfFloatPoint;
  624. const ClipRect: TFloatRect; const RenderProc: TRenderSpanEvent);
  625. begin
  626. RenderPolygon(Points, ClipRect, TRenderSpanProc(TMethod(RenderProc).Code), TMethod(RenderProc).Data);
  627. end;
  628. end.