Ignore:
Timestamp:
Dec 22, 2016, 8:49:19 PM (7 years ago)
Author:
chronos
Message:
  • Modified: Updated BGRABitmap package.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • GraphicTest/Packages/bgrabitmap/bgrapen.pas

    r472 r494  
    1212
    1313uses
    14   SysUtils, Graphics, BGRABitmapTypes;
     14  SysUtils, BGRAGraphics, BGRABitmapTypes, BGRATransform;
    1515
    1616var   //predefined pen styles
     
    1818
    1919type
     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
    2065  TBGRAPolyLineOption = (plRoundCapOpen, //specifies that the line ending is opened
    2166                         plCycle,        //specifies that it is a polygon
     
    2671  TComputeArrowHeadProc = function(const APosition: TPointF; const ADirection: TPointF; const AWidth: single; const ACurrentPos: single): ArrayOfTPointF of object;
    2772
    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 
    3473{ Compute the path for a polyline }
    3574function ComputeWidePolylinePoints(const linepts: array of TPointF; width: single;
    3675          pencolor: TBGRAPixel; linecap: TPenEndCap; joinstyle: TPenJoinStyle; const penstyle: TBGRAPenStyle;
    37           options: TBGRAPolyLineOptions; miterLimit: single = 2; arrowStart: TComputeArrowHeadProc = nil; wantedStartArrowPos: single = 0; arrowEnd: TComputeArrowHeadProc = nil; WantedEndArrowPos: single = 0): ArrayOfTPointF;
     76          options: TBGRAPolyLineOptions; miterLimit: single = 2; arrow: TBGRACustomArrow = nil): ArrayOfTPointF;
    3877
    3978{ Compute the path for a poly-polyline }
    4079function ComputeWidePolyPolylinePoints(const linepts: array of TPointF; width: single;
    4180          pencolor: TBGRAPixel; linecap: TPenEndCap; joinstyle: TPenJoinStyle; const penstyle: TBGRAPenStyle;
    42           options: TBGRAPolyLineOptions; miterLimit: single = 2; arrowStart: TComputeArrowHeadProc = nil; arrowStartPos: single = 0; arrowEnd: TComputeArrowHeadProc = nil; arrowEndPos: single = 0): ArrayOfTPointF;
     81          options: TBGRAPolyLineOptions; miterLimit: single = 2; arrow: TBGRACustomArrow = nil): ArrayOfTPointF;
    4382
    4483{--------------------- Pixel line procedures --------------------------}
     
    106145    end;
    107146    dest.VertLine(X1,Y1,Y2,c, ADrawMode);
     147        Exit;
    108148  end;
    109149
     
    690730  styleLength := 0;
    691731  styleIndex := -1;
     732  remainingDash := 0;
     733  betweenDash   := false;
    692734  for i := 0 to high(penstyle) do
    693735    if penstyle[i] <= 0 then
     
    745787end;
    746788
    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 var
    751   widePolylinePoints: ArrayOfTPointF;
    752 begin
    753   widePolylinePoints := ComputeWidePolylinePoints(linepts,width,pencolor,linecap,joinstyle,penstyle,options,miterLimit,arrowStart,arrowStartPos,arrowEnd,arrowEndPos);
    754   if scan <> nil then
    755     bmp.FillPolyAntialias(widePolylinePoints,scan)
    756   else
    757     bmp.FillPolyAntialias(widePolylinePoints,pencolor);
    758 end;
    759 
    760789function ComputeWidePolylinePoints(const linepts: array of TPointF; width: single;
    761790          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;
     792const oneOver512 = 1/512;
    763793var
    764794  startArrowPos, startArrowDir, endArrowPos, endArrowDir: TPointF;
     
    9861016    hasStart,hasEnd: boolean;
    9871017  begin
    988     if assigned(arrowStart) and not isEmptyPointF(startArrowPos) then
    989       arrowStartData := arrowStart(startArrowPos, startArrowDir, width, startArrowLinePos)
     1018    if assigned(arrow) and not isEmptyPointF(startArrowPos) then
     1019      arrowStartData := arrow.ComputeStartAt(startArrowPos, startArrowDir, width, startArrowLinePos)
    9901020    else
    9911021      arrowStartData := nil;
    992     if assigned(arrowEnd) and not isEmptyPointF(endArrowPos) then
    993       arrowEndData := arrowEnd(endArrowPos, endArrowDir, width, endArrowLinePos)
     1022    if assigned(arrow) and not isEmptyPointF(endArrowPos) then
     1023      arrowEndData := arrow.ComputeEndAt(endArrowPos, endArrowDir, width, endArrowLinePos)
    9941024    else
    9951025      arrowEndData := nil;
     
    10331063  linePos: single;
    10341064  startArrowDone,endArrowDone: boolean;
     1065  wantedStartArrowPos,wantedEndArrowPos: single;
    10351066
    10361067begin
     
    10421073    if isEmptyPointF(linepts[i]) then
    10431074    begin
    1044       result := ComputeWidePolyPolylinePoints(linepts,width,pencolor,linecap,joinstyle,penstyle,options,miterLimit);
     1075      result := ComputeWidePolyPolylinePoints(linepts,width,pencolor,linecap,joinstyle,penstyle,options,miterLimit,arrow);
    10451076      exit;
    10461077    end;
     
    10551086  pjsMiter: if miterLimit < 1.001 then maxMiter := hw*1.001 else
    10561087               maxMiter := hw*miterLimit;
     1088  else
     1089    raise Exception.Create('Unknown join style');
    10571090  end;
    10581091
     
    10621095  setlength(pts, length(linepts)+2);
    10631096  for i := 0 to high(linepts) do
    1064     if (nbPts = 0) or (linepts[i] <> pts[nbPts-1]) then
     1097    if (nbPts = 0) or (abs(linepts[i].x-pts[nbPts-1].x)>oneOver512) or (abs(linepts[i].y-pts[nbPts-1].y)>oneOver512) then
    10651098    begin
    10661099      pts[nbPts]:= linePts[i];
    10671100      inc(nbPts);
    10681101    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);
    10711105  if (plCycle in options) and (nbPts > 2) then
    10721106  begin
     
    10781112    pts[nbPts] := pts[1];
    10791113    inc(nbPts);
    1080     linecap := pecRound;
    10811114  end else
    10821115    options -= [plCycle];
     
    10951128  endArrowDir := EmptyPointF;
    10961129  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;
    10991144
    11001145  //init computed points arrays
     
    14111456  width: single; pencolor: TBGRAPixel; linecap: TPenEndCap;
    14121457  joinstyle: TPenJoinStyle; const penstyle: TBGRAPenStyle;
    1413   options: TBGRAPolyLineOptions; miterLimit: single; arrowStart: TComputeArrowHeadProc; arrowStartPos: single; arrowEnd: TComputeArrowHeadProc; arrowEndPos: single): ArrayOfTPointF;
     1458  options: TBGRAPolyLineOptions; miterLimit: single; arrow: TBGRACustomArrow): ArrayOfTPointF;
    14141459
    14151460var
     
    14281473      for j := startIndex to endIndexP1-1 do
    14291474        subPts[j-startIndex] := linepts[j];
    1430       tempWidePolyline := ComputeWidePolylinePoints(subPts,width,pencolor,linecap,joinstyle,penstyle,options,miterLimit,arrowStart,arrowStartPos,arrowEnd,arrowEndPos);
     1475      tempWidePolyline := ComputeWidePolylinePoints(subPts,width,pencolor,linecap,joinstyle,penstyle,options,miterLimit,arrow);
    14311476      if length(results) = nbresults then
    14321477        setlength(results,(nbresults+1)*2);
     
    14701515end;
    14711516
     1517{ TBGRAPenStroker }
     1518
     1519function TBGRAPenStroker.GetArrow: TBGRACustomArrow;
     1520begin
     1521  result := FArrow;
     1522end;
     1523
     1524function TBGRAPenStroker.GetArrowOwned: boolean;
     1525begin
     1526  result := FArrowOwned;
     1527end;
     1528
     1529function TBGRAPenStroker.GetCustomPenStyle: TBGRAPenStyle;
     1530begin
     1531  result := FCustomPenStyle;
     1532end;
     1533
     1534function TBGRAPenStroker.GetJoinStyle: TPenJoinStyle;
     1535begin
     1536  result := FJoinStyle;
     1537end;
     1538
     1539function TBGRAPenStroker.GetLineCap: TPenEndCap;
     1540begin
     1541  result := FLineCap;
     1542end;
     1543
     1544function TBGRAPenStroker.GetMiterLimit: single;
     1545begin
     1546  result := FMiterLimit;
     1547end;
     1548
     1549function TBGRAPenStroker.GetPenStyle: TPenStyle;
     1550begin
     1551  result := FPenStyle;
     1552end;
     1553
     1554function TBGRAPenStroker.GetStrokeMatrix: TAffineMatrix;
     1555begin
     1556  result := FOriginalStrokeMatrix;
     1557end;
     1558
     1559procedure TBGRAPenStroker.SetArrow(AValue: TBGRACustomArrow);
     1560begin
     1561  FArrow := AValue;
     1562end;
     1563
     1564procedure TBGRAPenStroker.SetArrowOwned(AValue: boolean);
     1565begin
     1566  FArrowOwned := AValue;
     1567end;
     1568
     1569procedure TBGRAPenStroker.SetCustomPenStyle(AValue: TBGRAPenStyle);
     1570begin
     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;
     1584end;
     1585
     1586procedure TBGRAPenStroker.SetJoinStyle(AValue: TPenJoinStyle);
     1587begin
     1588  FJoinStyle:= AValue;
     1589end;
     1590
     1591procedure TBGRAPenStroker.SetLineCap(AValue: TPenEndCap);
     1592begin
     1593  FLineCap:= AValue;
     1594end;
     1595
     1596procedure TBGRAPenStroker.SetMiterLimit(AValue: single);
     1597begin
     1598  FMiterLimit := AValue;
     1599end;
     1600
     1601procedure TBGRAPenStroker.SetStrokeMatrix(const AValue: TAffineMatrix);
     1602begin
     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);
     1614end;
     1615
     1616procedure TBGRAPenStroker.SetPenStyle(AValue: TPenStyle);
     1617begin
     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;
     1628end;
     1629
     1630constructor TBGRAPenStroker.Create;
     1631begin
     1632  Style := psSolid;
     1633  LineCap := pecRound;
     1634  JoinStyle := pjsBevel;
     1635  MiterLimit := 2;
     1636  fillchar(FOriginalStrokeMatrix,sizeof(FOriginalStrokeMatrix),0);
     1637  StrokeMatrix := AffineMatrixIdentity;
     1638end;
     1639
     1640destructor TBGRAPenStroker.Destroy;
     1641begin
     1642  if ArrowOwned then FreeAndNil(FArrow);
     1643  inherited Destroy;
     1644end;
     1645
     1646function TBGRAPenStroker.ComputePolyline(const APoints: array of TPointF;
     1647  AWidth: single; AClosedCap: boolean): ArrayOfTPointF;
     1648var
     1649  c: TBGRAPixel;
     1650begin
     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);
     1660end;
     1661
     1662function TBGRAPenStroker.ComputePolyline(const APoints: array of TPointF;
     1663  AWidth: single; APenColor: TBGRAPixel; AClosedCap: boolean): ArrayOfTPointF;
     1664var options: TBGRAPolyLineOptions;
     1665begin
     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);
     1674end;
     1675
     1676function TBGRAPenStroker.ComputePolylineAutocycle(
     1677  const APoints: array of TPointF; AWidth: single): ArrayOfTPointF;
     1678var options: TBGRAPolyLineOptions;
     1679begin
     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)
     1687end;
     1688
     1689function TBGRAPenStroker.ComputePolygon(const APoints: array of TPointF;
     1690  AWidth: single): ArrayOfTPointF;
     1691begin
     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);
     1696end;
     1697
    14721698initialization
    14731699
Note: See TracChangeset for help on using the changeset viewer.