source: trunk/Packages/bgrabitmap/bgrafillinfo.pas

Last change on this file was 2, checked in by chronos, 5 years ago
File size: 40.4 KB
Line 
1unit BGRAFillInfo;
2
3{$mode objfpc}{$H+}
4
5interface
6
7uses
8 Classes, SysUtils, BGRABitmapTypes;
9
10const
11 AntialiasPrecision = 16;
12 AntialiasPrecisionShift = 4;
13
14type
15 TDensity = word;
16 PDensity = ^TDensity;
17
18type
19 { TFillShapeInfo }
20
21 TFillShapeInfo = class(TBGRACustomFillInfo)
22 protected
23 //compute intersections. the array must be big enough
24 procedure ComputeIntersection(cury: single; var inter: ArrayOfTIntersectionInfo; var nbInter: integer); virtual;
25 //sort from left to right
26 procedure SortIntersection(var inter: ArrayOfTIntersectionInfo; nbInter: integer); virtual;
27 //apply non-zero winding rule. it can change the number of intersections
28 procedure ConvertFromNonZeroWinding(var inter: ArrayOfTIntersectionInfo; var nbInter: integer); virtual;
29 //returns maximum of intersection per line
30 function NbMaxIntersection: integer; virtual;
31
32 public
33 //returns true if the same segment number can be curved
34 function SegmentsCurved: boolean; override;
35
36 //returns integer bounds
37 function GetBounds: TRect; override;
38
39 //check if the point is inside the filling zone
40 function IsPointInside(x,y: single; windingMode: boolean): boolean; override;
41
42 //create an array that will contain computed intersections.
43 //you may augment, in this case, use CreateIntersectionInfo for new items
44 function CreateIntersectionArray: ArrayOfTIntersectionInfo; override;
45 function CreateIntersectionInfo: TIntersectionInfo; override; //creates a single info
46 procedure FreeIntersectionArray(var inter: ArrayOfTIntersectionInfo); override;
47
48 //fill a previously created array of intersections with actual intersections at the current y coordinate.
49 //nbInter gets the number of computed intersections
50 procedure ComputeAndSort(cury: single; var inter: ArrayOfTIntersectionInfo; out nbInter: integer; windingMode: boolean); override;
51
52 //can be called after ComputeAndSort or ComputeIntersection to determine the current horizontal slice
53 //so that it can be checked if the intermediates scanlines can be skipped
54 function GetSliceIndex: integer; override;
55
56 end;
57
58 { TFillEllipseInfo }
59
60 TFillEllipseInfo = class(TFillShapeInfo)
61 private
62 FX, FY, FRX, FRY: single;
63 FSliceIndex: integer;
64 function GetCenter: TPointF;
65 protected
66 function NbMaxIntersection: integer; override;
67 procedure ComputeIntersection(cury: single;
68 var inter: ArrayOfTIntersectionInfo; var nbInter: integer); override;
69 public
70 WindingFactor: integer;
71 constructor Create(x, y, rx, ry: single);
72 function GetBounds: TRect; override;
73 function SegmentsCurved: boolean; override;
74 function GetSliceIndex: integer; override;
75 property Center: TPointF read GetCenter;
76 property RadiusX: single read FRX;
77 property RadiusY: single read FRY;
78 end;
79
80 { TFillBorderEllipseInfo }
81
82 TFillBorderEllipseInfo = class(TFillShapeInfo)
83 private
84 FInnerBorder, FOuterBorder: TFillEllipseInfo;
85 protected
86 function NbMaxIntersection: integer; override;
87 procedure ComputeIntersection(cury: single;
88 var inter: ArrayOfTIntersectionInfo; var nbInter: integer); override;
89 public
90 constructor Create(x, y, rx, ry, w: single);
91 function GetBounds: TRect; override;
92 function SegmentsCurved: boolean; override;
93 destructor Destroy; override;
94 function GetSliceIndex: integer; override;
95 property InnerBorder: TFillEllipseInfo read FInnerBorder;
96 property OuterBorder: TFillEllipseInfo read FOuterBorder;
97 end;
98
99 { TFillRoundRectangleInfo }
100
101 TFillRoundRectangleInfo = class(TFillShapeInfo)
102 private
103 FX1, FY1, FX2, FY2, FRX, FRY: single;
104 FOptions: TRoundRectangleOptions;
105 function GetBottomRight: TPointF;
106 function GetTopLeft: TPointF;
107 protected
108 function NbMaxIntersection: integer; override;
109 procedure ComputeIntersection(cury: single;
110 var inter: ArrayOfTIntersectionInfo; var nbInter: integer); override;
111 public
112 WindingFactor: integer;
113 constructor Create(x1, y1, x2, y2, rx, ry: single; options: TRoundRectangleOptions; APixelCenteredCoordinates: boolean = true);
114 function SegmentsCurved: boolean; override;
115 function GetBounds: TRect; override;
116 property TopLeft: TPointF read GetTopLeft;
117 property BottomRight: TPointF read GetBottomRight;
118 property RadiusX: single read FRX;
119 property RadiusY: single read FRY;
120 end;
121
122 { TFillBorderRoundRectInfo }
123
124 TFillBorderRoundRectInfo = class(TFillShapeInfo)
125 protected
126 FInnerBorder, FOuterBorder: TFillRoundRectangleInfo;
127 function NbMaxIntersection: integer; override;
128 procedure ComputeIntersection(cury: single;
129 var inter: ArrayOfTIntersectionInfo; var nbInter: integer); override;
130 public
131 constructor Create(x1, y1, x2, y2, rx, ry, w: single; options: TRoundRectangleOptions; APixelCenteredCoordinates: boolean = true);
132 function GetBounds: TRect; override;
133 function SegmentsCurved: boolean; override;
134 destructor Destroy; override;
135 property InnerBorder: TFillRoundRectangleInfo read FInnerBorder;
136 property OuterBorder: TFillRoundRectangleInfo read FOuterBorder;
137 end;
138
139 TPolySlice = record
140 y1,y2: single;
141 segments: array of record
142 y1,x1: single;
143 slope: single;
144 winding: integer;
145 data: pointer;
146 id: integer;
147 end;
148 nbSegments: integer;
149 end;
150
151 { TCustomFillPolyInfo }
152
153 TCustomFillPolyInfo = class(TFillShapeInfo)
154 private
155 function GetNbPoints: integer;
156 protected
157 FPoints: array of TPointF;
158 FSlopes: array of single;
159 FEmptyPt: array of boolean;
160 FNext, FPrev: array of integer;
161 function NbMaxIntersection: integer; override;
162 procedure SetIntersectionValues(AInter: TIntersectionInfo; AInterX: Single; AWinding, ANumSegment: integer; {%H-}dy: single; {%H-}AData: pointer); virtual;
163 procedure InitPoints(const points: array of TPointF);
164 public
165 constructor Create(const points: array of TPointF; APixelCenteredCoordinates: boolean = true);
166 function CreateSegmentData(numPt,nextPt: integer; x,y: single): pointer; virtual;
167 procedure FreeSegmentData(data: pointer); virtual;
168 function GetBounds: TRect; override;
169 property NbPoints: integer read GetNbPoints;
170 end;
171
172 { TFillPolyInfo }
173
174 TFillPolyInfo = class(TCustomFillPolyInfo)
175 protected
176 FSlices: array of TPolySlice;
177 FCurSlice: integer;
178 FMaxIntersection: integer;
179 function NbMaxIntersection: integer; override;
180 procedure ComputeIntersection(cury: single;
181 var inter: ArrayOfTIntersectionInfo; var nbInter: integer); override;
182 public
183 constructor Create(const points: array of TPointF; APixelCenteredCoordinates: boolean = true);
184 destructor Destroy; override;
185 function GetSliceIndex: integer; override;
186 end;
187
188 POnePassRecord = ^TOnePassRecord;
189 TOnePassRecord = record
190 id: integer;
191 data: pointer;
192 slope: single;
193 winding: integer;
194 includeStartingPoint: boolean;
195 originalY1: single;
196 x1,y1,y2: single;
197 next: POnePassRecord;
198 nextWaiting: POnePassRecord;
199 nextDrawing: POnePassRecord;
200 end;
201
202 { TOnePassFillPolyInfo }
203
204 TOnePassFillPolyInfo = class(TCustomFillPolyInfo)
205 private
206 procedure InsertionSortByY;
207 function PartitionByY(left, right: integer): integer;
208 procedure QuickSortByY(left, right: integer);
209 procedure SortByY;
210 protected
211 FOnePass: array of TOnePassRecord;
212 FSortedByY: array of POnePassRecord;
213 FFirstWaiting, FFirstDrawing: POnePassRecord;
214 FShouldInitializeDrawing: boolean;
215 FSliceIndex: integer;
216 procedure ComputeIntersection(cury: single;
217 var inter: ArrayOfTIntersectionInfo; var nbInter: integer); override;
218 public
219 constructor Create(const points: array of TPointF; APixelCenteredCoordinates: boolean = true);
220 function CreateIntersectionArray: ArrayOfTIntersectionInfo; override;
221 function GetSliceIndex: integer; override;
222 destructor Destroy; override;
223 end;
224
225 { TSimpleFillPolyInfo }
226
227 TSimpleFillPolyInfo = class(TCustomFillPolyInfo)
228 protected
229 FSimple: array of record
230 winding: integer;
231 includeStartingPoint: boolean;
232 data: pointer;
233 end;
234 procedure ComputeIntersection(cury: single; var inter: ArrayOfTIntersectionInfo;
235 var nbInter: integer); override;
236 public
237 constructor Create(const points: array of TPointF);
238 destructor Destroy; override;
239 end;
240
241procedure AddDensity(dest: PDensity; start,count: integer; value : word); inline;
242function DivByAntialiasPrecision(value: UInt32or64): UInt32or64; inline;
243function DivByAntialiasPrecision256(value: UInt32or64): UInt32or64; inline;
244function DivByAntialiasPrecision65536(value: UInt32or64): UInt32or64; inline;
245procedure ComputeAliasedRowBounds(x1,x2: single; minx,maxx: integer; out ix1,ix2: integer);
246
247function IsPointInPolygon(const points: ArrayOfTPointF; point: TPointF; windingMode: boolean): boolean;
248function IsPointInEllipse(x,y,rx,ry: single; point: TPointF): boolean;
249function IsPointInRoundRectangle(x1, y1, x2, y2, rx, ry: single; point: TPointF): boolean;
250function IsPointInRectangle(x1, y1, x2, y2: single; point: TPointF): boolean;
251
252function BGRAShapeComputeMinMax(AShape: TBGRACustomFillInfo; out minx, miny, maxx, maxy: integer;
253 bmpDest: TBGRACustomBitmap): boolean;
254
255implementation
256
257uses Math;
258
259function BGRAShapeComputeMinMax(AShape: TBGRACustomFillInfo; out minx, miny, maxx, maxy: integer;
260 bmpDest: TBGRACustomBitmap): boolean;
261var clip,bounds: TRect;
262begin
263 result := true;
264 bounds := AShape.GetBounds;
265
266 if (bounds.Right <= bounds.left) or (bounds.bottom <= bounds.top) then
267 begin
268 result := false;
269 exit;
270 end;
271
272 miny := bounds.top;
273 maxy := bounds.bottom - 1;
274 minx := bounds.left;
275 maxx := bounds.right - 1;
276
277 clip := bmpDest.ClipRect;
278
279 if minx < clip.Left then
280 minx := clip.Left;
281 if maxx < clip.Left then
282 result := false;
283
284 if maxx > clip.Right - 1 then
285 maxx := clip.Right- 1;
286 if minx > clip.Right - 1 then
287 result := false;
288
289 if miny < clip.Top then
290 miny := clip.Top;
291 if maxy < clip.Top then
292 result := false;
293
294 if maxy > clip.Bottom - 1 then
295 maxy := clip.Bottom - 1;
296 if miny > clip.Bottom - 1 then
297 result := false;
298end;
299
300procedure ComputeAliasedRowBounds(x1,x2: single; minx,maxx: integer; out ix1,ix2: integer);
301begin
302 if frac(x1)=0.5 then
303 ix1 := trunc(x1) else
304 ix1 := round(x1);
305 if frac(x2)=0.5 then
306 ix2 := trunc(x2)-1 else
307 ix2 := round(x2)-1;
308
309 if ix1 < minx then
310 ix1 := minx;
311 if ix2 >= maxx then
312 ix2 := maxx;
313end;
314
315function IsPointInPolygon(const points: ArrayOfTPointF; point: TPointF
316 ; windingMode: boolean): boolean;
317var info: TBGRACustomFillInfo;
318begin
319 info := TSimpleFillPolyInfo.Create(points);
320 result := info.IsPointInside(point.x+0.5,point.y+0.5,windingMode);
321 info.free;
322end;
323
324function IsPointInEllipse(x, y, rx, ry: single; point: TPointF): boolean;
325var info: TBGRACustomFillInfo;
326begin
327 info := TFillEllipseInfo.Create(x,y,rx,ry);
328 result := info.IsPointInside(point.x+0.5,point.y+0.5,false);
329 info.free;
330end;
331
332function IsPointInRoundRectangle(x1, y1, x2, y2, rx, ry: single; point: TPointF
333 ): boolean;
334var info: TBGRACustomFillInfo;
335begin
336 info := TFillRoundRectangleInfo.Create(x1, y1, x2, y2, rx, ry,[]);
337 result := info.IsPointInside(point.x+0.5,point.y+0.5,false);
338 info.free;
339end;
340
341function IsPointInRectangle(x1, y1, x2, y2: single; point: TPointF): boolean;
342begin
343 with point do
344 result := (((x1<x) and (x2>x)) or ((x1>x) and (x2<x))) and
345 (((y1<y) and (y2>y)) or ((y1>y) and (y2<y)));
346end;
347
348procedure AddDensity(dest: PDensity; start,count: integer; value: word);
349var valueValue: longword;
350 lastAdd: integer;
351begin
352 if count=0 then exit;
353 inc(dest,start);
354 if start and 1 = 1 then
355 begin
356 dest^ += value;
357 inc(dest);
358 dec(count);
359 end;
360 lastAdd := count and 1;
361 count := count shr 1;
362 if count > 0 then
363 begin
364 valueValue := value+(value shl 16);
365 while count > 0 do
366 begin
367 plongword(dest)^ += valueValue;
368 inc(dest,2);
369 dec(count);
370 end;
371 end;
372 if lastAdd <> 0 then
373 dest^ += value;
374end;
375
376function DivByAntialiasPrecision(value: UInt32or64): UInt32or64;
377begin //
378 result := value shr AntialiasPrecisionShift;// div AntialiasPrecision;
379end;
380
381function DivByAntialiasPrecision256(value: UInt32or64): UInt32or64;
382begin //
383 result := value shr (AntialiasPrecisionShift+8);// div (256*AntialiasPrecision);
384end;
385
386function DivByAntialiasPrecision65536(value: UInt32or64): UInt32or64;
387begin //
388 result := value shr (AntialiasPrecisionShift+16);//div (65536*AntialiasPrecision);
389end;
390
391{ TFillShapeInfo }
392
393function TFillShapeInfo.GetBounds: TRect;
394begin
395 Result := rect(0, 0, 0, 0);
396end;
397
398
399function TFillShapeInfo.IsPointInside(x, y: single; windingMode: boolean
400 ): boolean;
401var
402 inter : ArrayOfTIntersectionInfo;
403 i,nbInter: integer;
404begin
405 inter := CreateIntersectionArray;
406 ComputeAndSort(y,inter,nbInter,windingMode);
407 i := 0;
408 while i+1 < nbInter do
409 begin
410 if (inter[i].interX < x) and (inter[i+1].interX > x) then
411 begin
412 result := true;
413 FreeIntersectionArray(inter);
414 exit;
415 end;
416 inc(i,2);
417 end;
418 result := false;
419 FreeIntersectionArray(inter);
420end;
421
422function TFillShapeInfo.NbMaxIntersection: integer;
423begin
424 Result := 0;
425end;
426
427function TFillShapeInfo.SegmentsCurved: boolean;
428begin
429 result := false;
430end;
431
432function TFillShapeInfo.CreateIntersectionInfo: TIntersectionInfo;
433begin
434 result := TIntersectionInfo.Create;
435end;
436
437procedure TFillShapeInfo.FreeIntersectionArray(
438 var inter: ArrayOfTIntersectionInfo);
439var
440 i: Integer;
441begin
442 for i := 0 to high(inter) do
443 inter[i].free;
444 inter := nil;
445end;
446
447{$hints off}
448procedure TFillShapeInfo.ComputeIntersection(cury: single;
449 var inter: ArrayOfTIntersectionInfo; var nbInter: integer);
450begin
451
452end;
453{$hints on}
454
455procedure TFillShapeInfo.SortIntersection(var inter: ArrayOfTIntersectionInfo; nbInter: integer);
456var
457 i,j,k: Integer;
458 tempInter: TIntersectionInfo;
459begin
460 for i := 1 to nbinter - 1 do
461 begin
462 j := i;
463 while (j > 0) and (inter[i].interX < inter[j-1].interX) do dec(j);
464 if j <> i then
465 begin
466 tempInter := inter[i];
467 for k := i-1 downto j do
468 inter[k+1] := inter[k];
469 inter[j] := tempInter;
470 end;
471 end;
472end;
473
474procedure TFillShapeInfo.ConvertFromNonZeroWinding(var inter: ArrayOfTIntersectionInfo; var nbInter: integer);
475var windingSum,prevSum,i,nbAlternate: integer;
476 tempInfo: TIntersectionInfo;
477begin
478 windingSum := 0;
479 nbAlternate := 0;
480 for i := 0 to nbInter-1 do
481 begin
482 prevSum := windingSum;
483 windingSum += inter[i].winding;
484 if (windingSum = 0) xor (prevSum = 0) then
485 begin
486 if nbAlternate<>i then
487 begin
488 tempInfo := inter[nbAlternate];
489 inter[nbAlternate] := inter[i];
490 inter[i] := tempInfo;
491 end;
492 inc(nbAlternate);
493 end;
494 end;
495 nbInter := nbAlternate;
496end;
497
498procedure TFillShapeInfo.ComputeAndSort(cury: single;
499 var inter: ArrayOfTIntersectionInfo; out nbInter: integer; windingMode: boolean);
500begin
501 nbInter := 0;
502 ComputeIntersection(cury,inter,nbInter);
503 if nbInter < 2 then exit;
504 SortIntersection(inter,nbInter);
505 if windingMode then ConvertFromNonZeroWinding(inter,nbInter);
506end;
507
508function TFillShapeInfo.GetSliceIndex: integer;
509begin
510 result := 0;
511end;
512
513function TFillShapeInfo.CreateIntersectionArray: ArrayOfTIntersectionInfo;
514var
515 i: Integer;
516begin
517 setlength(result, NbMaxIntersection);
518 for i := 0 to high(result) do
519 result[i] := CreateIntersectionInfo;
520end;
521
522function ComputeWinding(y1,y2: single): integer;
523begin
524 if y2 > y1 then result := 1 else
525 if y2 < y1 then result := -1 else
526 result := 0;
527end;
528
529type
530 arrayOfSingle = array of single;
531
532procedure InsertionSortSingles(var a: arrayOfSingle);
533var i,j: integer;
534 temp: single;
535begin
536 for i := 1 to high(a) do
537 begin
538 Temp := a[i];
539 j := i;
540 while (j>0) and (a[j-1]> Temp) do
541 begin
542 a[j] := a[j-1];
543 dec(j);
544 end;
545 a[j] := Temp;
546 end;
547end;
548
549function PartitionSingles(var a: arrayOfSingle; left,right: integer): integer;
550
551 procedure Swap(idx1,idx2: integer); inline;
552 var temp: single;
553 begin
554 temp := a[idx1];
555 a[idx1] := a[idx2];
556 a[idx2] := temp;
557 end;
558
559var pivotIndex: integer;
560 pivotValue: single;
561 storeIndex: integer;
562 i: integer;
563
564begin
565 pivotIndex := left + random(right-left+1);
566 pivotValue := a[pivotIndex];
567 swap(pivotIndex,right);
568 storeIndex := left;
569 for i := left to right-1 do
570 if a[i] <= pivotValue then
571 begin
572 swap(i,storeIndex);
573 inc(storeIndex);
574 end;
575 swap(storeIndex,right);
576 result := storeIndex;
577end;
578
579procedure QuickSortSingles(var a: arrayOfSingle; left,right: integer);
580var pivotNewIndex: integer;
581begin
582 if right > left+9 then
583 begin
584 pivotNewIndex := PartitionSingles(a,left,right);
585 QuickSortSingles(a,left,pivotNewIndex-1);
586 QuickSortSingles(a,pivotNewIndex+1,right);
587 end;
588end;
589
590procedure SortSingles(var a: arrayOfSingle);
591begin
592 if length(a) < 10 then InsertionSortSingles(a) else
593 begin
594 QuickSortSingles(a,0,high(a));
595 InsertionSortSingles(a);
596 end;
597end;
598
599procedure RemoveSingleDuplicates(var a: arrayOfSingle; var nb: integer);
600var i,idx: integer;
601begin
602 idx := 0;
603 for i := 1 to nb-1 do
604 begin
605 if a[i] <> a[idx] then
606 begin
607 inc(idx);
608 a[idx] := a[i];
609 end;
610 end;
611 nb := idx+1;
612end;
613
614function BinarySearchSingle(value: single; var a: arrayOfSingle; left,right: integer): integer;
615var pivotIndex: integer;
616 pivotValue: single;
617begin
618 pivotIndex := (left+right) div 2;
619 pivotValue := a[pivotIndex];
620 if value = pivotValue then
621 result := pivotIndex else
622 if value < pivotValue then
623 begin
624 if pivotIndex = left then result := left else
625 result := BinarySearchSingle(value, a, left,pivotIndex-1);
626 end else
627 begin
628 if pivotIndex = right then result := right+1 else
629 result := BinarySearchSingle(value, a, pivotIndex+1, right);
630 end;
631end;
632
633{ TCustomFillPolyInfo }
634
635constructor TCustomFillPolyInfo.Create(const points: array of TPointF; APixelCenteredCoordinates: boolean);
636var
637 cur, first, i, j: integer;
638
639begin
640 InitPoints(points);
641
642 //look for empty points, correct coordinate and successors
643 setlength(FEmptyPt, length(FPoints));
644 setlength(FNext, length(FPoints));
645
646 cur := -1;
647 first := -1;
648 for i := 0 to high(FPoints) do
649 if not isEmptyPointF(FPoints[i]) then
650 begin
651 FEmptyPt[i] := False;
652 if APixelCenteredCoordinates then
653 begin
654 FPoints[i].x += 0.5;
655 FPoints[i].y += 0.5;
656 end;
657 if cur <> -1 then
658 FNext[cur] := i;
659 if first = -1 then
660 first := i;
661 cur := i;
662 end
663 else
664 begin
665 if (first <> -1) and (cur <> first) then
666 FNext[cur] := first;
667
668 FEmptyPt[i] := True;
669 FNext[i] := -1;
670 cur := -1;
671 first := -1;
672 end;
673 if (first <> -1) and (cur <> first) then
674 FNext[cur] := first;
675
676 setlength(FPrev, length(FPoints));
677 for i := 0 to high(FPrev) do
678 FPrev[i] := -1;
679 for i := 0 to high(FNext) do
680 if FNext[i] <> -1 then
681 FPrev[FNext[i]] := i;
682
683 setlength(FSlopes, length(FPoints));
684
685 //compute slopes
686 for i := 0 to high(FPoints) do
687 if not FEmptyPt[i] then
688 begin
689 j := FNext[i];
690
691 if FPoints[i].y <> FPoints[j].y then
692 FSlopes[i] := (FPoints[j].x - FPoints[i].x) / (FPoints[j].y - FPoints[i].y)
693 else
694 FSlopes[i] := EmptySingle;
695 end
696 else
697 FSlopes[i] := EmptySingle;
698end;
699
700{$hints off}
701function TCustomFillPolyInfo.CreateSegmentData(numPt,nextPt: integer; x, y: single
702 ): pointer;
703begin
704 result := nil;
705end;
706{$hints on}
707
708procedure TCustomFillPolyInfo.FreeSegmentData(data: pointer);
709begin
710 freemem(data);
711end;
712
713function TCustomFillPolyInfo.GetBounds: TRect;
714var
715 minx, miny, maxx, maxy, i: integer;
716begin
717 if length(FPoints) = 0 then
718 begin
719 result := rect(0,0,0,0);
720 exit;
721 end;
722 miny := floor(FPoints[0].y);
723 maxy := ceil(FPoints[0].y);
724 minx := floor(FPoints[0].x);
725 maxx := ceil(FPoints[0].x);
726 for i := 1 to high(FPoints) do
727 if not FEmptyPt[i] then
728 begin
729 if floor(FPoints[i].y) < miny then
730 miny := floor(FPoints[i].y)
731 else
732 if ceil(FPoints[i].y) > maxy then
733 maxy := ceil(FPoints[i].y);
734
735 if floor(FPoints[i].x) < minx then
736 minx := floor(FPoints[i].x)
737 else
738 if ceil(FPoints[i].x) > maxx then
739 maxx := ceil(FPoints[i].x);
740 end;
741 Result := rect(minx, miny, maxx + 1, maxy + 1);
742end;
743
744function TCustomFillPolyInfo.GetNbPoints: integer;
745begin
746 result := length(FPoints);
747end;
748
749function TCustomFillPolyInfo.NbMaxIntersection: integer;
750begin
751 Result := length(FPoints);
752end;
753
754procedure TCustomFillPolyInfo.SetIntersectionValues(AInter: TIntersectionInfo;
755 AInterX: Single; AWinding, ANumSegment: integer; dy: single; AData: pointer);
756begin
757 AInter.SetValues( AInterX, AWinding, ANumSegment );
758end;
759
760procedure TCustomFillPolyInfo.InitPoints(const points: array of TPointF);
761const
762 minDist = 0.00390625; //1 over 256
763
764var
765 i, first, nbP: integer;
766
767 function PointAlmostEqual(const p1,p2: TPointF): boolean;
768 begin
769 result := (abs(p1.x-p2.x) < minDist) and (abs(p1.y-p2.y) < minDist);
770 end;
771
772 procedure EndOfSubPolygon;
773 begin
774 //if there is a subpolygon
775 if first<>-1 then
776 begin
777 //last point is the same as first point?
778 if (nbP >= first+2) and PointAlmostEqual(FPoints[nbP-1],FPoints[first]) then
779 dec(nbP); //remove superfluous looping point
780
781 if (nbP <= first+2) then //are there only one or two points?
782 begin
783 //remove subpolygon because we need at least a triangle
784 nbP := first;
785 first := -1;
786 end;
787
788 end;
789 end;
790
791begin
792 setlength(FPoints, length(points));
793 nbP := 0;
794 first := -1;
795 for i := 0 to high(points) do
796 if isEmptyPointF(points[i]) then
797 begin
798 EndOfSubPolygon;
799 if first<>-1 then
800 begin
801 FPoints[nbP] := EmptyPointF;
802 inc(nbP);
803 first := -1;
804 end;
805 end else
806 if (first=-1) or not PointAlmostEqual(FPoints[nbP-1],points[i]) then
807 begin
808 if first = -1 then first := nbP;
809 FPoints[nbP] := points[i];
810 inc(nbP);
811 end;
812 EndOfSubPolygon;
813 //if last point was a subpolygon delimiter (EmptyPointF) then removes it
814 if (nbP > 0) and isEmptyPointF(FPoints[nbP-1]) then dec(nbP);
815
816 setlength(FPoints, nbP);
817end;
818
819{ TFillPolyInfo }
820
821function TFillPolyInfo.NbMaxIntersection: integer;
822begin
823 Result:= FMaxIntersection;
824end;
825
826procedure TFillPolyInfo.ComputeIntersection(cury: single;
827 var inter: ArrayOfTIntersectionInfo; var nbInter: integer);
828var
829 j: integer;
830begin
831 if length(FSlices)=0 then exit;
832
833 while (cury < FSlices[FCurSlice].y1) and (FCurSlice > 0) do dec(FCurSlice);
834 while (cury > FSlices[FCurSlice].y2) and (FCurSlice < high(FSlices)) do inc(FCurSlice);
835 with FSlices[FCurSlice] do
836 if (cury >= y1) and (cury <= y2) then
837 begin
838 for j := 0 to nbSegments-1 do
839 begin
840 SetIntersectionValues(inter[nbinter], (cury - segments[j].y1) * segments[j].slope + segments[j].x1,
841 segments[j].winding, segments[j].id, cury - segments[j].y1, segments[j].data );
842 Inc(nbinter);
843 end;
844 end;
845end;
846
847constructor TFillPolyInfo.Create(const points: array of TPointF; APixelCenteredCoordinates: boolean);
848 function AddSeg(numSlice: integer): integer;
849 begin
850 result := FSlices[numSlice].nbSegments;
851 if length(FSlices[numSlice].segments)=FSlices[numSlice].nbSegments then
852 setlength(FSlices[numSlice].segments,FSlices[numSlice].nbSegments*2+2);
853 inc(FSlices[numSlice].nbSegments);
854 end;
855
856var
857 yList: array of single;
858 nbYList: integer;
859 ya,yb,temp: single;
860 sliceStart,sliceEnd,idxSeg: integer;
861 i,j,k,idSeg: integer;
862
863begin
864 inherited Create(points, APixelCenteredCoordinates);
865
866 //slice
867 nbYList:= length(FPoints);
868 setlength(YList, nbYList);
869 for i := 0 to high(FPoints) do
870 YList[i] := FPoints[i].y;
871
872 SortSingles(YList);
873 RemoveSingleDuplicates(YList, nbYList);
874
875 setlength(FSlices, nbYList-1);
876 for i := 0 to high(FSlices) do
877 begin
878 FSlices[i].y1 := YList[i];
879 FSlices[i].y2 := YList[i+1];
880 FSlices[i].nbSegments := 0;
881 end;
882
883 idSeg := 0;
884 for j := 0 to high(FSlopes) do
885 begin
886 if FSlopes[j]<>EmptySingle then
887 begin
888 k := FNext[j];
889
890 ya := FPoints[j].y;
891 yb := FPoints[k].y;
892 if yb < ya then
893 begin
894 temp := ya;
895 ya := yb;
896 yb := temp;
897 end;
898 sliceStart := BinarySearchSingle(ya,YList,0,nbYList-1);
899 sliceEnd := BinarySearchSingle(yb,YList,0,nbYList-1);
900 if sliceEnd > high(FSlices) then sliceEnd := high(FSlices);
901 for i := sliceStart to sliceEnd do
902 begin
903 if ((FPoints[j].y < FSlices[i].y2) and
904 (FPoints[k].y > FSlices[i].y1)) or
905 ((FPoints[k].y < FSlices[i].y2) and
906 (FPoints[j].y > FSlices[i].y1)) then
907 begin
908 idxSeg := AddSeg(i);
909 with FSlices[i].segments[idxSeg] do
910 begin
911 x1 := (FSlices[i].y1 - FPoints[j].y) * FSlopes[j] + FPoints[j].x;
912 y1 := FSlices[i].y1;
913 slope := FSlopes[j];
914 winding := ComputeWinding(FPoints[j].y,FPoints[k].y);
915 data := CreateSegmentData(j,k,x1,y1);
916 inc(idSeg);
917 id := idSeg;
918 end;
919 end;
920 end;
921 end;
922 end;
923
924 FCurSlice := 0;
925 FMaxIntersection:= 0;
926 for i := 0 to high(FSlices) do
927 if FSlices[i].nbSegments > FMaxIntersection then
928 FMaxIntersection:= FSlices[i].nbSegments;
929end;
930
931destructor TFillPolyInfo.Destroy;
932var i,j: integer;
933begin
934 for i := 0 to high(FSlices) do
935 with FSlices[i] do
936 for j := 0 to nbSegments-1 do
937 if segments[j].data <> nil then FreeSegmentData(segments[j].data);
938 inherited Destroy;
939end;
940
941function TFillPolyInfo.GetSliceIndex: integer;
942begin
943 Result:= FCurSlice;
944end;
945
946{ TOnePassFillPolyInfo }
947
948function TOnePassFillPolyInfo.PartitionByY(left,right: integer): integer;
949
950 procedure Swap(idx1,idx2: integer); inline;
951 var temp: POnePassRecord;
952 begin
953 temp := FSortedByY[idx1];
954 FSortedByY[idx1] := FSortedByY[idx2];
955 FSortedByY[idx2] := temp;
956 end;
957
958var pivotIndex: integer;
959 pivotValue: single;
960 storeIndex: integer;
961 i: integer;
962
963begin
964 pivotIndex := left + random(right-left+1);
965 pivotValue := FSortedByY[pivotIndex]^.y1;
966 swap(pivotIndex,right);
967 storeIndex := left;
968 for i := left to right-1 do
969 if FSortedByY[i]^.y1 <= pivotValue then
970 begin
971 swap(i,storeIndex);
972 inc(storeIndex);
973 end;
974 swap(storeIndex,right);
975 result := storeIndex;
976end;
977
978procedure TOnePassFillPolyInfo.QuickSortByY(left,right: integer);
979var pivotNewIndex: integer;
980begin
981 if right > left+9 then
982 begin
983 pivotNewIndex := PartitionByY(left,right);
984 QuickSortByY(left,pivotNewIndex-1);
985 QuickSortByY(pivotNewIndex+1,right);
986 end;
987end;
988
989procedure TOnePassFillPolyInfo.InsertionSortByY;
990var i,j: integer;
991 tempValue: single;
992 tempPtr: POnePassRecord;
993begin
994 for i := 1 to high(FSortedByY) do
995 begin
996 tempPtr := FSortedByY[i];
997 TempValue := tempPtr^.y1;
998 j := i;
999 while (j>0) and (FSortedByY[j-1]^.y1 > TempValue) do
1000 begin
1001 FSortedByY[j] := FSortedByY[j-1];
1002 dec(j);
1003 end;
1004 FSortedByY[j] := tempPtr;
1005 end;
1006end;
1007
1008procedure TOnePassFillPolyInfo.SortByY;
1009var i,nbSorted: integer;
1010begin
1011 setlength(FSortedByY, length(FPoints));
1012 nbSorted := 0;
1013 for i := 0 to high(FSortedByY) do
1014 if not FEmptyPt[i] then
1015 begin
1016 FSortedByY[nbSorted] := @FOnePass[i];
1017 inc(nbSorted);
1018 end;
1019 setlength(FSortedByY,nbSorted);
1020 if length(FSortedByY) < 10 then InsertionSortByY else
1021 begin
1022 QuickSortByY(0,high(FSortedByY));
1023 InsertionSortByY;
1024 end;
1025end;
1026
1027procedure TOnePassFillPolyInfo.ComputeIntersection(cury: single;
1028 var inter: ArrayOfTIntersectionInfo; var nbInter: integer);
1029var
1030 p,pprev,pnext: POnePassRecord;
1031begin
1032 FShouldInitializeDrawing := true;
1033
1034 p := FFirstWaiting;
1035 while p <> nil do
1036 begin
1037 if (cury >= p^.y1) then
1038 begin
1039 if cury <= p^.y2+1 then
1040 begin
1041 p^.nextDrawing := FFirstDrawing;
1042 FFirstDrawing := p;
1043 inc(FSliceIndex);
1044 end;
1045 end
1046 else break;
1047 p := p^.nextWaiting;
1048 end;
1049 FFirstWaiting:= p;
1050
1051 p := FFirstDrawing;
1052 pprev := nil;
1053 while p <> nil do
1054 begin
1055 pnext := p^.nextDrawing;
1056{ if p^.slope = EmptySingle then
1057 raise exception.Create('Unexpected');}
1058 if ((cury > p^.y1) and (cury <= p^.y2)) or
1059 (p^.includeStartingPoint and (cury = p^.y1)) then
1060 begin
1061{ if nbinter = length(inter) then
1062 raise exception.Create('too much'); }
1063 if inter[nbinter] = nil then inter[nbinter] := CreateIntersectionInfo;
1064 SetIntersectionValues(inter[nbinter], (cury - p^.y1)*p^.slope + p^.x1, p^.winding, p^.id, cury - p^.originalY1, p^.data);
1065 inc(nbinter);
1066 end else
1067 if (cury > p^.y2+1) then
1068 begin
1069 if pprev <> nil then
1070 pprev^.nextDrawing := pnext
1071 else
1072 FFirstDrawing:= pnext;
1073 p := pnext;
1074 Inc(FSliceIndex);
1075 continue;
1076 end;
1077 pprev := p;
1078 p := pnext;
1079 end;
1080end;
1081
1082constructor TOnePassFillPolyInfo.Create(const points: array of TPointF; APixelCenteredCoordinates: boolean);
1083var i,j: integer;
1084 p: POnePassRecord;
1085 temp: single;
1086begin
1087 inherited create(points, APixelCenteredCoordinates);
1088
1089 FShouldInitializeDrawing := true;
1090 setlength(FOnePass, length(FPoints));
1091 for i := 0 to high(FPoints) do
1092 if not FEmptyPt[i] then
1093 begin
1094 p := @FOnePass[i];
1095 j := FNext[i];
1096 p^.next := @FOnePass[FNext[i]];
1097 p^.id := i;
1098 p^.slope := FSlopes[i];
1099 if p^.slope <> EmptySingle then
1100 p^.data := CreateSegmentData(i, j, FPoints[i].x, FPoints[i].y);
1101 p^.y1 := FPoints[i].y;
1102 p^.y2 := FPoints[j].y;
1103 p^.originalY1 := p^.y1;
1104 p^.winding:= ComputeWinding(p^.y1,p^.y2);
1105 if p^.y1 < p^.y2 then
1106 p^.x1 := FPoints[i].x
1107 else
1108 if p^.y1 > p^.y2 then
1109 begin
1110 temp := p^.y1;
1111 p^.y1 := p^.y2;
1112 p^.y2 := temp;
1113 p^.x1 := FPoints[j].x;
1114 end;
1115 end;
1116
1117 SortByY;
1118 FSliceIndex := 0;
1119end;
1120
1121function TOnePassFillPolyInfo.CreateIntersectionArray: ArrayOfTIntersectionInfo;
1122var i: integer;
1123 p,pprev: POnePassRecord;
1124begin
1125 if FShouldInitializeDrawing then
1126 begin
1127 FShouldInitializeDrawing := false;
1128 FFirstWaiting:= nil;
1129 pprev := nil;
1130 for i := 0 to high(FSortedByY) do
1131 begin
1132 p := FSortedByY[i];
1133 if p^.winding > 0 then
1134 p^.includeStartingPoint := p^.next^.winding <= 0
1135 else if p^.winding < 0 then
1136 p^.includeStartingPoint := p^.next^.winding >= 0;
1137 if p^.slope <> EmptySingle then
1138 begin
1139 if pprev <> nil then
1140 pprev^.nextWaiting:= p
1141 else
1142 FFirstWaiting := p;
1143 pprev := p;
1144 end;
1145 end;
1146 end;
1147
1148 setlength(result, NbMaxIntersection);
1149 for i := 0 to high(result) do
1150 result[i] := nil;
1151end;
1152
1153function TOnePassFillPolyInfo.GetSliceIndex: integer;
1154begin
1155 Result:= FSliceIndex;
1156end;
1157
1158destructor TOnePassFillPolyInfo.Destroy;
1159var i: integer;
1160begin
1161 for i := 0 to high(FOnePass) do
1162 if FOnePass[i].data<>nil then FreeSegmentData(FOnePass[i].data);
1163 FOnePass := nil;
1164 inherited Destroy;
1165end;
1166
1167{ TSimpleFillPolyInfo }
1168
1169procedure TSimpleFillPolyInfo.ComputeIntersection(cury: single;
1170 var inter: ArrayOfTIntersectionInfo; var nbInter: integer);
1171var i,j: integer;
1172begin
1173 for i := 0 to high(FPoints) do
1174 if FSlopes[i] <> EmptySingle then
1175 begin
1176 j := FNext[i];
1177 if ((cury > FPoints[i].y) and (cury <= FPoints[j].y)) or
1178 ((cury < FPoints[i].y) and (cury >= FPoints[j].y)) or
1179 (FSimple[i].includeStartingPoint and (cury = FPoints[i].y)) then
1180 begin
1181 SetIntersectionValues(inter[nbinter], (cury - FPoints[i].y)*FSlopes[i] + FPoints[i].x, FSimple[i].winding, i, cury - FPoints[i].y, FSimple[i].data);
1182 inc(nbinter);
1183 end;
1184 end;
1185end;
1186
1187constructor TSimpleFillPolyInfo.Create(const points: array of TPointF);
1188var i,j: integer;
1189begin
1190 inherited Create(points);
1191
1192 setlength(FSimple, length(FPoints));
1193 for i := 0 to high(FPoints) do
1194 begin
1195 j := FNext[i];
1196 if j <> -1 then
1197 FSimple[i].winding:= ComputeWinding(FPoints[i].y,FPoints[j].y)
1198 else
1199 FSimple[i].winding:= 0;
1200 if FSlopes[i] <> EmptySingle then
1201 FSimple[i].data := CreateSegmentData(i, j, FPoints[i].x, FPoints[i].y);
1202 end;
1203end;
1204
1205destructor TSimpleFillPolyInfo.Destroy;
1206var i: integer;
1207begin
1208 for i := 0 to high(FSimple) do
1209 if FSimple[i].data <> nil then
1210 FreeSegmentData(FSimple[i].data);
1211 FSimple := nil;
1212 inherited Destroy;
1213end;
1214
1215{ TFillEllipseInfo }
1216
1217constructor TFillEllipseInfo.Create(x, y, rx, ry: single);
1218begin
1219 FX := x + 0.5;
1220 FY := y + 0.5;
1221 FRX := abs(rx);
1222 FRY := abs(ry);
1223 WindingFactor := 1;
1224 FSliceIndex:= -1;
1225end;
1226
1227function TFillEllipseInfo.GetBounds: TRect;
1228begin
1229 Result := rect(floor(fx - frx), floor(fy - fry), ceil(fx + frx), ceil(fy + fry));
1230end;
1231
1232function TFillEllipseInfo.SegmentsCurved: boolean;
1233begin
1234 Result:= true;
1235end;
1236
1237function TFillEllipseInfo.GetSliceIndex: integer;
1238begin
1239 Result:= FSliceIndex;
1240end;
1241
1242function TFillEllipseInfo.GetCenter: TPointF;
1243begin
1244 result := PointF(FX-0.5,FY-0.5);
1245end;
1246
1247function TFillEllipseInfo.NbMaxIntersection: integer;
1248begin
1249 Result := 2;
1250end;
1251
1252procedure TFillEllipseInfo.ComputeIntersection(cury: single;
1253 var inter: ArrayOfTIntersectionInfo; var nbInter: integer);
1254var
1255 d: single;
1256begin
1257 if (FRY <= 0) or (FRX <= 0) then exit;
1258 d := sqr((cury - FY) / FRY);
1259 if d < 1 then
1260 begin
1261 d := sqrt(1 - d) * FRX;
1262 inter[nbinter].SetValues( FX - d, -windingFactor, 0);
1263 Inc(nbinter);
1264 inter[nbinter].SetValues( FX + d, windingFactor, 1);
1265 Inc(nbinter);
1266 FSliceIndex := 0;
1267 end else
1268 begin
1269 if cury < FY then
1270 FSliceIndex:= -1
1271 else
1272 FSliceIndex:= 1;
1273 end;
1274end;
1275
1276{ TFillBorderEllipseInfo }
1277
1278constructor TFillBorderEllipseInfo.Create(x, y, rx, ry, w: single);
1279begin
1280 if rx < 0 then
1281 rx := -rx;
1282 if ry < 0 then
1283 ry := -ry;
1284 FOuterBorder := TFillEllipseInfo.Create(x, y, rx + w / 2, ry + w / 2);
1285 if (rx > w / 2) and (ry > w / 2) then
1286 begin
1287 FInnerBorder := TFillEllipseInfo.Create(x, y, rx - w / 2, ry - w / 2);
1288 FInnerBorder.WindingFactor := -1;
1289 end
1290 else
1291 FInnerBorder := nil;
1292end;
1293
1294function TFillBorderEllipseInfo.GetBounds: TRect;
1295begin
1296 Result := FOuterBorder.GetBounds;
1297end;
1298
1299function TFillBorderEllipseInfo.SegmentsCurved: boolean;
1300begin
1301 Result:= FOuterBorder.SegmentsCurved;
1302 if FInnerBorder <> nil then result := result or FInnerBorder.SegmentsCurved;
1303end;
1304
1305function TFillBorderEllipseInfo.NbMaxIntersection: integer;
1306begin
1307 Result := 4;
1308end;
1309
1310procedure TFillBorderEllipseInfo.ComputeIntersection(cury: single;
1311 var inter: ArrayOfTIntersectionInfo; var nbInter: integer);
1312begin
1313 FOuterBorder.ComputeIntersection(cury, inter, nbInter);
1314 if FInnerBorder <> nil then
1315 FInnerBorder.ComputeIntersection(cury, inter, nbInter);
1316end;
1317
1318destructor TFillBorderEllipseInfo.Destroy;
1319begin
1320 FOuterBorder.Free;
1321 if FInnerBorder <> nil then
1322 FInnerBorder.Free;
1323 inherited Destroy;
1324end;
1325
1326function TFillBorderEllipseInfo.GetSliceIndex: integer;
1327begin
1328 Result:= FOuterBorder.GetSliceIndex;
1329end;
1330
1331{ TFillRoundRectangleInfo }
1332
1333constructor TFillRoundRectangleInfo.Create(x1, y1, x2, y2, rx, ry: single; options: TRoundRectangleOptions; APixelCenteredCoordinates: boolean);
1334var
1335 temp: Single;
1336begin
1337 if y1 > y2 then
1338 begin
1339 temp := y1;
1340 y1 := y2;
1341 y2 := temp;
1342 end;
1343 if x1 > x2 then
1344 begin
1345 temp := x1;
1346 x1 := x2;
1347 x2 := temp;
1348 end;
1349 if APixelCenteredCoordinates then
1350 begin
1351 FX1 := x1 + 0.5;
1352 FY1 := y1 + 0.5;
1353 FX2 := x2 + 0.5;
1354 FY2 := y2 + 0.5;
1355 end else
1356 begin
1357 FX1 := x1;
1358 FY1 := y1;
1359 FX2 := x2;
1360 FY2 := y2;
1361 end;
1362 FRX := abs(rx);
1363 FRY := abs(ry);
1364 if 2*FRX > x2-x1 then FRX := (x2-x1)/2;
1365 if 2*FRY > y2-y1 then FRY := (y2-y1)/2;
1366 FOptions:= options;
1367 WindingFactor := 1;
1368end;
1369
1370function TFillRoundRectangleInfo.SegmentsCurved: boolean;
1371begin
1372 if (not (rrTopLeftSquare in FOptions) and not (rrTopLeftBevel in FOptions)) or
1373 (not (rrTopRightSquare in FOptions) and not (rrTopRightBevel in FOptions)) or
1374 (not (rrBottomRightSquare in FOptions) and not (rrBottomRightBevel in FOptions)) or
1375 (not (rrBottomLeftSquare in FOptions) and not (rrBottomLeftBevel in FOptions)) then
1376 result := true else result := false;
1377end;
1378
1379function TFillRoundRectangleInfo.GetBounds: TRect;
1380begin
1381 result := rect(floor(fx1),floor(fy1),floor(fx2)+1,floor(fy2)+1);
1382end;
1383
1384function TFillRoundRectangleInfo.GetBottomRight: TPointF;
1385begin
1386 result := PointF(FX2-0.5,FY2-0.5);
1387end;
1388
1389function TFillRoundRectangleInfo.GetTopLeft: TPointF;
1390begin
1391 result := PointF(FX1-0.5,FY1-0.5);
1392end;
1393
1394function TFillRoundRectangleInfo.NbMaxIntersection: integer;
1395begin
1396 result := 2;
1397end;
1398
1399procedure TFillRoundRectangleInfo.ComputeIntersection(cury: single;
1400 var inter: ArrayOfTIntersectionInfo; var nbInter: integer);
1401var
1402 d,d2: single;
1403begin
1404 if (cury >= FY1) and (cury <= FY2) then
1405 begin
1406 if cury < FY1+FRY then
1407 begin
1408 d := abs((cury - (FY1+FRY)) / FRY);
1409 d2 := sqrt(1 - sqr(d)) * FRX;
1410
1411 if rrTopLeftSquare in FOptions then
1412 inter[nbinter].interX := FX1 else
1413 if rrTopLeftBevel in FOptions then
1414 inter[nbinter].interX := FX1 + d*FRX
1415 else
1416 inter[nbinter].interX := FX1 + FRX - d2;
1417 inter[nbinter].winding := -windingFactor;
1418 inter[nbinter].numSegment := 0;
1419 Inc(nbinter);
1420
1421 if rrTopRightSquare in FOptions then
1422 inter[nbinter].interX := FX2 else
1423 if rrTopRightBevel in FOptions then
1424 inter[nbinter].interX := FX2 - d*FRX
1425 else
1426 inter[nbinter].interX := FX2 - FRX + d2;
1427 inter[nbinter].winding := +windingFactor;
1428 inter[nbinter].numSegment := 1;
1429 Inc(nbinter);
1430 end else
1431 if cury > FY2-FRY then
1432 begin
1433 d := abs((cury - (FY2-FRY)) / FRY);
1434 d2 := sqrt(1 - sqr(d)) * FRX;
1435
1436 if rrBottomLeftSquare in FOptions then
1437 inter[nbinter].interX := FX1 else
1438 if rrBottomLeftBevel in FOptions then
1439 inter[nbinter].interX := FX1 + d*FRX
1440 else
1441 inter[nbinter].interX := FX1 + FRX - d2;
1442 inter[nbinter].winding := -windingFactor;
1443 inter[nbinter].numSegment := 0;
1444 Inc(nbinter);
1445
1446 if rrBottomRightSquare in FOptions then
1447 inter[nbinter].interX := FX2 else
1448 if rrBottomRightBevel in FOptions then
1449 inter[nbinter].interX := FX2 - d*FRX
1450 else
1451 inter[nbinter].interX := FX2 - FRX + d2;
1452 inter[nbinter].winding := +windingFactor;
1453 inter[nbinter].numSegment := 1;
1454 Inc(nbinter);
1455 end else
1456 begin
1457 inter[nbinter].interX := FX1;
1458 inter[nbinter].winding := -windingFactor;
1459 inter[nbinter].numSegment := 0;
1460 Inc(nbinter);
1461 inter[nbinter].interX := FX2;
1462 inter[nbinter].winding := +windingFactor;
1463 inter[nbinter].numSegment := 1;
1464 Inc(nbinter);
1465 end;
1466 end;
1467end;
1468
1469{ TFillBorderRoundRectInfo }
1470
1471constructor TFillBorderRoundRectInfo.Create(x1, y1, x2, y2, rx, ry, w: single; options: TRoundRectangleOptions; APixelCenteredCoordinates: boolean);
1472var rdiff: single;
1473 temp: Single;
1474begin
1475 if y1 > y2 then
1476 begin
1477 temp := y1;
1478 y1 := y2;
1479 y2 := temp;
1480 end;
1481 if x1 > x2 then
1482 begin
1483 temp := x1;
1484 x1 := x2;
1485 x2 := temp;
1486 end;
1487
1488 if rx < 0 then
1489 rx := -rx;
1490 if ry < 0 then
1491 ry := -ry;
1492 if 2*rx > x2-x1 then rx := (x2-x1)/2;
1493 if 2*ry > y2-y1 then ry := (y2-y1)/2;
1494 rdiff := w*(sqrt(2)-1);
1495 FOuterBorder := TFillRoundRectangleInfo.Create(x1-w/2,y1-w/2,x2+w/2,y2+w/2, rx+rdiff, ry+rdiff, options, APixelCenteredCoordinates);
1496 if (abs(x2-x1) > w) and (abs(y2-y1) > w) then
1497 begin
1498 if (rx-rdiff <= 0) or (ry-rdiff <= 0) then
1499 FInnerBorder := TFillRoundRectangleInfo.Create(x1+w/2, y1+w/2, x2-w/2, y2-w/2, 0,0, options, APixelCenteredCoordinates)
1500 else
1501 FInnerBorder := TFillRoundRectangleInfo.Create(x1+w/2, y1+w/2, x2-w/2, y2-w/2, rx-rdiff, ry-rdiff, options, APixelCenteredCoordinates);
1502 FInnerBorder.WindingFactor := -1;
1503 end
1504 else
1505 FInnerBorder := nil;
1506end;
1507
1508function TFillBorderRoundRectInfo.GetBounds: TRect;
1509begin
1510 result := FOuterBorder.GetBounds;
1511end;
1512
1513function TFillBorderRoundRectInfo.SegmentsCurved: boolean;
1514begin
1515 Result:= FOuterBorder.SegmentsCurved;
1516 if FInnerBorder <> nil then result := result or FInnerBorder.SegmentsCurved;
1517end;
1518
1519function TFillBorderRoundRectInfo.NbMaxIntersection: integer;
1520begin
1521 Result := 4;
1522end;
1523
1524procedure TFillBorderRoundRectInfo.ComputeIntersection(cury: single;
1525 var inter: ArrayOfTIntersectionInfo; var nbInter: integer);
1526begin
1527 FOuterBorder.ComputeIntersection(cury, inter, nbInter);
1528 if FInnerBorder <> nil then
1529 FInnerBorder.ComputeIntersection(cury, inter, nbInter);
1530end;
1531
1532destructor TFillBorderRoundRectInfo.Destroy;
1533begin
1534 FOuterBorder.Free;
1535 FInnerBorder.Free;
1536 inherited Destroy;
1537end;
1538
1539initialization
1540
1541 Randomize;
1542
1543end.
1544
Note: See TracBrowser for help on using the repository browser.