Рисование идеальных по пикселям кругов с помощью Qt - PullRequest
0 голосов
/ 20 февраля 2019

Я пытаюсь использовать QPainter::drawEllipse для рисования кругов.Я хочу иметь возможность:

  • установить ширину обводки круга (QPen::width)
  • выбрать форму пикселей, которые находятся в центре круга(1x1, 1x2, 2x1 или 2x2)
  • при желании закрашивайте круг вместо штрихов
  • убедитесь, что круг имеет правильный радиус (даже если штрихширина больше 1)

Эти цели на удивление труднодостижимы.Это пример того, что я хочу сделать (нарисовано от руки):

Example

Изображение 32x32 (масштабировано до 512x512).Красная центральная точка находится в (15, 15).Центр - 1x2, поэтому под центральным пикселем есть дополнительный красный пиксель.Штрих имеет ширину 2 пикселя.Если обводка стала шире, пиксели будут добавлены внутрь круга.Ограничительная рамка круга одинакова независимо от ширины обводки.Радиус 8 пикселей.Каждая из синих линий имеет длину 8 пикселей. Просто чтобы прояснить, красные и синие пиксели как раз для описания круга.Они не являются частью моего желаемого результата.

То, к чему в действительности сводится моя проблема, это рендеринг эллипса, который идеально вписывается в прямоугольник.Я могу вычислить прямоугольник, используя центральную точку, радиус и форму центра.Эта часть проста.Простой вызов drawEllipse с этим прямоугольником не работает.Я думаю, что мне нужно как-то настроить этот прямоугольник перед вызовом drawEllipse, но я не слишком уверен, как его настроить.Я попытался поиграться с ним, и я нашел некоторые решения, которые работают для некоторых размеров ручки, но не для других.

Имеет ли значение крышка ручки?Я использовал RoundCap.Должен ли я использовать другой колпачок?

Я почти нахожусь в точке, где я собираюсь сделать манипуляции с пикселями самостоятельно.Я выполняю рендеринг на QImage и использую составную операцию Source, поэтому мой код может быть немного быстрее, чем drawEllipse.memset примерно в 10 раз быстрее, чем QImage::fill, поэтому написание более быстрого кода, вероятно, не будет слишком сложным!Я бы предпочел этого не делать.

1 Ответ

0 голосов
/ 21 февраля 2019

Я наткнулся на раздел в документации , в котором говорится о том, как QRect отображается.Он описывает отношения между визуализированными пикселями и логическим прямоугольником.Визуализированный прямоугольник больше, чем логический прямоугольник.Все, что мне нужно сделать, это сделать логический прямоугольник меньше, чтобы компенсировать это.

QRect adjustStrokedRect(const QRect rect, const int thickness) {
  return QRect{
    rect.left() + thickness / 2,
    rect.top() + thickness / 2,
    rect.width() - thickness,
    rect.height() - thickness
  };
}

Хорошо, теперь я могу получить штриховые прямоугольники для рендеринга в нужном месте.Эллипс описывается QRect, а что, если я просто применю это преобразование к этому прямоугольнику?

Нет.

Это работает, если толщина равна 1, 2, 4, 6, но не 3, 5, 7.Круг на один пиксель слишком мал при толщине 3, 5, 7.Поэтому я попытался добавить 1 к размеру прямоугольника, если thickness % 2 == 1 && thickness != 1, но тогда из квадрата отображается асимметричный круг .Для некоторых комбинаций положения и размера шаткий асимметричный круг отображается даже при квадратном размере.

Вот странное изображение, которое вы можете легко воспроизвести:

Example

Произведите это с помощью этого кода:

QImage image{32, 32, QImage::Format_ARGB32_Premultiplied};
QPainter painter{&image};
QPen pen{Qt::NoBrush, 3.0, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin};
pen.setColor(QColor{0, 255, 0, 255});
painter.setPen(pen);
painter.drawEllipse(8, 8, 17, 17);
image.save("weird.png");

Я просто не понимаю, как это вообще возможно.Мне кажется, что drawEllipse рендерит эллипс, который примерно вписывается в прямоугольник.Я не смог найти связь между прямоугольником и эллипсом нигде в документах.Возможно, это потому, что это очень слабые отношения.

У меня нет проблем с получением QPainter::drawEllipse для рисования кругов с шириной хода 1, поэтому сейчас я просто не позволю толстые круги в моем приложении.Если я не могу сделать это идеально, я не буду делать это вообще.Я не отмечаю этот ответ как принятый, хотя я все еще хотел бы, чтобы это сработало.

...