图书馆能画粗线吗?

can the CImg library draw thick lines

本文关键字:图书馆      更新时间:2023-10-16

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

显然,这是不可能的'开箱即用',但创建自己的例程,多次调用CImg的'draw_line()'例程,一个或两个像素的移动应该给你你想要的结果,没有太多的工作。

此函数可将粗线绘制为多边形。

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

基本上这段代码与@vll的回答相同,但也处理(x1-x2)/(y1-y2) < 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);
   }