Может ли библиотека CImg рисовать толстые линии
Я использовал библиотеку CImg и был доволен тем, насколько легко интегрировать и использовать. Однако теперь я хочу нарисовать толстые линии (то есть толщиной более одного пикселя). Это не ясно из документации API draw_line
Функция ( здесь), как это можно сделать. Вторая версия функции (чуть ниже первой в документации) даже принимает текстуру в качестве входных данных, но опять же без ширины. Кажется странным, что у такой всеобъемлющей библиотеки не было бы этой функции. Возможно, это должно быть сделано с помощью какого-то преобразования? Я знаю, что мог бы сделать это, используя многоугольник (то есть прямоугольник, где я бы вычислял углы многоугольника, используя нормаль к линии), но я боюсь, что это будет значительно медленнее.
3 ответа
По-видимому, это не возможно "из коробки", но создание собственной подпрограммы, которая несколько раз вызывает подпрограмму "draw_line()" CImg, со сдвигом на один или два пикселя, должно дать вам желаемый результат без много работы.
Эта функция может использоваться для рисования толстых линий в виде многоугольников.
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;
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 цвета. Первый раз использую эту функцию, второй раз рисую одну линию шириной 1 px используя image.draw_line()
,
- От 1000,1000 до 2000,2000: 216 мкс / 123 мкс
- 2000,2000 до 8000,4000: 588 мкс / 151 мкс
- 3000-13020.1000: 21 мкс / 5 мкс
В основном этот код делает то же самое, что и ответ @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);
}