MiterJoin игнорируется для пера шириной в один пиксель

Проблема

При рисовании прямоугольника с шириной линии 1 пиксель в QPainter с включенным сглаживанием углы становятся слегка прозрачными, независимо от того, какой JoinStyle установлен. То же самое можно наблюдать и для полилиний и QPainterPath.

def paintEvent(self, event):
    with QPainter(self) as p:
        p.setRenderHint(QPainter.Antialiasing, True)

        p.fillRect(self.rect(), QColor('black'))
        p.setBrush(Qt.NoBrush)

        # 1px width border, corner is partially transparent
        p.setPen(QPen(QColor("orange"), 1, cap=Qt.SquareCap, join=Qt.MiterJoin))
        p.drawRect(QRectF(10.5, 10.5, 50., 25.))

        # 3px width border, corner is crisp
        p.setPen(QPen(QColor("orange"), 3, cap=Qt.SquareCap, join=Qt.MiterJoin))
        p.drawRect(QRectF(15.5, 15.5, 50., 25.))

производит этот вывод:

Вопрос

Есть ли способ получить четкие углы для пера шириной 1 пиксель, который также работает для частично прозрачных линий и произвольных QPainterPath?

Решение опробовано до сих пор

  • Рисуем каждую линию отдельно:
    Работает нормально, но есть артефакты с частично прозрачными линиями из-за перекрытия углов.

  • Рисуем каждую линию отдельно, но сдвигаем начало каждой строки на 1:
    Решает проблему с частично прозрачными линиями под прямыми углами, но не с другими углами или кривыми.

  • Временно отключаем сглаживание:
    Диагональные линии и изогнутые линии становятся неровными.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
0
57
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это вызвано частным QCosmeticStroker, который QPainter использует при рисовании контуров с шириной пера от 0 до 1 (включительно) в масштабе устройства рисования.

Рисование «идеальных по пикселям» линий в режиме сглаживания всегда было немного сложным, и всегда существует некоторый уровень компромисса (и возможных ошибок).

Точно так же этот обводчик, хотя обычно он точен, быстр и эффективен, при определенных обстоятельствах может выйти из строя (чтобы понять, насколько все это на самом деле сложно, посмотрите его текущую реализацию), и хотя можно было бы предположить, что рисование простого прямоугольного угла может оказаться невозможным. Несмотря на то, что это проблема, она может возникнуть при модульном рисовании «полилиний» (которые также могут включать кривые) и замкнутых путей.

Также учтите, что для перьев шириной 1 пиксель разница в стилях соединения практически незначительна: соединение со скосом по умолчанию заполнит 87,5% площади пикселя, а со скруглением — ~94,6%.

Тем не менее, возможно, это все же стоит сообщить об ошибке.

В любом случае, простой способ обойти эту проблему — предотвратить использование обводки: просто используйте ширину пера, немного превышающую 1. Это может быть немного менее эффективно, поскольку рисовальщику нужно будет проверить, является ли линия выходит за «границу пикселя» даже для прямых ортогональных линий и поэтому пытаться применить сглаживание почти без необходимости, но это приемлемая цена, если точность изображения важнее:

# no need to set the cap, which already defaults to square
p.setPen(QPen(QColor("orange"), 1.0001))

+1 Хороший ответ. Было бы интересно узнать, устранил ли вызов setCosmetic(false) на QPen проблему — независимо от ширины пера.

G.M. 06.06.2024 18:39

@Г.М. К сожалению, это ничего не меняет: насколько я понимаю, QPainter автоматически использует QCosmeticStroker, как только распознает, что перо, которое в конечном итоге используется для рисования, находится в диапазоне от 0 до 1, в зависимости от устройства рисования и текущих преобразований: Косметический атрибут в конечном итоге используется для учета окончательной ширины, поэтому он не имеет никакого эффекта.

musicamante 06.06.2024 18:49

@Г.М. Я попробовал, и setCosmetic(false) вообще не повлиял.

J. Coenen 07.06.2024 15:22

Другие вопросы по теме