对于使用Core Animation图层支持视图的项目工作的任何人,不幸的是,对于未在预先设置的不透明背景上呈现的文本,禁用了子像素抗锯齿(文本平滑)。现在,对于能够为其文本设置不透明背景的人(使用setBackgroundColor:
调用或Interface Builder中的等效设置),此问题不会出现太多问题。但是对于其他人来说,绝对没有办法解决它,这不是一个人们无法忽视的事情。
我已经在线寻找两天以上的解决方案,但没有任何可用的功能。我想要做的就是创建一个在工作表窗口上设置的文本标签(不是用户可编辑的)(工作表窗口背景是透明的,因此将工作表的内容视图声明为wantsLayer
为true会禁用所有标签的文本平滑在窗户上);非常简单。我已经看到很多有争议的解决方案(快速谷歌搜索将在许多其他地方提出这个主题),但到目前为止,所有这些解决方案依赖于人们能够并愿意与不透明的背景妥协(你不能在表格上使用。)
到目前为止,我能想象到的最佳方向是将文本预渲染到文本平滑到图像上,然后像往常一样在图层支持的视图上显示图像。但是,这似乎不可能。我认为可以尝试的“正常”方式是:
NSAttributedString *string = [[self cell] attributedStringValue];
NSImage *textImage = [[NSImage alloc] initWithSize:[string size]];
[textImage lockFocus];
[string drawAtPoint:NSZeroPoint];
[textImage unlockFocus];
但这似乎不起作用(很可能因为从drawRect:
调用时,图形上下文设置已禁用文本平滑 - 关闭子像素抗锯齿,而使用常规抗锯齿),但在this question(您创建自己的图形上下文)中找到了更复杂的解决方案。
那么如何做到这样的事情呢?你能以某种方式“伪造”文本平滑的效果吗?是否有甚至黑客攻击的解决方案可以设置一些东西?我真的不想因为这个愚蠢的问题而放弃核心动画;它有很多好处,可以节省大量的时间和代码。
编辑经过大量搜索,我发现了一些东西。虽然thread itself只是重申了我的怀疑,但我想我可能找到了一个问题的解决方案:自定义CALayer
绘图。 Timothy Wood在他的一篇回复中附上了一个示例项目,该项目使用几种技术显示字体平滑,其中一些技术有效。我会考虑将其整合到我的项目中。
编辑#2:原来,上面的链接也是一个半身像。虽然链接的方法提供了亚像素抗锯齿,但它们在透明背景上也会失败。
答案 0 :(得分:9)
如果您使用透明纸张,则事先不知道它下面的像素是什么。他们可能会改变。请记住,对于所有三种颜色,您都有一个Alpha通道:如果将其设为透明,则不会看到任何子像素效果,但如果使其不透明,则所有三个子元素都将与背景合成。如果为白色背景上的合成赋予边缘正确的颜色,如果背景更改为其他颜色,则它看起来不正确。
例如,假设您在白色背景上绘制黑色文本,子元素顺序为RGB。右边缘可能是微弱的蓝色:高B值(远离字形的一侧的全亮度),略低但仍然高的R和G值(靠近字形的一侧的亮度较低)。如果您现在将该像素合成在深灰色背景上,那么如果您直接在深灰色背景上渲染黑色文本,那么它将会变得更亮。
基本上,您没有面对CoreAnimation的任意限制:在可能在任意背景上合成的透明层上使用子像素渲染是没有意义的。每个颜色通道需要一个单独的alpha,但由于缓冲区的像素格式是RGBA(或ARGB或其它任何东西),所以你不能拥有它。
但这引出了我们的解决方案。如果您知道背景将保持不变(例如,工作表显示在您控制其内容的窗口上),那么您可以简单地使图层不透明,用背景窗口的覆盖区域的副本填充它,然后渲染子像素抗锯齿的文字就可以了。基本上,您将使用背景预先组合图层。如果背景保持不变,这将看起来与正常的alpha合成相同,除了您现在可以进行亚像素文本渲染;如果背景发生变化,那么无论如何你都不得不放弃进行亚像素文本渲染(尽管我猜你可以继续复制背景并重新绘制不透明的叠加层)。
答案 1 :(得分:2)
作为一个简单的黑客,如果文本抗锯齿正在工作(但不是亚像素),你可以通过渲染到宽度为3倍的视图来伪造它,然后缩小。这是不可移植的,因为我知道无法在显示器上查询元素顺序,但它应该可以工作。
如,
RGB RGB RGB -> RGB
| | | |||
| | +----++^
| +---------+^
+--------------^
答案 2 :(得分:2)
我不确定我是否已经掌握了这个问题,所以这是一个真正的平底船。
但是我注意到LaC的答案取决于像素以正常方式相互叠加。按照Photoshop中的方式调用“正常混合模式”。
您可以通过许多其他方式合并两个图层,包括“Multiply”:
这分别乘以每个像素的R,G和B.因此,如果您在白色背景上显示文本,则可以通过将顶层设置为“Multiply”来显示更远的图层,这会导致两个图层彼此燃烧。
这也适用于您的用例:
此镜头中的两个文本图层都具有不透明的白色背景,但右侧的文本图层将混合模式设置为“乘法”。
使用“Multiply”:
换句话说,您只需将文本直接放在背景上即可获得相同的结果。
我没有对这个截图中的文字进行亚像素抗锯齿处理,但是对于不同的R,G和B值,它会同样有效。
我对Mac的API并不熟悉,但根据this link,Core Animation确实具备此功能,您可以通过写作获得:
myLayer.compositingFilter = [CIFilter filterWithName:@"CIMultiplyBlendMode"];
或
myLayer.compositingFilter = [CIFilter filterWithName:@"CIMultiplyCompositing"];
(我不知道“混合模式”与“合成”在Mac术语中的含义是什么,所以你必须尝试两者!)
编辑:我不确定你是否曾指明你的文字是黑色的。如果是白色,则可以使用设置为“屏幕”混合模式的黑白色图层。
答案 3 :(得分:0)
如果您只是想在不透明的背景上显示清晰的文字,并使用CATextLayer将头撞到墙上 - 放弃并使用NSTextField,将inset禁用并将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)
这个类显然带来了更多的开销,可能还有不必要的东西,如文本选择/复制等,但是文本显示得很好。
答案 4 :(得分:-3)
我不知道这个解决方案是否适合您,但是您可以使用UIGraphicsBeginImageContext()将文本绘制成具有透明背景的图像,我很确定您可以为此设置别名,如果不是您绘制文本大小的两倍或四倍,然后在视图中绘制图像时缩小。