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

Подделка субпиксельного сглаживания на текст с активной анимацией

Для тех, кто работает над проектом с поддержкой слоев с поддержкой Core Animation, к сожалению, очевидно, что сглаживание подпикселя (сглаживание текста) отключено для текста, не отображаемого на предварительно заданном непрозрачном фоне. Теперь для людей, которые могут устанавливать непрозрачные фоны для своего текста (либо с вызовом setBackgroundColor:, либо с эквивалентной настройкой в ​​Interface Builder), этот вопрос не представляет слишком большой проблемы. Тем не менее, для других, у которых нет абсолютно никакого способа обойти это, это не то, что можно игнорировать.

Я искал онлайн более двух дней для решения, и ничего полезного не появилось. Все, что я хочу сделать, это создать текстовую метку (не редактируемую пользователем), которая устанавливается в окне листа (фоны окон листа прозрачны, поэтому просмотр содержимого содержимого объявляет wantsLayer как истинный, отключает сглаживание текста для всех меток на окно); что-то очень простое. Я видел много оспариваемых решений (быстрый поиск в Google приведет к этой теме во многих других местах), но до сих пор все эти решения полагаются на людей, которые могут и хотят идти на компромисс с непрозрачным фоном (который вы не можете используйте на листе).

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

NSAttributedString *string = [[self cell] attributedStringValue];
NSImage *textImage = [[NSImage alloc] initWithSize:[string size]];
[textImage lockFocus];
[string drawAtPoint:NSZeroPoint];
[textImage unlockFocus];

Но это не работает (скорее всего, из-за того, что при вызове из drawRect: настроенный графический контекст уже отключен сглаживанием текста, сглаживание подпикселя отключено, вместо этого используется регулярное сглаживание), но и более активное решение найдено в этом вопросе (где вы создаете свой собственный графический контекст).

Итак, как делается что-то подобное? Можете ли вы каким-то образом "подделать" эффект сглаживания текста? Есть ли даже хак-иш обходные пути, которые могут получить что-то настроенное? Я действительно не хочу отказываться от Core Animation только из-за этой глупой проблемы; есть много преимуществ, которые сохраняют много времени и кода.


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

Редактировать # 2: Оказывается, ссылка выше также является бюстом. Хотя связанные с ними методы дают подпиксельное сглаживание, они также не работают на прозрачных фонах.

4b9b3361

Ответ 1

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

Например, скажем, вы рисуете черный текст на белом фоне, а порядок подэлементов - RGB. Правый край может быть слабым синим: высокое значение B (полная яркость на стороне от глифа), немного ниже, но все еще высокие значения R и G (меньшая яркость на стороне ближе к глифу). Если вы теперь составьте этот пиксель на темно-сером фоне, вы сделаете его легче, чем это было бы, если бы вы внесли черный текст на темно-серый фон напрямую.

В принципе, вы не сталкиваетесь с каким-либо ограничением CoreAnimation: просто не имеет смысла использовать субпиксельную визуализацию на прозрачном слое, который может быть составлен на произвольном фоне. Вам понадобится отдельный альфа для цветного канала, но поскольку формат пикселя вашего буфера - это RGBA (или ARGB или что-то еще), вы не сможете его использовать.

Но это приводит нас к решению. Если вы знаете, что фон останется прежним (например, лист отображается над окном, содержимое которого вы контролируете), вы можете просто сделать свой слой непрозрачным, заполнить его копией покрытой области фонового окна и сделать рендер субпиксель-сглаженный текст на нем. В принципе, вы должны предварительно компоновать свой слой с фоном. Если фон остается тем же, это будет выглядеть так же, как обычный альфа-композитинг, за исключением того, что теперь вы можете выполнять субпиксельный рендеринг текста; если фон меняется, тогда вам придется отказаться от выполнения подпиксельного текстового рендеринга в любом случае (хотя, я думаю, вы могли бы продолжать копировать фон и перерисовывать свой непрозрачный оверлей при каждом изменении).

Ответ 2

Как простой хак, если текстовое сглаживание работает (но не субпиксель), вы можете подделать его, сделав рендеринг в 3 раза, а затем уменьшите масштаб. Это неприменимо, поскольку я не знаю, как запросить порядок элементов на вашем дисплее, но он должен работать.

например.

RGB RGB RGB -> RGB
|    |    |    |||  
|    |    +----++^
|    +---------+^
+--------------^

Ответ 3

Я не уверен, что понял этот вопрос, так что это настоящий пунт.

Но я заметил, что ответ LaC зависит от того, что пиксели написаны друг на друга обычным способом. Таким образом, вы должны в Photoshop вызывать "Normal Blend Mode".

Вы можете объединить два слоя множеством других способов, включая "Умножить" :

enter image description here

Это индивидуально умножает R, G и B каждого пикселя. Поэтому, если у вас есть текст на белом фоне, вы можете получить дополнительный слой назад, чтобы показать верхний слой, чтобы "Умножить" , что заставляет оба слоя сжигать друг с другом так.

Это также должно работать для вашего случая использования:

enter image description here

Оба текстовых слоя в этом снимке имеют непрозрачный белый фон, но один справа имеет режим смешивания, установленный на " Умножить".

С "Умножить" :

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

Другими словами, тот же результат, что вы бы просто наложили текст прямо на задний план.

У меня нет субпикселя, сглаживающего текст на этом скриншоте, но он будет работать одинаково хорошо с разными значениями R, G и B.


Я не очень хорошо знаком с Mac API, но, согласно эта ссылка, Core Animation имеет такую ​​возможность, которую вы получаете писание:

myLayer.compositingFilter = [CIFilter filterWithName:@"CIMultiplyBlendMode"];

или

myLayer.compositingFilter = [CIFilter filterWithName:@"CIMultiplyCompositing"];

(я понятия не имею, что "Blend Mode" против "Compositing" относится к макинтову языка, поэтому вам придется попробовать оба!)


EDIT: Я не уверен, что вы указали, что ваш текст был черным. Если это белый, вы можете использовать белый-на-черный слой, установленный в режим наложения "Экран".

Ответ 4

Если вы просто пытаетесь получить резкий текст для отображения на непрозрачном фоне и ударить головой по стене с помощью CATextLayer - сдайте и используйте NSTextField с отключенной вставкой и установкой linefragmentpadding равным 0. Пример кода быстро, но вы должен быть в состоянии легко перевести...

var testV = NSTextView()
testV.backgroundColor = NSColor(calibratedRed: 0.73, green: 0.84, blue: 0.89, alpha: 1)
testV.frame = CGRectMake(120.0, 100.0, 200.0, 30.0)
testV.string = "Hello World!"
testV.textContainerInset = NSZeroSize
testV.textContainer!.lineFragmentPadding = 0
self.addSubview(testV)

для меня отображается текст, эквивалентный:

var testL = CATextLayer()
testL.backgroundColor = NSColor(calibratedRed: 0.73, green: 0.84, blue: 0.89, alpha: 1).CGColor
testL.bounds = CGRectMake(0.0, 0.0, 200.0, 30.0)
testL.position = CGPointMake(100.0, 100.0)
testL.string = "Hello World!"
testL.fontSize = 14
testL.foregroundColor = NSColor.blackColor().CGColor
self.layer!.addSublayer(testL)

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

Ответ 5

Я не знаю, будет ли это решение работать для вас, но вы можете нарисовать текст в изображении с прозрачным фоном, используя UIGraphicsBeginImageContext(), я уверен, что вы можете настроить псевдонимы для этого, если не рисовать текст в два или четыре раза больше размера, а затем уменьшаться при рисовании изображения в вашем представлении.