Как получить 1 пиксельную линию с NSBezierPath? - PullRequest
14 голосов
/ 05 ноября 2011

Я разрабатываю пользовательский элемент управления. Одним из требований является рисование линий. Хотя это работает, я заметил, что мои линии шириной в 1 пиксель на самом деле не выглядят как строки шириной в 1 пиксель - я знаю, они не совсем пиксели, но вы понимаете, о чем я. Они больше похожи на два или три пикселя в ширину. Это становится очень очевидным, когда я рисую пунктирную линию с штрихом в 1 пиксель и зазором в 2 пикселя. Штрихи в 1 пиксель на самом деле выглядят как крошечные линии вместо точек.

Я прочитал документацию по рисованию какао, и, хотя Apple упоминает метод setLineWidth, изменение ширины линии на значения меньше 1,0 только сделает линию более размытой, а не тонкой.

Итак, я подозреваю, что что-то еще влияет на то, как выглядят мои линии.

Есть идеи?

Ответы [ 4 ]

25 голосов
/ 05 ноября 2011

Траектории Безье рисуются по центру на их пути, поэтому, если вы рисуете траекторию шириной 1 пиксель вдоль X-координаты, линия на самом деле рисует вдоль Y-координат {-0,5, 0,5}. Решением обычно является смещение координаты 0,5, так что линия не рисуется в границах субпикселя. Вы должны быть в состоянии сместить ограничивающий прямоугольник на 0,5, чтобы получить более четкое поведение при рисовании.

18 голосов
/ 08 марта 2014

Фрэнсис МакГрю уже дал правильный ответ, но так как я однажды сделал презентацию по этому вопросу, я подумал, что добавлю несколько фотографий.

Проблема здесь в том, что координаты в Кварцележат на пересечениях между пикселями.Это хорошо при заполнении прямоугольника, потому что каждый пиксель, который находится внутри координат, заполняется.Но линии технически (математически!) Невидимы.Чтобы нарисовать их, Кварц должен нарисовать прямоугольник с заданной шириной линии.Этот прямоугольник центрируется по координатам:

The rectangle you tell Quartz to draw when you specify integral coordinates

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

Half pixels averaged out when drawing between pixels

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

Coordinates offset by 0.5 towards lower right

Теперь, конечно, просто смещение может быть не тем, что вы хотели.Потому что, если вы сравните заполненный вариант с штрихованным, обводка будет на один пиксель больше в правом нижнем углу.Например, если вы обрежете прямоугольник, это обрезает нижнюю правую часть:

Comparison between offset stroked and non-offset filled rectangle

Поскольку люди обычно ожидают, что прямоугольник будет штриховать внутри указанного прямоугольника, что вы обычно делаетеэто то, что вы смещены на 0,5 к центру , поэтому нижний правый угол фактически сдвигается на один пиксель вверх.С другой стороны, многие приложения для рисования смещены на 0,5 от от центра, чтобы избежать наложения между границей и заливкой (что может выглядеть странно при рисовании с прозрачностью).

Обратите внимание, чтоэто справедливо только для 1x экранов.Экран 2x Retina на самом деле демонстрирует эту проблему по-разному, потому что каждый из пикселей ниже фактически отрисовывается 4 пикселями Retina, что означает, что они могут на самом деле рисовать полупиксели.Тем не менее, у вас все еще есть та же проблема, если вы хотите резкую линию 0.5pt.Кроме того, так как Apple может в будущем представить другие экраны Retina, где, например, каждый пиксель состоит из 9 пикселей Retina (3x) или чего-либо еще, вам не следует полагаться на это.Вместо этого теперь есть вызовы API для преобразования прямоугольников в «выравнивание по сторонам», что делает это для вас, независимо от того, используете ли вы 1x, 2x или вымышленный 3x.

PS - Так как я пошел виз-за трудностей с написанием всего этого, я разместил это на своем веб-сайте: http://orangejuiceliberationfront.com/are-your-rectangles-blurry-pale-and-have-rounded-corners/, где я обновлю и пересмотрю это описание и добавлю больше изображений.

9 голосов
/ 26 марта 2013

Ответ (похоронен) в Apple Docs:

" Чтобы избежать сглаживания при рисовании горизонтальной или вертикальной линии шириной в одну точку, если линиянечетное количество пикселей по ширине, вы должны сместить позицию на 0,5 точки по обе стороны от позиции с полным номером"

Скрыто в Руководство по рисованию и печати для iOS: Рисование iOSПонятия , хотя ничего особенного в текущем стандарте не найти, стандарт (OS X) Руководство по рисованию какао ..

Что касается эффектов вызова setDefaultLineWidth: the Документы также утверждают, что:

" Ширина 0 интерпретируется как самая тонкая линия, которая может быть визуализирована на конкретном устройстве. Фактическая ширина визуализированной линии может отличаться от указанной ширины нацелых 2 пикселя устройства, в зависимости от положения линии относительно сетки пикселей и текущих настроек сглаживания. На ширину линии также могут влиять коэффициенты масштабирования, указанные в текущем значенииматрица информации активного графического контекста."

6 голосов
/ 05 ноября 2011

Я нашел некоторую информацию о том, что это вызвано сглаживанием.Временное отключение сглаживания легко:

[[NSGraphicsContext currentContext] setShouldAntialias: NO];

Это дает четкую линию в 1 пиксель.После рисования просто включите его снова.

Я попробовал решение, предложенное Фрэнсисом МакГрю, сместив координату x на 0,5, однако это не имело никакого значения для появления моей линии.

РЕДАКТИРОВАТЬ: Чтобы быть более точным, я изменил координаты X и Y индивидуально и вместе со смещением 0,5.

РЕДАКТИРОВАТЬ 2: Я, должно быть, сделал что-то не так, поскольку изменение координат со смещением 0,5 действительно работает,Конечный результат лучше, чем результат, полученный при отключении сглаживания, поэтому я сделаю ответ Фрэнсиса Мсгрю принятым.

...