【问题标题】:can the CImg library draw thick linesCImg库可以画粗线吗
【发布时间】:2011-04-15 07:09:32
【问题描述】:

我一直在使用 CImg 库,并且对集成和使用的便捷性感到满意。但是,我现在想画粗线(即超过一个像素的粗线)。从 draw_line 函数 (here) 的 API 文档中不清楚如何做到这一点。该函数的第二个版本(就在文档中第一个版本的下方)甚至将纹理作为输入,但同样没有宽度。这么全面的库居然没有这个功能,似乎很奇怪。也许应该使用某种转换来完成?我知道我可以使用多边形(即一个矩形,我将使用直线的法线计算多边形的角)来做到这一点,但我担心这会慢得多。

【问题讨论】:

    标签: c++ line cimg thickness


    【解决方案1】:

    显然,“开箱即用”是不可能的,但是创建自己的例程,多次调用 CImg 的“draw_line()”例程,一到两个像素移位应该会给你结果想要,无需太多工作。

    【讨论】:

    • 谢谢,但我不相信这对于任意角度的线都能顺利工作。我将不得不计算线的法线并在该方向上偏移“一个像素”。但这通常是一个小数,所以由于舍入以及 Bresenham 算法中的混叠,我不希望额外的行会与第一行完全一致,我最终会得到一个组合间隙像素(线条中的“孔”)和重叠像素,线条只有一个像素厚。
    • 实际上,在考虑了更多之后,我认为您的建议对我有用。由于我只希望能够绘制最大约 4 像素的宽度,因此我可以更加小心地选择线条的偏移量。与其试图精确匹配法线角度,我可以根据大致角度通过一组有限的选择((1,1)或(1,0)或(0,-1)等)来抵消我的线,如果我在理论上保持线长相同(?)布雷森汉姆算法将使偏移线与原始线正确“勺子”。
    • 实际上,通过绘制几条偏移一两个像素的线,结果证明是行不通的。画线算法使得在填充线内获得未填充像素和条纹是可能的并且相当普遍。我最终在 CImg 中使用了 draw_polygon() 函数,它为此目的工作得很好。例如,要绘制从 (0, 0) 到 (10, 10) 的两像素宽的线,我填充由 (0, 0)、(10,10)、(11,10)、(1) 勾勒的多边形,0)。我没有将这种方式与绘制多条线的速度进行比较,但它对于我的目的来说已经足够好了。
    • 嗯,我用多边形画粗线的方法也不是很好。我最终通过在每个点上绘制一个矩形来重做它。这很好用,对我的目的来说足够快。
    【解决方案2】:

    此功能可用于将粗线绘制为多边形。

    void draw_line(cimg_library::CImg<uint8_t>& image,
        const int x1, const int y1,
        const int x2, const int y2,
        const uint8_t* const color,
        const unsigned int line_width)
    {
        if (x1 == x2 && y1 == y2) {
            return;
        }
        // Convert line (p1, p2) to polygon (pa, pb, pc, pd)
        const double x_diff = std::abs(x1 - x2);
        const double y_diff = std::abs(y1 - y2);
        const double w_diff = line_width / 2.0;
    
        // Triangle between pa and p1: x_adj^2 + y_adj^2 = w_diff^2
        // Triangle between p1 and p2: x_diff^2 + y_diff^2 = length^2 
        // Similar triangles: y_adj / x_diff = x_adj / y_diff = w_diff / length
        // -> y_adj / x_diff = w_diff / sqrt(x_diff^2 + y_diff^2) 
        const int x_adj = y_diff * w_diff / std::sqrt(std::pow(x_diff, 2) + std::pow(y_diff, 2));
        const int y_adj = x_diff * w_diff / std::sqrt(std::pow(x_diff, 2) + std::pow(y_diff, 2));
    
        // Points are listed in clockwise order, starting from top-left
        cimg_library::CImg<int> points(4, 2);
        points(0, 0) = x1 - x_adj;
        points(0, 1) = y1 + y_adj;
        points(1, 0) = x1 + x_adj;
        points(1, 1) = y1 - y_adj;
        points(2, 0) = x2 + x_adj;
        points(2, 1) = y2 - y_adj;
        points(3, 0) = x2 - x_adj;
        points(3, 1) = y2 + y_adj;
    
        image.draw_polygon(points, color);
    }
    

    line_width 20 和 3 种颜色的基准。第一次用这个功能,第二次用image.draw_line()画单条1px宽的线。

    • 1000,1000 到 2000,2000:216 µs / 123 µs
    • 2000,2000 到 8000,4000:588 µs / 151 µs
    • 3000,1000 到 3020,1000:21 µs / 5 µs

    【讨论】:

    • @MKatz 我添加了一些测量值。
    • 谢谢!也有兴趣看到一些关于线条如何围绕各种类型的多边形/折线角度的图片。
    • x_adj 和 y_adj 是做什么的?
    • @Paul 我们正在将一条线(两个点)转换为一个矩形(四个点)。对于p1,必须通过添加垂直于线的w_diff 将其转换为两个点papbx_adjy_adj 是与原点的区别。我添加了值的计算方式。
    【解决方案3】:

    基本上这段代码和@vll的回答一样,但也处理(x1-x2)/(y1-y2) &lt; 0时的情况(我删除了abs函数)。

    void draw_line(cimg_library::CImg<uint8_t>& image,
          const int x1, const int y1,
          const int x2, const int y2,
          const uint8_t* const color,
          const uint8_t line_width,
          const double opacity=1.0)
       {
          if (x1 == x2 && y1 == y2) {
             return;
          }
          // Convert line (p1, p2) to polygon (pa, pb, pc, pd)
          const double x_diff = (x1 - x2);
          const double y_diff = (y1 - y2);
          const double w_diff = line_width / 2.0;
    
          // Triangle between pa and p1: x_adj^2 + y_adj^2 = w_diff^2
          // Triangle between p1 and p2: x_diff^2 + y_diff^2 = length^2 
          // Similar triangles: y_adj / x_diff = x_adj / y_diff = w_diff / length
          // -> y_adj / x_diff = w_diff / sqrt(x_diff^2 + y_diff^2) 
          const int x_adj = y_diff * w_diff / std::sqrt(std::pow(x_diff, 2) + std::pow(y_diff, 2));
          const int y_adj = x_diff * w_diff / std::sqrt(std::pow(x_diff, 2) + std::pow(y_diff, 2));
    
          // Points are listed in clockwise order, starting from top-left
          cimg_library::CImg<int> points(4, 2);
          points(0, 0) = x1 - x_adj;
          points(0, 1) = y1 + y_adj;
          points(1, 0) = x1 + x_adj;
          points(1, 1) = y1 - y_adj;
          points(2, 0) = x2 + x_adj;
          points(2, 1) = y2 - y_adj;
          points(3, 0) = x2 - x_adj;
          points(3, 1) = y2 + y_adj;
    
          image.draw_polygon(points, color, opacity);
       }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-02
      • 2019-12-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多