Changeset 494 for GraphicTest/Packages/bgrabitmap/bgrapen.pas
- Timestamp:
- Dec 22, 2016, 8:49:19 PM (7 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
GraphicTest/Packages/bgrabitmap/bgrapen.pas
r472 r494 12 12 13 13 uses 14 SysUtils, Graphics, BGRABitmapTypes;14 SysUtils, BGRAGraphics, BGRABitmapTypes, BGRATransform; 15 15 16 16 var //predefined pen styles … … 18 18 19 19 type 20 21 { TBGRAPenStroker } 22 23 TBGRAPenStroker = class(TBGRACustomPenStroker) 24 protected 25 { Pen style can be defined by PenStyle property of by CustomPenStyle property. 26 When PenStyle property is assigned, CustomPenStyle property is assigned the actual 27 pen pattern. } 28 FCustomPenStyle: TBGRAPenStyle; 29 FPenStyle: TPenStyle; 30 FArrow: TBGRACustomArrow; 31 FArrowOwned: boolean; 32 FOriginalStrokeMatrix,FStrokeMatrix,FStrokeMatrixInverse: TAffineMatrix; 33 FStrokeZoom: single; 34 FStrokeMatrixIdentity: boolean; 35 FLineCap: TPenEndCap; 36 FJoinStyle: TPenJoinStyle; 37 FMiterLimit: single; 38 39 function GetArrow: TBGRACustomArrow; override; 40 function GetArrowOwned: boolean; override; 41 function GetCustomPenStyle: TBGRAPenStyle; override; 42 function GetJoinStyle: TPenJoinStyle; override; 43 function GetLineCap: TPenEndCap; override; 44 function GetMiterLimit: single; override; 45 function GetPenStyle: TPenStyle; override; 46 function GetStrokeMatrix: TAffineMatrix; override; 47 procedure SetArrow(AValue: TBGRACustomArrow); override; 48 procedure SetArrowOwned(AValue: boolean); override; 49 procedure SetCustomPenStyle(AValue: TBGRAPenStyle); override; 50 procedure SetJoinStyle(AValue: TPenJoinStyle); override; 51 procedure SetLineCap(AValue: TPenEndCap); override; 52 procedure SetMiterLimit(AValue: single); override; 53 procedure SetPenStyle(AValue: TPenStyle); override; 54 procedure SetStrokeMatrix(const AValue: TAffineMatrix); override; 55 public 56 constructor Create; 57 destructor Destroy; override; 58 function ComputePolyline(const APoints: array of TPointF; AWidth: single; AClosedCap: boolean = true): ArrayOfTPointF; override; 59 function ComputePolyline(const APoints: array of TPointF; AWidth: single; APenColor: TBGRAPixel; AClosedCap: boolean = true): ArrayOfTPointF; override; 60 function ComputePolylineAutocycle(const APoints: array of TPointF; AWidth: single): ArrayOfTPointF; override; 61 function ComputePolygon(const APoints: array of TPointF; AWidth: single): ArrayOfTPointF; override; 62 63 end; 64 20 65 TBGRAPolyLineOption = (plRoundCapOpen, //specifies that the line ending is opened 21 66 plCycle, //specifies that it is a polygon … … 26 71 TComputeArrowHeadProc = function(const APosition: TPointF; const ADirection: TPointF; const AWidth: single; const ACurrentPos: single): ArrayOfTPointF of object; 27 72 28 { Draw a polyline with specified parameters. If a scanner is specified, it is used as a texture.29 Else the pencolor parameter is used as a solid color. }30 procedure BGRAPolyLine(bmp: TBGRACustomBitmap; const linepts: array of TPointF;31 width: single; pencolor: TBGRAPixel; linecap: TPenEndCap; joinstyle: TPenJoinStyle; const penstyle: TBGRAPenStyle;32 options: TBGRAPolyLineOptions; scan: IBGRAScanner = nil; miterLimit: single = 2; arrowStart: TComputeArrowHeadProc = nil; arrowStartPos: single = 0; arrowEnd: TComputeArrowHeadProc = nil; arrowEndPos: single = 0);33 34 73 { Compute the path for a polyline } 35 74 function ComputeWidePolylinePoints(const linepts: array of TPointF; width: single; 36 75 pencolor: TBGRAPixel; linecap: TPenEndCap; joinstyle: TPenJoinStyle; const penstyle: TBGRAPenStyle; 37 options: TBGRAPolyLineOptions; miterLimit: single = 2; arrow Start: TComputeArrowHeadProc = nil; wantedStartArrowPos: single = 0; arrowEnd: TComputeArrowHeadProc = nil; WantedEndArrowPos: single = 0): ArrayOfTPointF;76 options: TBGRAPolyLineOptions; miterLimit: single = 2; arrow: TBGRACustomArrow = nil): ArrayOfTPointF; 38 77 39 78 { Compute the path for a poly-polyline } 40 79 function ComputeWidePolyPolylinePoints(const linepts: array of TPointF; width: single; 41 80 pencolor: TBGRAPixel; linecap: TPenEndCap; joinstyle: TPenJoinStyle; const penstyle: TBGRAPenStyle; 42 options: TBGRAPolyLineOptions; miterLimit: single = 2; arrow Start: TComputeArrowHeadProc = nil; arrowStartPos: single = 0; arrowEnd: TComputeArrowHeadProc = nil; arrowEndPos: single = 0): ArrayOfTPointF;81 options: TBGRAPolyLineOptions; miterLimit: single = 2; arrow: TBGRACustomArrow = nil): ArrayOfTPointF; 43 82 44 83 {--------------------- Pixel line procedures --------------------------} … … 106 145 end; 107 146 dest.VertLine(X1,Y1,Y2,c, ADrawMode); 147 Exit; 108 148 end; 109 149 … … 690 730 styleLength := 0; 691 731 styleIndex := -1; 732 remainingDash := 0; 733 betweenDash := false; 692 734 for i := 0 to high(penstyle) do 693 735 if penstyle[i] <= 0 then … … 745 787 end; 746 788 747 procedure BGRAPolyLine(bmp: TBGRACustomBitmap; const linepts: array of TPointF; width: single;748 pencolor: TBGRAPixel; linecap: TPenEndCap; joinstyle: TPenJoinStyle; const penstyle: TBGRAPenStyle;749 options: TBGRAPolyLineOptions; scan: IBGRAScanner; miterLimit: single; arrowStart: TComputeArrowHeadProc; arrowStartPos: single; arrowEnd: TComputeArrowHeadProc; arrowEndPos: single);750 var751 widePolylinePoints: ArrayOfTPointF;752 begin753 widePolylinePoints := ComputeWidePolylinePoints(linepts,width,pencolor,linecap,joinstyle,penstyle,options,miterLimit,arrowStart,arrowStartPos,arrowEnd,arrowEndPos);754 if scan <> nil then755 bmp.FillPolyAntialias(widePolylinePoints,scan)756 else757 bmp.FillPolyAntialias(widePolylinePoints,pencolor);758 end;759 760 789 function ComputeWidePolylinePoints(const linepts: array of TPointF; width: single; 761 790 pencolor: TBGRAPixel; linecap: TPenEndCap; joinstyle: TPenJoinStyle; const penstyle: TBGRAPenStyle; 762 options: TBGRAPolyLineOptions; miterLimit: single; arrowStart: TComputeArrowHeadProc; wantedStartArrowPos: single; arrowEnd: TComputeArrowHeadProc; wantedEndArrowPos: single): ArrayOfTPointF; 791 options: TBGRAPolyLineOptions; miterLimit: single; arrow: TBGRACustomArrow): ArrayOfTPointF; 792 const oneOver512 = 1/512; 763 793 var 764 794 startArrowPos, startArrowDir, endArrowPos, endArrowDir: TPointF; … … 986 1016 hasStart,hasEnd: boolean; 987 1017 begin 988 if assigned(arrow Start) and not isEmptyPointF(startArrowPos) then989 arrowStartData := arrow Start(startArrowPos, startArrowDir, width, startArrowLinePos)1018 if assigned(arrow) and not isEmptyPointF(startArrowPos) then 1019 arrowStartData := arrow.ComputeStartAt(startArrowPos, startArrowDir, width, startArrowLinePos) 990 1020 else 991 1021 arrowStartData := nil; 992 if assigned(arrow End) and not isEmptyPointF(endArrowPos) then993 arrowEndData := arrow End(endArrowPos, endArrowDir, width, endArrowLinePos)1022 if assigned(arrow) and not isEmptyPointF(endArrowPos) then 1023 arrowEndData := arrow.ComputeEndAt(endArrowPos, endArrowDir, width, endArrowLinePos) 994 1024 else 995 1025 arrowEndData := nil; … … 1033 1063 linePos: single; 1034 1064 startArrowDone,endArrowDone: boolean; 1065 wantedStartArrowPos,wantedEndArrowPos: single; 1035 1066 1036 1067 begin … … 1042 1073 if isEmptyPointF(linepts[i]) then 1043 1074 begin 1044 result := ComputeWidePolyPolylinePoints(linepts,width,pencolor,linecap,joinstyle,penstyle,options,miterLimit );1075 result := ComputeWidePolyPolylinePoints(linepts,width,pencolor,linecap,joinstyle,penstyle,options,miterLimit,arrow); 1045 1076 exit; 1046 1077 end; … … 1055 1086 pjsMiter: if miterLimit < 1.001 then maxMiter := hw*1.001 else 1056 1087 maxMiter := hw*miterLimit; 1088 else 1089 raise Exception.Create('Unknown join style'); 1057 1090 end; 1058 1091 … … 1062 1095 setlength(pts, length(linepts)+2); 1063 1096 for i := 0 to high(linepts) do 1064 if (nbPts = 0) or ( linepts[i] <> pts[nbPts-1]) then1097 if (nbPts = 0) or (abs(linepts[i].x-pts[nbPts-1].x)>oneOver512) or (abs(linepts[i].y-pts[nbPts-1].y)>oneOver512) then 1065 1098 begin 1066 1099 pts[nbPts]:= linePts[i]; 1067 1100 inc(nbPts); 1068 1101 end; 1069 if (nbPts > 1) and 1070 (pts[nbPts-1] = pts[0]) then dec(nbPts); 1102 if (nbPts > 1) and (plCycle in options) and 1103 (abs(pts[0].x-pts[nbPts-1].x)<=oneOver512) and 1104 (abs(pts[0].y-pts[nbPts-1].y)<=oneOver512) then dec(nbPts); 1071 1105 if (plCycle in options) and (nbPts > 2) then 1072 1106 begin … … 1078 1112 pts[nbPts] := pts[1]; 1079 1113 inc(nbPts); 1080 linecap := pecRound;1081 1114 end else 1082 1115 options -= [plCycle]; … … 1095 1128 endArrowDir := EmptyPointF; 1096 1129 endArrowPos := EmptyPointF; 1097 startArrowDone := @arrowStart = nil; 1098 endArrowDone := @arrowEnd = nil; 1130 if Assigned(arrow) then 1131 begin 1132 wantedStartArrowPos:= arrow.StartOffsetX; 1133 wantedEndArrowPos:= arrow.EndOffsetX; 1134 startArrowDone := not arrow.IsStartDefined; 1135 endArrowDone := not arrow.IsEndDefined; 1136 end 1137 else 1138 begin 1139 wantedStartArrowPos:= 0; 1140 wantedEndArrowPos:= 0; 1141 startArrowDone := true; 1142 endArrowDone := true; 1143 end; 1099 1144 1100 1145 //init computed points arrays … … 1411 1456 width: single; pencolor: TBGRAPixel; linecap: TPenEndCap; 1412 1457 joinstyle: TPenJoinStyle; const penstyle: TBGRAPenStyle; 1413 options: TBGRAPolyLineOptions; miterLimit: single; arrow Start: TComputeArrowHeadProc; arrowStartPos: single; arrowEnd: TComputeArrowHeadProc; arrowEndPos: single): ArrayOfTPointF;1458 options: TBGRAPolyLineOptions; miterLimit: single; arrow: TBGRACustomArrow): ArrayOfTPointF; 1414 1459 1415 1460 var … … 1428 1473 for j := startIndex to endIndexP1-1 do 1429 1474 subPts[j-startIndex] := linepts[j]; 1430 tempWidePolyline := ComputeWidePolylinePoints(subPts,width,pencolor,linecap,joinstyle,penstyle,options,miterLimit,arrow Start,arrowStartPos,arrowEnd,arrowEndPos);1475 tempWidePolyline := ComputeWidePolylinePoints(subPts,width,pencolor,linecap,joinstyle,penstyle,options,miterLimit,arrow); 1431 1476 if length(results) = nbresults then 1432 1477 setlength(results,(nbresults+1)*2); … … 1470 1515 end; 1471 1516 1517 { TBGRAPenStroker } 1518 1519 function TBGRAPenStroker.GetArrow: TBGRACustomArrow; 1520 begin 1521 result := FArrow; 1522 end; 1523 1524 function TBGRAPenStroker.GetArrowOwned: boolean; 1525 begin 1526 result := FArrowOwned; 1527 end; 1528 1529 function TBGRAPenStroker.GetCustomPenStyle: TBGRAPenStyle; 1530 begin 1531 result := FCustomPenStyle; 1532 end; 1533 1534 function TBGRAPenStroker.GetJoinStyle: TPenJoinStyle; 1535 begin 1536 result := FJoinStyle; 1537 end; 1538 1539 function TBGRAPenStroker.GetLineCap: TPenEndCap; 1540 begin 1541 result := FLineCap; 1542 end; 1543 1544 function TBGRAPenStroker.GetMiterLimit: single; 1545 begin 1546 result := FMiterLimit; 1547 end; 1548 1549 function TBGRAPenStroker.GetPenStyle: TPenStyle; 1550 begin 1551 result := FPenStyle; 1552 end; 1553 1554 function TBGRAPenStroker.GetStrokeMatrix: TAffineMatrix; 1555 begin 1556 result := FOriginalStrokeMatrix; 1557 end; 1558 1559 procedure TBGRAPenStroker.SetArrow(AValue: TBGRACustomArrow); 1560 begin 1561 FArrow := AValue; 1562 end; 1563 1564 procedure TBGRAPenStroker.SetArrowOwned(AValue: boolean); 1565 begin 1566 FArrowOwned := AValue; 1567 end; 1568 1569 procedure TBGRAPenStroker.SetCustomPenStyle(AValue: TBGRAPenStyle); 1570 begin 1571 if FCustomPenStyle=AValue then Exit; 1572 FCustomPenStyle:=AValue; 1573 if AValue = SolidPenStyle then FPenStyle := psSolid 1574 else if AValue = ClearPenStyle then FPenStyle:= psClear 1575 else if AValue = DashPenStyle then FPenStyle:= psDash 1576 else if AValue = DotPenStyle then FPenStyle := psDot 1577 else if AValue = DashDotPenStyle then FPenStyle:= psDashDot 1578 else if AValue = DashDotDotPenStyle then FPenStyle:= psDashDotDot 1579 else 1580 begin 1581 FPenStyle := psPattern; 1582 FCustomPenStyle:= DuplicatePenStyle(AValue); 1583 end; 1584 end; 1585 1586 procedure TBGRAPenStroker.SetJoinStyle(AValue: TPenJoinStyle); 1587 begin 1588 FJoinStyle:= AValue; 1589 end; 1590 1591 procedure TBGRAPenStroker.SetLineCap(AValue: TPenEndCap); 1592 begin 1593 FLineCap:= AValue; 1594 end; 1595 1596 procedure TBGRAPenStroker.SetMiterLimit(AValue: single); 1597 begin 1598 FMiterLimit := AValue; 1599 end; 1600 1601 procedure TBGRAPenStroker.SetStrokeMatrix(const AValue: TAffineMatrix); 1602 begin 1603 if FOriginalStrokeMatrix=AValue then Exit; 1604 FOriginalStrokeMatrix:=AValue; 1605 FStrokeMatrix := AValue; 1606 FStrokeMatrix[1,3] := 0; 1607 FStrokeMatrix[2,3] := 0; 1608 FStrokeZoom := max(VectLen(PointF(FStrokeMatrix[1,1],FStrokeMatrix[2,1])), 1609 VectLen(PointF(FStrokeMatrix[1,2],FStrokeMatrix[2,2]))); 1610 if FStrokeZoom > 0 then 1611 FStrokeMatrix *= AffineMatrixScale(1/FStrokeZoom,1/FStrokeZoom); 1612 FStrokeMatrixIdentity := IsAffineMatrixIdentity(FStrokeMatrix); 1613 FStrokeMatrixInverse := AffineMatrixInverse(FStrokeMatrix); 1614 end; 1615 1616 procedure TBGRAPenStroker.SetPenStyle(AValue: TPenStyle); 1617 begin 1618 if FPenStyle=AValue then Exit; 1619 Case AValue of 1620 psSolid: FCustomPenStyle := SolidPenStyle; 1621 psDash: FCustomPenStyle := DashPenStyle; 1622 psDot: FCustomPenStyle := DotPenStyle; 1623 psDashDot: FCustomPenStyle := DashDotPenStyle; 1624 psDashDotDot: FCustomPenStyle := DashDotDotPenStyle; 1625 else FCustomPenStyle := ClearPenStyle; 1626 end; 1627 FPenStyle := AValue; 1628 end; 1629 1630 constructor TBGRAPenStroker.Create; 1631 begin 1632 Style := psSolid; 1633 LineCap := pecRound; 1634 JoinStyle := pjsBevel; 1635 MiterLimit := 2; 1636 fillchar(FOriginalStrokeMatrix,sizeof(FOriginalStrokeMatrix),0); 1637 StrokeMatrix := AffineMatrixIdentity; 1638 end; 1639 1640 destructor TBGRAPenStroker.Destroy; 1641 begin 1642 if ArrowOwned then FreeAndNil(FArrow); 1643 inherited Destroy; 1644 end; 1645 1646 function TBGRAPenStroker.ComputePolyline(const APoints: array of TPointF; 1647 AWidth: single; AClosedCap: boolean): ArrayOfTPointF; 1648 var 1649 c: TBGRAPixel; 1650 begin 1651 if not AClosedCap then 1652 c := BGRAWhite //needed for alpha junction 1653 else 1654 c := BGRAPixelTransparent; 1655 1656 if FStrokeMatrixIdentity then 1657 result := ComputePolyline(APoints,AWidth*FStrokeZoom,c,AClosedCap) 1658 else 1659 result := FStrokeMatrix*ComputePolyline(FStrokeMatrixInverse*APoints,AWidth*FStrokeZoom,c,AClosedCap); 1660 end; 1661 1662 function TBGRAPenStroker.ComputePolyline(const APoints: array of TPointF; 1663 AWidth: single; APenColor: TBGRAPixel; AClosedCap: boolean): ArrayOfTPointF; 1664 var options: TBGRAPolyLineOptions; 1665 begin 1666 options := []; 1667 if Assigned(Arrow) and Arrow.IsStartDefined then options += [plNoStartCap]; 1668 if Assigned(Arrow) and Arrow.IsEndDefined then options += [plNoEndCap]; 1669 if not AClosedCap then options += [plRoundCapOpen]; 1670 if FStrokeMatrixIdentity then 1671 result := BGRAPen.ComputeWidePolylinePoints(APoints, AWidth*FStrokeZoom, APenColor, LineCap, JoinStyle, CustomPenStyle, options, MiterLimit, Arrow) 1672 else 1673 result := FStrokeMatrix*BGRAPen.ComputeWidePolylinePoints(FStrokeMatrixInverse*APoints, AWidth*FStrokeZoom, APenColor, LineCap, JoinStyle, CustomPenStyle, options, MiterLimit, Arrow); 1674 end; 1675 1676 function TBGRAPenStroker.ComputePolylineAutocycle( 1677 const APoints: array of TPointF; AWidth: single): ArrayOfTPointF; 1678 var options: TBGRAPolyLineOptions; 1679 begin 1680 options := [plAutoCycle]; 1681 if Assigned(Arrow) and Arrow.IsStartDefined then options += [plNoStartCap]; 1682 if Assigned(Arrow) and Arrow.IsEndDefined then options += [plNoEndCap]; 1683 if FStrokeMatrixIdentity then 1684 result := BGRAPen.ComputeWidePolylinePoints(APoints, AWidth*FStrokeZoom, BGRAPixelTransparent, LineCap, JoinStyle, CustomPenStyle, options, MiterLimit, Arrow) 1685 else 1686 result := FStrokeMatrix*BGRAPen.ComputeWidePolylinePoints(FStrokeMatrixInverse*APoints, AWidth*FStrokeZoom, BGRAPixelTransparent, LineCap, JoinStyle, CustomPenStyle, options, MiterLimit, Arrow) 1687 end; 1688 1689 function TBGRAPenStroker.ComputePolygon(const APoints: array of TPointF; 1690 AWidth: single): ArrayOfTPointF; 1691 begin 1692 if FStrokeMatrixIdentity then 1693 result := BGRAPen.ComputeWidePolylinePoints(APoints, AWidth*FStrokeZoom, BGRAPixelTransparent, LineCap, JoinStyle, CustomPenStyle, [plCycle], MiterLimit) 1694 else 1695 result := FStrokeMatrix*BGRAPen.ComputeWidePolylinePoints(FStrokeMatrixInverse*APoints, AWidth*FStrokeZoom, BGRAPixelTransparent, LineCap, JoinStyle, CustomPenStyle, [plCycle], MiterLimit); 1696 end; 1697 1472 1698 initialization 1473 1699
Note:
See TracChangeset
for help on using the changeset viewer.