source: trunk/Packages/Graphics32/GR32_VPR.pas

Last change on this file was 2, checked in by chronos, 5 years ago
File size: 14.1 KB
Line 
1unit GR32_VPR;
2
3(* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1 or LGPL 2.1 with linking exception
5 *
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
10 *
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
15 *
16 * Alternatively, the contents of this file may be used under the terms of the
17 * Free Pascal modified version of the GNU Lesser General Public License
18 * Version 2.1 (the "FPC modified LGPL License"), in which case the provisions
19 * of this license are applicable instead of those above.
20 * Please see the file LICENSE.txt for additional information concerning this
21 * license.
22 *
23 * The Original Code is Vectorial Polygon Rasterizer for Graphics32
24 *
25 * The Initial Developer of the Original Code is
26 * Mattias Andersson <mattias@centaurix.com>
27 *
28 * Portions created by the Initial Developer are Copyright (C) 2008-2012
29 * the Initial Developer. All Rights Reserved.
30 *
31 * Contributor(s):
32 *
33 * ***** END LICENSE BLOCK ***** *)
34
35interface
36
37{$I GR32.inc}
38
39uses
40 GR32;
41
42type
43 PInteger = ^Integer;
44 PSingleArray = GR32.PSingleArray;
45 TSingleArray = GR32.TSingleArray;
46
47 PValueSpan = ^TValueSpan;
48 TValueSpan = record
49 X1, X2: Integer;
50 Values: PSingleArray;
51 end;
52
53 TRenderSpanEvent = procedure(const Span: TValueSpan; DstY: Integer) of object;
54 TRenderSpanProc = procedure(Data: Pointer; const Span: TValueSpan; DstY: Integer);
55
56procedure RenderPolyPolygon(const Points: TArrayOfArrayOfFloatPoint;
57 const ClipRect: TFloatRect; const RenderProc: TRenderSpanProc; Data: Pointer = nil); overload;
58procedure RenderPolygon(const Points: TArrayOfFloatPoint;
59 const ClipRect: TFloatRect; const RenderProc: TRenderSpanProc; Data: Pointer = nil); overload;
60procedure RenderPolyPolygon(const Points: TArrayOfArrayOfFloatPoint;
61 const ClipRect: TFloatRect; const RenderProc: TRenderSpanEvent); overload;
62procedure RenderPolygon(const Points: TArrayOfFloatPoint;
63 const ClipRect: TFloatRect; const RenderProc: TRenderSpanEvent); overload;
64
65implementation
66
67uses
68 Math, GR32_Math, GR32_LowLevel, GR32_VectorUtils;
69
70type
71 TArrayOfValueSpan = array of TValueSpan;
72
73 PValueSpanArray = ^TValueSpanArray;
74 TValueSpanArray = array [0..0] of TValueSpan;
75
76 PLineSegment = ^TLineSegment;
77 TLineSegment = array [0..1] of TFloatPoint;
78 TArrayOfLineSegment = array of TLineSegment;
79
80 PLineSegmentArray = ^TLineSegmentArray;
81 TLineSegmentArray = array [0..0] of TLineSegment;
82
83 PScanLine = ^TScanLine;
84 TScanLine = record
85 Segments: PLineSegmentArray;
86 Count: Integer;
87 Y: Integer;
88 end;
89 TScanLines = array of TScanLine;
90 PScanLineArray = ^TScanLineArray;
91 TScanLineArray = array [0..0] of TScanLine;
92
93procedure IntegrateSegment(var P1, P2: TFloatPoint; Values: PSingleArray);
94var
95 X1, X2, I: Integer;
96 Dx, Dy, DyDx, Sx, Y, fracX1, fracX2: TFloat;
97begin
98 X1 := Round(P1.X);
99 X2 := Round(P2.X);
100 if X1 = X2 then
101 begin
102 Values[X1] := Values[X1] + 0.5 * (P2.X - P1.X) * (P1.Y + P2.Y);
103 end
104 else
105 begin
106 fracX1 := P1.X - X1;
107 fracX2 := P2.X - X2;
108
109 Dx := P2.X - P1.X;
110 Dy := P2.Y - P1.Y;
111 DyDx := Dy/Dx;
112
113 if X1 < X2 then
114 begin
115 Sx := 1 - fracX1;
116 Y := P1.Y + Sx * DyDx;
117 Values[X1] := Values[X1] + 0.5 * (P1.Y + Y) * Sx;
118 for I := X1 + 1 to X2 - 1 do
119 begin
120 Values[I] := Values[I] + (Y + DyDx * 0.5); // N: Sx = 1
121 Y := Y + DyDx;
122 end;
123
124 Sx := fracX2;
125 Values[X2] := Values[X2] + 0.5 * (Y + P2.Y) * Sx;
126 end
127 else // X1 > X2
128 begin
129 Sx := fracX1;
130 Y := P1.Y - Sx * DyDx;
131 Values[X1] := Values[X1] - 0.5 * (P1.Y + Y) * Sx;
132 for I := X1 - 1 downto X2 + 1 do
133 begin
134 Values[I] := Values[I] - (Y - DyDx * 0.5); // N: Sx = -1
135 Y := Y - DyDx;
136 end;
137 Sx := 1 - fracX2;
138 Values[X2] := Values[X2] - 0.5 * (Y + P2.Y) * Sx;
139 end;
140 end;
141end;
142
143procedure ExtractSingleSpan(const ScanLine: TScanLine; out Span: TValueSpan;
144 SpanData: PSingleArray);
145var
146 I, X: Integer;
147 P: PFloatPoint;
148 S: PLineSegment;
149 fracX: TFloat;
150 Points: PFloatPointArray;
151 N: Integer;
152begin
153 N := ScanLine.Count * 2;
154 Points := @ScanLine.Segments[0];
155 Span.X1 := High(Integer);
156 Span.X2 := Low(Integer);
157
158 for I := 0 to N - 1 do
159 begin
160 P := @Points[I];
161 X := Round(P.X);
162 if X < Span.X1 then Span.X1 := X;
163 if P.Y = 1 then
164 begin
165 fracX := P.X - X;
166 if Odd(I) then
167 begin
168 SpanData[X] := SpanData[X] + (1 - fracX); Inc(X);
169 SpanData[X] := SpanData[X] + fracX;
170 end
171 else
172 begin
173 SpanData[X] := SpanData[X] - (1 - fracX); Inc(X);
174 SpanData[X] := SpanData[X] - fracX;
175 end;
176 end;
177 if X > Span.X2 then Span.X2 := X;
178 end;
179
180 CumSum(@SpanData[Span.X1], Span.X2 - Span.X1 + 1);
181
182 for I := 0 to ScanLine.Count - 1 do
183 begin
184 S := @ScanLine.Segments[I];
185 IntegrateSegment(S[0], S[1], SpanData);
186 end;
187
188 Span.Values := @SpanData[Span.X1];
189end;
190
191(*
192procedure ExtractPackedSpans(const ScanLine: TScanLine; out Spans: PValueSpanArray;
193 out Count: Integer);
194const
195 SpanDelta = 16; {** N: this constant adjusts the span subdivision size }
196var
197 I, J, X, J1, J2: Integer;
198 Values: PSingleArray;
199 SpanData: PSingleArray;
200 P: TFloatPoint;
201 S: PLineSegment;
202 V, fracX: TFloat;
203 Points: PFloatPointArray;
204 N, SpanWidth: Integer;
205 X1, X2: Integer;
206 Span: PValueSpan;
207begin
208 N := ScanLine.Count * 2;
209 Points := @ScanLine.Segments[0];
210 X1 := ScanLine.X1;
211 X2 := ScanLine.X2;
212 SpanWidth := X2 - X1 + 1;
213
214 FillLongWord(ScanLine.SpanData[0], SpanWidth + 1, 0);
215
216 Count := (SpanWidth - 1) div SpanDelta + 1;
217 GetMem(Spans, Count * SizeOf(TValueSpan));
218
219 for I := 0 to Count - 1 do
220 begin
221 Spans[I].SpanMode := smPacked;
222 end;
223
224 for I := 0 to ScanLine.Count - 1 do
225 begin
226 S := @ScanLine.Segments[I];
227 J1 := (Round(S[0].X) - X1) div SpanDelta;
228 J2 := (Round(S[1].X) - X1) div SpanDelta;
229 if J1 > J2 then Swap(J1, J2);
230 for J := J1 to J2 do Spans[J].SpanMode := smUnpacked;
231 end;
232
233 SpanData := ScanLine.SpanData;
234 Values := @SpanData[-X1];
235
236 for I := 0 to N - 1 do
237 begin
238 P := Points[I];
239 if P.Y = 1 then
240 begin
241 X := Round(P.X);
242 fracX := P.X - X;
243 if Odd(I) then
244 begin
245 Values[X] := Values[X] + (1 - fracX);
246 Inc(X);
247 Values[X] := Values[X] + fracX;
248 end
249 else
250 begin
251 Values[X] := Values[X] - (1 - fracX);
252 Inc(X);
253 Values[X] := Values[X] - fracX;
254 end;
255 end;
256 end;
257
258 Span := @Spans[0];
259 Span.X1 := X1;
260 Span.Values := @SpanData[0];
261 for I := 1 to Count - 1 do
262 begin
263 if Spans[I].SpanMode <> Span.SpanMode then
264 begin
265 X := I * SpanDelta;
266 Span.X2 := X1 + X - 1;
267 Inc(Span);
268 Span^ := Spans[I];
269 Span.Values := @SpanData[X];
270 Span.X1 := X1 + X;
271 end
272 else
273 Dec(Count);
274 end;
275 Span.X2 := X2;
276
277 V := 0;
278 Span := @Spans[0];
279 if Span.SpanMode = smPacked then Span.Values[0] := V;
280 for I := 0 to Count - 1 do
281 begin
282 if Span.SpanMode = smPacked then
283 begin
284 V := Span.Values[0];
285 Span.Value := V;
286 end
287 else
288 begin
289 Span.Values[0] := Span.Values[0] + V;
290 CumSum(Span.Values, Span.X2 - Span.X1 + 2);
291 end;
292 Inc(Span);
293 end;
294
295 for I := 0 to ScanLine.Count - 1 do
296 begin
297 S := @ScanLine.Segments[I];
298 IntegrateSegment(S[0], S[1], Values);
299 end;
300end;
301*)
302
303procedure AddSegment(const X1, Y1, X2, Y2: TFloat; var ScanLine: TScanLine); {$IFDEF USEINLINING} inline; {$ENDIF}
304var
305 S: PLineSegment;
306begin
307 if (Y1 = 0) and (Y2 = 0) then Exit; {** needed for proper clipping }
308 with ScanLine do
309 begin
310 S := @Segments[Count];
311 Inc(Count);
312 end;
313
314 S[0].X := X1;
315 S[0].Y := Y1;
316 S[1].X := X2;
317 S[1].Y := Y2;
318end;
319
320procedure DivideSegment(var P1, P2: TFloatPoint; const ScanLines: PScanLineArray);
321var
322 Y, Y1, Y2: Integer;
323 k, X: TFloat;
324begin
325 Y1 := Round(P1.Y);
326 Y2 := Round(P2.Y);
327
328 if Y1 = Y2 then
329 begin
330 AddSegment(P1.X, P1.Y - Y1, P2.X, P2.Y - Y1, ScanLines[Y1]);
331 end
332 else
333 begin
334 k := (P2.X - P1.X) / (P2.Y - P1.Y);
335 if Y1 < Y2 then
336 begin
337 X := P1.X + (Y1 + 1 - P1.Y) * k;
338 AddSegment(P1.X, P1.Y - Y1, X, 1, ScanLines[Y1]);
339 for Y := Y1 + 1 to Y2 - 1 do
340 begin
341 AddSegment(X, 0, X + k, 1, ScanLines[Y]);
342 X := X + k;
343 end;
344 AddSegment(X, 0, P2.X, P2.Y - Y2, ScanLines[Y2]);
345 end
346 else
347 begin
348 X := P1.X + (Y1 - P1.Y) * k;
349 AddSegment(P1.X, P1.Y - Y1, X, 0, ScanLines[Y1]);
350 for Y := Y1 - 1 downto Y2 + 1 do
351 begin
352 AddSegment(X, 1, X - k, 0, ScanLines[Y]);
353 X := X - k
354 end;
355 AddSegment(X, 1, P2.X, P2.Y - Y2, ScanLines[Y2]);
356 end;
357 end;
358end;
359
360procedure BuildScanLines(const Points: TArrayOfFloatPoint;
361 out ScanLines: TScanLines);
362var
363 I, J, N, J0, J1, Y, YMin, YMax: Integer;
364 PScanLines: PScanLineArray;
365begin
366 N := Length(Points);
367 if N <= 2 then Exit;
368
369 YMin := Round(Points[0].Y);
370 YMax := YMin;
371 for I := 1 to N - 1 do
372 begin
373 Y := Round(Points[I].Y);
374 if YMin > Y then YMin := Y;
375 if YMax < Y then YMax := Y;
376 end;
377
378 SetLength(ScanLines, YMax - YMin + 2);
379 PScanLines := @ScanLines[-YMin];
380
381 {** compute array sizes for each scanline }
382 J0 := Round(Points[0].Y);
383 for I := 1 to N - 1 do
384 begin
385 J1 := J0;
386 J0 := Round(Points[I].Y);
387 if J0 <= J1 then
388 begin
389 Inc(PScanLines[J0].Count);
390 Dec(PScanLines[J1 + 1].Count);
391 end
392 else
393 begin
394 Inc(PScanLines[J1].Count);
395 Dec(PScanLines[J0 + 1].Count);
396 end;
397 end;
398
399 {** allocate memory }
400 J := 0;
401 for I := 0 to High(ScanLines) do
402 begin
403 Inc(J, ScanLines[I].Count);
404 GetMem(ScanLines[I].Segments, J * SizeOf(TLineSegment));
405 ScanLines[I].Count := 0;
406 ScanLines[I].Y := YMin + I;
407 end;
408
409 for I := 0 to N - 2 do
410 begin
411 DivideSegment(Points[I], Points[I + 1], PScanLines);
412 end;
413end;
414
415procedure MergeScanLines(const Src: TScanLines; var Dst: TScanLines);
416var
417 Temp: TScanLines;
418 I, J, K, SrcCount, DstCount: Integer;
419begin
420 if Length(Src) = 0 then Exit;
421 SetLength(Temp, Length(Src) + Length(Dst));
422
423 I := 0;
424 J := 0;
425 K := 0;
426 while (I <= High(Src)) and (J <= High(Dst)) do
427 begin
428 if Src[I].Y = Dst[J].Y then
429 begin
430 SrcCount := Src[I].Count;
431 DstCount := Dst[J].Count;
432 Temp[K].Count := SrcCount + DstCount;
433 Temp[K].Y := Src[I].Y;
434 GetMem(Temp[K].Segments, Temp[K].Count * SizeOf(TLineSegment));
435
436 Move(Src[I].Segments[0], Temp[K].Segments[0], SrcCount * SizeOf(TLineSegment));
437 Move(Dst[J].Segments[0], Temp[K].Segments[SrcCount], DstCount * SizeOf(TLineSegment));
438 FreeMem(Src[I].Segments);
439 FreeMem(Dst[J].Segments);
440 Inc(I);
441 Inc(J);
442 end
443 else if Src[I].Y < Dst[J].Y then
444 begin
445 Temp[K] := Src[I];
446 Inc(I);
447 end
448 else
449 begin
450 Temp[K] := Dst[J];
451 Inc(J);
452 end;
453 Inc(K);
454 end;
455 while I <= High(Src) do
456 begin
457 Temp[K] := Src[I];
458 Inc(I); Inc(K);
459 end;
460 while J <= High(Dst) do
461 begin
462 Temp[K] := Dst[J];
463 Inc(J); Inc(K);
464 end;
465 Dst := Copy(Temp, 0, K);
466end;
467
468procedure RenderScanline(var ScanLine: TScanLine;
469 RenderProc: TRenderSpanProc; Data: Pointer; SpanData: PSingleArray; X1, X2: Integer);
470var
471 Span: TValueSpan;
472begin
473 if ScanLine.Count > 0 then
474 begin
475 ExtractSingleSpan(ScanLine, Span, SpanData);
476 if Span.X1 < X1 then Span.X1 := X1;
477 if Span.X2 > X2 then Span.X2 := X2;
478 if Span.X2 < Span.X1 then Exit;
479
480 RenderProc(Data, Span, ScanLine.Y);
481 FillLongWord(SpanData[Span.X1], Span.X2 - Span.X1 + 1, 0);
482 end;
483end;
484
485{$ifndef COMPILERXE2_UP}
486type
487 TRoundingMode = Math.TFPURoundingMode;
488{$endif COMPILERXE2_UP}
489
490procedure RenderPolyPolygon(const Points: TArrayOfArrayOfFloatPoint;
491 const ClipRect: TFloatRect; const RenderProc: TRenderSpanProc; Data: Pointer);
492var
493 ScanLines, Temp: TScanLines;
494 I: Integer;
495 Poly: TArrayOfFloatPoint;
496 SavedRoundMode: TRoundingMode;
497 CX1, CX2: Integer;
498 SpanData: PSingleArray;
499begin
500 if Length(Points) = 0 then Exit;
501 SavedRoundMode := SetRoundMode(rmDown);
502 try
503 Poly := ClosePolygon(ClipPolygon(Points[0], ClipRect));
504 BuildScanLines(Poly, ScanLines);
505 for I := 1 to High(Points) do
506 begin
507 Poly := ClosePolygon(ClipPolygon(Points[I], ClipRect));
508 BuildScanLines(Poly, Temp);
509 MergeScanLines(Temp, ScanLines);
510 Temp := nil;
511 end;
512
513 CX1 := Round(ClipRect.Left);
514 CX2 := -Round(-ClipRect.Right) - 1;
515
516 I := CX2 - CX1 + 4;
517 GetMem(SpanData, I * SizeOf(Single));
518 FillLongWord(SpanData^, I, 0);
519
520 for I := 0 to High(ScanLines) do
521 begin
522 RenderScanline(ScanLines[I], RenderProc, Data, @SpanData[-CX1 + 1], CX1, CX2);
523 FreeMem(ScanLines[I].Segments);
524 end;
525 FreeMem(SpanData);
526 finally
527 SetRoundMode(SavedRoundMode);
528 end;
529end;
530
531procedure RenderPolygon(const Points: TArrayOfFloatPoint;
532 const ClipRect: TFloatRect; const RenderProc: TRenderSpanProc; Data: Pointer);
533begin
534 RenderPolyPolygon(PolyPolygon(Points), ClipRect, RenderProc, Data);
535end;
536
537procedure RenderPolyPolygon(const Points: TArrayOfArrayOfFloatPoint;
538 const ClipRect: TFloatRect; const RenderProc: TRenderSpanEvent);
539begin
540 with TMethod(RenderProc) do
541 RenderPolyPolygon(Points, ClipRect, TRenderSpanProc(Code), Data);
542end;
543
544procedure RenderPolygon(const Points: TArrayOfFloatPoint;
545 const ClipRect: TFloatRect; const RenderProc: TRenderSpanEvent);
546begin
547 with TMethod(RenderProc) do
548 RenderPolygon(Points, ClipRect, TRenderSpanProc(Code), Data);
549end;
550
551end.
Note: See TracBrowser for help on using the repository browser.