GR32_VPR.pas 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  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. * Contributor(s):
  31. *
  32. * ***** END LICENSE BLOCK ***** *)
  33. interface
  34. {$I GR32.inc}
  35. uses
  36. GR32;
  37. type
  38. PInteger = ^Integer;
  39. PSingleArray = GR32.PSingleArray;
  40. TSingleArray = GR32.TSingleArray;
  41. PValueSpan = ^TValueSpan;
  42. TValueSpan = record
  43. X1, X2: Integer;
  44. Values: PSingleArray;
  45. end;
  46. TRenderSpanEvent = procedure(const Span: TValueSpan; DstY: Integer) of object;
  47. TRenderSpanProc = procedure(Data: Pointer; const Span: TValueSpan; DstY: Integer);
  48. procedure RenderPolyPolygon(const Points: TArrayOfArrayOfFloatPoint;
  49. const ClipRect: TFloatRect; const RenderProc: TRenderSpanProc; Data: Pointer = nil); overload;
  50. procedure RenderPolygon(const Points: TArrayOfFloatPoint;
  51. const ClipRect: TFloatRect; const RenderProc: TRenderSpanProc; Data: Pointer = nil); overload;
  52. procedure RenderPolyPolygon(const Points: TArrayOfArrayOfFloatPoint;
  53. const ClipRect: TFloatRect; const RenderProc: TRenderSpanEvent); overload;
  54. procedure RenderPolygon(const Points: TArrayOfFloatPoint;
  55. const ClipRect: TFloatRect; const RenderProc: TRenderSpanEvent); overload;
  56. implementation
  57. uses
  58. Math, GR32_Math, GR32_LowLevel, GR32_VectorUtils;
  59. type
  60. TArrayOfValueSpan = array of TValueSpan;
  61. PValueSpanArray = ^TValueSpanArray;
  62. TValueSpanArray = array [0..0] of TValueSpan;
  63. PLineSegment = ^TLineSegment;
  64. TLineSegment = array [0..1] of TFloatPoint;
  65. TArrayOfLineSegment = array of TLineSegment;
  66. PLineSegmentArray = ^TLineSegmentArray;
  67. TLineSegmentArray = array [0..0] of TLineSegment;
  68. PScanLine = ^TScanLine;
  69. TScanLine = record
  70. Segments: PLineSegmentArray;
  71. Count: Integer;
  72. Y: Integer;
  73. end;
  74. TScanLines = array of TScanLine;
  75. PScanLineArray = ^TScanLineArray;
  76. TScanLineArray = array [0..0] of TScanLine;
  77. procedure IntegrateSegment(var P1, P2: TFloatPoint; Values: PSingleArray);
  78. var
  79. X1, X2, I: Integer;
  80. Dx, Dy, DyDx, Sx, Y, fracX1, fracX2: TFloat;
  81. begin
  82. X1 := Round(P1.X);
  83. X2 := Round(P2.X);
  84. if X1 = X2 then
  85. begin
  86. Values[X1] := Values[X1] + 0.5 * (P2.X - P1.X) * (P1.Y + P2.Y);
  87. end
  88. else
  89. begin
  90. fracX1 := P1.X - X1;
  91. fracX2 := P2.X - X2;
  92. Dx := P2.X - P1.X;
  93. Dy := P2.Y - P1.Y;
  94. DyDx := Dy/Dx;
  95. if X1 < X2 then
  96. begin
  97. Sx := 1 - fracX1;
  98. Y := P1.Y + Sx * DyDx;
  99. Values[X1] := Values[X1] + 0.5 * (P1.Y + Y) * Sx;
  100. for I := X1 + 1 to X2 - 1 do
  101. begin
  102. Values[I] := Values[I] + (Y + DyDx * 0.5); // N: Sx = 1
  103. Y := Y + DyDx;
  104. end;
  105. Sx := fracX2;
  106. Values[X2] := Values[X2] + 0.5 * (Y + P2.Y) * Sx;
  107. end
  108. else // X1 > X2
  109. begin
  110. Sx := fracX1;
  111. Y := P1.Y - Sx * DyDx;
  112. Values[X1] := Values[X1] - 0.5 * (P1.Y + Y) * Sx;
  113. for I := X1 - 1 downto X2 + 1 do
  114. begin
  115. Values[I] := Values[I] - (Y - DyDx * 0.5); // N: Sx = -1
  116. Y := Y - DyDx;
  117. end;
  118. Sx := 1 - fracX2;
  119. Values[X2] := Values[X2] - 0.5 * (Y + P2.Y) * Sx;
  120. end;
  121. end;
  122. end;
  123. procedure ExtractSingleSpan(const ScanLine: TScanLine; out Span: TValueSpan;
  124. SpanData: PSingleArray);
  125. var
  126. I, X: Integer;
  127. P: PFloatPoint;
  128. S: PLineSegment;
  129. fracX: TFloat;
  130. Points: PFloatPointArray;
  131. N: Integer;
  132. begin
  133. N := ScanLine.Count * 2;
  134. Points := @ScanLine.Segments[0];
  135. Span.X1 := High(Integer);
  136. Span.X2 := Low(Integer);
  137. for I := 0 to N - 1 do
  138. begin
  139. P := @Points[I];
  140. X := Round(P.X);
  141. if X < Span.X1 then Span.X1 := X;
  142. if P.Y = 1 then
  143. begin
  144. fracX := P.X - X;
  145. if Odd(I) then
  146. begin
  147. SpanData[X] := SpanData[X] + (1 - fracX); Inc(X);
  148. SpanData[X] := SpanData[X] + fracX;
  149. end
  150. else
  151. begin
  152. SpanData[X] := SpanData[X] - (1 - fracX); Inc(X);
  153. SpanData[X] := SpanData[X] - fracX;
  154. end;
  155. end;
  156. if X > Span.X2 then Span.X2 := X;
  157. end;
  158. CumSum(@SpanData[Span.X1], Span.X2 - Span.X1 + 1);
  159. for I := 0 to ScanLine.Count - 1 do
  160. begin
  161. S := @ScanLine.Segments[I];
  162. IntegrateSegment(S[0], S[1], SpanData);
  163. end;
  164. Span.Values := @SpanData[Span.X1];
  165. end;
  166. (*
  167. procedure ExtractPackedSpans(const ScanLine: TScanLine; out Spans: PValueSpanArray;
  168. out Count: Integer);
  169. const
  170. SpanDelta = 16; {** N: this constant adjusts the span subdivision size }
  171. var
  172. I, J, X, J1, J2: Integer;
  173. Values: PSingleArray;
  174. SpanData: PSingleArray;
  175. P: TFloatPoint;
  176. S: PLineSegment;
  177. V, fracX: TFloat;
  178. Points: PFloatPointArray;
  179. N, SpanWidth: Integer;
  180. X1, X2: Integer;
  181. Span: PValueSpan;
  182. begin
  183. N := ScanLine.Count * 2;
  184. Points := @ScanLine.Segments[0];
  185. X1 := ScanLine.X1;
  186. X2 := ScanLine.X2;
  187. SpanWidth := X2 - X1 + 1;
  188. FillLongWord(ScanLine.SpanData[0], SpanWidth + 1, 0);
  189. Count := (SpanWidth - 1) div SpanDelta + 1;
  190. GetMem(Spans, Count * SizeOf(TValueSpan));
  191. for I := 0 to Count - 1 do
  192. begin
  193. Spans[I].SpanMode := smPacked;
  194. end;
  195. for I := 0 to ScanLine.Count - 1 do
  196. begin
  197. S := @ScanLine.Segments[I];
  198. J1 := (Round(S[0].X) - X1) div SpanDelta;
  199. J2 := (Round(S[1].X) - X1) div SpanDelta;
  200. if J1 > J2 then Swap(J1, J2);
  201. for J := J1 to J2 do Spans[J].SpanMode := smUnpacked;
  202. end;
  203. SpanData := ScanLine.SpanData;
  204. Values := @SpanData[-X1];
  205. for I := 0 to N - 1 do
  206. begin
  207. P := Points[I];
  208. if P.Y = 1 then
  209. begin
  210. X := Round(P.X);
  211. fracX := P.X - X;
  212. if Odd(I) then
  213. begin
  214. Values[X] := Values[X] + (1 - fracX);
  215. Inc(X);
  216. Values[X] := Values[X] + fracX;
  217. end
  218. else
  219. begin
  220. Values[X] := Values[X] - (1 - fracX);
  221. Inc(X);
  222. Values[X] := Values[X] - fracX;
  223. end;
  224. end;
  225. end;
  226. Span := @Spans[0];
  227. Span.X1 := X1;
  228. Span.Values := @SpanData[0];
  229. for I := 1 to Count - 1 do
  230. begin
  231. if Spans[I].SpanMode <> Span.SpanMode then
  232. begin
  233. X := I * SpanDelta;
  234. Span.X2 := X1 + X - 1;
  235. Inc(Span);
  236. Span^ := Spans[I];
  237. Span.Values := @SpanData[X];
  238. Span.X1 := X1 + X;
  239. end
  240. else
  241. Dec(Count);
  242. end;
  243. Span.X2 := X2;
  244. V := 0;
  245. Span := @Spans[0];
  246. if Span.SpanMode = smPacked then Span.Values[0] := V;
  247. for I := 0 to Count - 1 do
  248. begin
  249. if Span.SpanMode = smPacked then
  250. begin
  251. V := Span.Values[0];
  252. Span.Value := V;
  253. end
  254. else
  255. begin
  256. Span.Values[0] := Span.Values[0] + V;
  257. CumSum(Span.Values, Span.X2 - Span.X1 + 2);
  258. end;
  259. Inc(Span);
  260. end;
  261. for I := 0 to ScanLine.Count - 1 do
  262. begin
  263. S := @ScanLine.Segments[I];
  264. IntegrateSegment(S[0], S[1], Values);
  265. end;
  266. end;
  267. *)
  268. procedure AddSegment(const X1, Y1, X2, Y2: TFloat; var ScanLine: TScanLine); {$IFDEF USEINLINING} inline; {$ENDIF}
  269. var
  270. S: PLineSegment;
  271. begin
  272. if (Y1 = 0) and (Y2 = 0) then Exit; {** needed for proper clipping }
  273. with ScanLine do
  274. begin
  275. S := @Segments[Count];
  276. Inc(Count);
  277. end;
  278. S[0].X := X1;
  279. S[0].Y := Y1;
  280. S[1].X := X2;
  281. S[1].Y := Y2;
  282. end;
  283. procedure DivideSegment(var P1, P2: TFloatPoint; const ScanLines: PScanLineArray);
  284. var
  285. Y, Y1, Y2: Integer;
  286. k, X: TFloat;
  287. begin
  288. Y1 := Round(P1.Y);
  289. Y2 := Round(P2.Y);
  290. if Y1 = Y2 then
  291. begin
  292. AddSegment(P1.X, P1.Y - Y1, P2.X, P2.Y - Y1, ScanLines[Y1]);
  293. end
  294. else
  295. begin
  296. k := (P2.X - P1.X) / (P2.Y - P1.Y);
  297. if Y1 < Y2 then
  298. begin
  299. X := P1.X + (Y1 + 1 - P1.Y) * k;
  300. AddSegment(P1.X, P1.Y - Y1, X, 1, ScanLines[Y1]);
  301. for Y := Y1 + 1 to Y2 - 1 do
  302. begin
  303. AddSegment(X, 0, X + k, 1, ScanLines[Y]);
  304. X := X + k;
  305. end;
  306. AddSegment(X, 0, P2.X, P2.Y - Y2, ScanLines[Y2]);
  307. end
  308. else
  309. begin
  310. X := P1.X + (Y1 - P1.Y) * k;
  311. AddSegment(P1.X, P1.Y - Y1, X, 0, ScanLines[Y1]);
  312. for Y := Y1 - 1 downto Y2 + 1 do
  313. begin
  314. AddSegment(X, 1, X - k, 0, ScanLines[Y]);
  315. X := X - k
  316. end;
  317. AddSegment(X, 1, P2.X, P2.Y - Y2, ScanLines[Y2]);
  318. end;
  319. end;
  320. end;
  321. procedure BuildScanLines(const Points: TArrayOfFloatPoint;
  322. out ScanLines: TScanLines);
  323. var
  324. I, J, N, J0, J1, Y, YMin, YMax: Integer;
  325. PScanLines: PScanLineArray;
  326. begin
  327. N := Length(Points);
  328. if N <= 2 then Exit;
  329. YMin := Round(Points[0].Y);
  330. YMax := YMin;
  331. for I := 1 to N - 1 do
  332. begin
  333. Y := Round(Points[I].Y);
  334. if YMin > Y then YMin := Y;
  335. if YMax < Y then YMax := Y;
  336. end;
  337. SetLength(ScanLines, YMax - YMin + 2);
  338. PScanLines := @ScanLines[-YMin];
  339. {** compute array sizes for each scanline }
  340. J0 := Round(Points[0].Y);
  341. for I := 1 to N - 1 do
  342. begin
  343. J1 := J0;
  344. J0 := Round(Points[I].Y);
  345. if J0 <= J1 then
  346. begin
  347. Inc(PScanLines[J0].Count);
  348. Dec(PScanLines[J1 + 1].Count);
  349. end
  350. else
  351. begin
  352. Inc(PScanLines[J1].Count);
  353. Dec(PScanLines[J0 + 1].Count);
  354. end;
  355. end;
  356. {** allocate memory }
  357. J := 0;
  358. for I := 0 to High(ScanLines) do
  359. begin
  360. Inc(J, ScanLines[I].Count);
  361. GetMem(ScanLines[I].Segments, J * SizeOf(TLineSegment));
  362. ScanLines[I].Count := 0;
  363. ScanLines[I].Y := YMin + I;
  364. end;
  365. for I := 0 to N - 2 do
  366. begin
  367. DivideSegment(Points[I], Points[I + 1], PScanLines);
  368. end;
  369. end;
  370. procedure MergeScanLines(const Src: TScanLines; var Dst: TScanLines);
  371. var
  372. Temp: TScanLines;
  373. I, J, K, SrcCount, DstCount: Integer;
  374. begin
  375. if Length(Src) = 0 then Exit;
  376. SetLength(Temp, Length(Src) + Length(Dst));
  377. I := 0;
  378. J := 0;
  379. K := 0;
  380. while (I <= High(Src)) and (J <= High(Dst)) do
  381. begin
  382. if Src[I].Y = Dst[J].Y then
  383. begin
  384. SrcCount := Src[I].Count;
  385. DstCount := Dst[J].Count;
  386. Temp[K].Count := SrcCount + DstCount;
  387. Temp[K].Y := Src[I].Y;
  388. GetMem(Temp[K].Segments, Temp[K].Count * SizeOf(TLineSegment));
  389. Move(Src[I].Segments[0], Temp[K].Segments[0], SrcCount * SizeOf(TLineSegment));
  390. Move(Dst[J].Segments[0], Temp[K].Segments[SrcCount], DstCount * SizeOf(TLineSegment));
  391. FreeMem(Src[I].Segments);
  392. FreeMem(Dst[J].Segments);
  393. Inc(I);
  394. Inc(J);
  395. end
  396. else if Src[I].Y < Dst[J].Y then
  397. begin
  398. Temp[K] := Src[I];
  399. Inc(I);
  400. end
  401. else
  402. begin
  403. Temp[K] := Dst[J];
  404. Inc(J);
  405. end;
  406. Inc(K);
  407. end;
  408. while I <= High(Src) do
  409. begin
  410. Temp[K] := Src[I];
  411. Inc(I); Inc(K);
  412. end;
  413. while J <= High(Dst) do
  414. begin
  415. Temp[K] := Dst[J];
  416. Inc(J); Inc(K);
  417. end;
  418. Dst := Copy(Temp, 0, K);
  419. end;
  420. procedure RenderScanline(var ScanLine: TScanLine;
  421. RenderProc: TRenderSpanProc; Data: Pointer; SpanData: PSingleArray; X1, X2: Integer);
  422. var
  423. Span: TValueSpan;
  424. begin
  425. if ScanLine.Count > 0 then
  426. begin
  427. ExtractSingleSpan(ScanLine, Span, SpanData);
  428. if Span.X1 < X1 then Span.X1 := X1;
  429. if Span.X2 > X2 then Span.X2 := X2;
  430. if Span.X2 < Span.X1 then Exit;
  431. RenderProc(Data, Span, ScanLine.Y);
  432. FillLongWord(SpanData[Span.X1], Span.X2 - Span.X1 + 1, 0);
  433. end;
  434. end;
  435. procedure RenderPolyPolygon(const Points: TArrayOfArrayOfFloatPoint;
  436. const ClipRect: TFloatRect; const RenderProc: TRenderSpanProc; Data: Pointer);
  437. var
  438. ScanLines, Temp: TScanLines;
  439. I: Integer;
  440. Poly: TArrayOfFloatPoint;
  441. SavedRoundMode: TRoundingMode;
  442. CX1, CX2: Integer;
  443. SpanData: PSingleArray;
  444. begin
  445. if Length(Points) = 0 then Exit;
  446. SavedRoundMode := SetRoundMode(rmDown);
  447. try
  448. Poly := ClosePolygon(ClipPolygon(Points[0], ClipRect));
  449. BuildScanLines(Poly, ScanLines);
  450. for I := 1 to High(Points) do
  451. begin
  452. Poly := ClosePolygon(ClipPolygon(Points[I], ClipRect));
  453. BuildScanLines(Poly, Temp);
  454. MergeScanLines(Temp, ScanLines);
  455. Temp := nil;
  456. end;
  457. CX1 := Round(ClipRect.Left);
  458. CX2 := -Round(-ClipRect.Right) - 1;
  459. I := CX2 - CX1 + 4;
  460. GetMem(SpanData, I * SizeOf(Single));
  461. FillLongWord(SpanData^, I, 0);
  462. for I := 0 to High(ScanLines) do
  463. begin
  464. RenderScanline(ScanLines[I], RenderProc, Data, @SpanData[-CX1 + 1], CX1, CX2);
  465. FreeMem(ScanLines[I].Segments);
  466. end;
  467. FreeMem(SpanData);
  468. finally
  469. SetRoundMode(SavedRoundMode);
  470. end;
  471. end;
  472. procedure RenderPolygon(const Points: TArrayOfFloatPoint;
  473. const ClipRect: TFloatRect; const RenderProc: TRenderSpanProc; Data: Pointer);
  474. begin
  475. RenderPolyPolygon(PolyPolygon(Points), ClipRect, RenderProc, Data);
  476. end;
  477. procedure RenderPolyPolygon(const Points: TArrayOfArrayOfFloatPoint;
  478. const ClipRect: TFloatRect; const RenderProc: TRenderSpanEvent);
  479. begin
  480. with TMethod(RenderProc) do
  481. RenderPolyPolygon(Points, ClipRect, TRenderSpanProc(Code), Data);
  482. end;
  483. procedure RenderPolygon(const Points: TArrayOfFloatPoint;
  484. const ClipRect: TFloatRect; const RenderProc: TRenderSpanEvent);
  485. begin
  486. with TMethod(RenderProc) do
  487. RenderPolygon(Points, ClipRect, TRenderSpanProc(Code), Data);
  488. end;
  489. end.