Подтвердить что ты не робот

Как получить 1 пиксельную линию с NSBezierPath?

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

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

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

Любые идеи?

4b9b3361

Ответ 1

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

Ответ 2

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

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

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 действительно демонстрируют эту проблему по-разному, потому что каждый из пикселей ниже на самом деле изображен четырьмя пикселями Retina, что означает, что они могут фактически нарисовать половинные пиксели. Тем не менее, у вас все еще есть одна и та же проблема, если вам нужна четкая строка в 0.5pt. Кроме того, поскольку Apple может в будущем внедрять другие экраны сетчатки, например, каждый пиксель состоит из 9 пикселей сетчатки (3x) или что-то еще, вы действительно не должны полагаться на это. Вместо этого теперь есть вызовы API для преобразования прямоугольников в "выравнивание по выровненным", что делает это для вас, независимо от того, используете ли вы 1x, 2x или фиктивный 3x.

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

Ответ 3

Ответ (похоронен) в Документах Apple:

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

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

Что касается эффектов вызова setDefaultLineWidth:, docs также указывается, что:

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

Ответ 4

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

[[NSGraphicsContext currentContext] setShouldAntialias: NO];

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

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

EDIT: Чтобы быть более конкретным, я изменил координаты x и y по отдельности и со смещением 0,5.

ИЗМЕНИТЬ 2: Я, должно быть, сделал что-то неправильно, так как смена координат со смещением 0.5 действительно работает. Конечный результат лучше, чем тот, который был получен путем отключения сглаживания, поэтому я сделаю Фрэнсис МсГрю ответом на принятый ответ.