Winform_GDI+绘制轮廓文字

先上效果图:

功能介绍:输入文字使用GraphicsPath绘制出文字,然后获取绘制的文字坐标,再根据文字坐标动态绘制文字。

一.画板上先绘制出文字,相关代码如下:

  private GraphicsPath textPath = new GraphicsPath();
  private Font textFont = new Font("Arial", 164, FontStyle.Regular);
  private RectangleF textRect;
  
  public void CreateTextPath(string value)
  {
      textPath.Reset();
      if (!string.IsNullOrWhiteSpace(value))
      {
          var sf = new StringFormat
          {
              Alignment = StringAlignment.Center,
              LineAlignment = StringAlignment.Center
          };
          var size = TextRenderer.MeasureText(value, textFont);
          var textPosition = new PointF(this.Width / 2.0f - size.Width / 2.0f,this.Height / 2.0f - size.Height / 2.0f);
          textRect = new RectangleF(textPosition, size);
          textPath.AddString(value, textFont.FontFamily, (int)textFont.Style, textFont.Size, textRect, sf);

      }
      this.Invalidate();//重绘
  }

二.获取画板上绘制文字的坐标,这里就从GraphicsPath类中取出文字的点坐标(PathPoints)和点类型(PathTypes)。PathTypes是个枚举类型,简单点来说就是一个点坐标对应一个点类型,类型如下所示:

    //指定 System.Drawing.Drawing2D.GraphicsPath 对象中点的类型。
    public enum PathPointType
    {
        // 起始点。
        Start = 0,
        // 连线线段。
        Line = 1,
        // 立体贝塞尔曲线。
        Bezier3 = 3,
        // 默认贝塞尔曲线。
        Bezier = 3,
        //遮盖点。
        PathTypeMask = 7,
        // 对应线段为虚线。
        DashMode = 16,
        // 路径标记。
        PathMarker = 32,
        // 子路径的终结点。
        CloseSubpath = 128,
    }

   private List<PointF> textPoints = new List<PointF>();
   private List<byte> textPathTypes = new List<byte>();
        public void ParserPathPoint()
        {
            textPoints.Clear();
            textPathTypes.Clear();
            using (var m=  new Matrix())
                textPath.Flatten(m, 0.1f);//将曲线段转换为线段,点坐标类型就会只有线段类型
            textPoints.AddRange(textPath.PathPoints);
            textPathTypes.AddRange(textPath.PathTypes);
        }

三.根据点坐标类型和点坐标绘制出文字,这里需要判断每个点的坐标类型,相关代码如下:

    for (int i = 0; i < textPoints.Count; i++){
       var pathPointType = (PathPointType)(textPathTypes[i] & (byte)PathPointType.PathTypeMask);
      if(pathPointType == PathPointType.Start)//如果是起点,则跳过
      {
          continue;
      }
      else
      {                       
          PointF currentPoint = textPoints[i];
          if (pathPointType == PathPointType.Line) // 绘制线段
          {
              PointF prevPoint = textPoints[i - 1];
              g.DrawLine(Pens.Red, prevPoint, currentPoint);
          }
          else if (pathPointType == PathPointType.Bezier)
          {
              PointF prevPoint = textPoints[i - 1];
              g.DrawBezier(Pens.Red, prevPoint, textPoints[i], textPoints[i + 1], textPoints[i + 2]);
              i += 2;
          }
          bool isClosed = ((textPathTypes[i] & (byte)PathPointType.CloseSubpath) == (byte)PathPointType.CloseSubpath);
          if (isClosed) //检查当前点是否是路径的终点
          {
              var pathMarker = (PathPointType)(textPathTypes[i] & (byte)PathPointType.PathMarker);
              if (pathMarker == PathPointType.PathMarker)
              {
                  continue;
              }
              if (pathPointType == PathPointType.Bezier)
              {
                  var index = GetStartPointIndex(i - 1);
                  CalculateControlPoints(textPoints[i], textPoints[index],out PointF point1,out PointF point2);
                  g.DrawBezier(Pens.Red,textPoints[i], point1,point2, textPoints[index]);
              }
              else
              {
                  g.DrawLine(Pens.Red, currentPoint, textPoints[GetStartPointIndex(i-1)]);
              }
          }
      }

    }
   #region CalculateControlPoints   
   private void CalculateControlPoints(PointF start, PointF end, out PointF cp1, out PointF cp2)
   {
       // 计算起点到终点的方向向量
       float dx = end.X - start.X;
       float dy = end.Y - start.Y;

       // 控制点距离(可以根据需要调整)
       float distance = Math.Min(Math.Abs(dx), Math.Abs(dy)) * 0.5f;

       // 控制点1:起点方向
       cp1 = new PointF(start.X + dx * 0.25f, start.Y + dy * 0.25f);

       // 控制点2:终点方向
       cp2 = new PointF(end.X - dx * 0.25f, end.Y - dy * 0.25f);
   } 
   #endregion

1.点数组的第一个点的类型都为PathPointType.Start,如果是PathPointType.Line或PathPointType.Bezier也会被改为PathPointType.Start。

2.PathPointType.Bezier:一段Bezier曲线有4个点,多段Bezier曲线需要3N+1个点。

3.点坐标有131这样的值,它是PathPointType.Bezier与PathPointType.CloseSubpath的和,表示该点是Bezier曲线而且还是子路径的结束点。

4.PathPointType.PathMarker:代码中可以看出它是与前三个点坐标类型一起使用,进行"&"操作后得到点的类型。

关于动态绘制文字,可以使用定时器控制每秒绘制多少个坐标点,就可以实现以上效果。

写在最后:

GDI+学习者,文笔不好,请勿喷!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zxy644492473

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值