提问人:AsmCoder8088 提问时间:5/16/2023 更新时间:5/16/2023 访问量:43
cocoa - 为什么在使用 CALayer 和 drawRect for NSView 时字体模糊?
cocoa - Why am I getting blurry fonts when using CALayer versus drawRect for NSView?
问:
我正在将应用程序从 MS Windows 移植到 macOS。一个主要功能是“图像查看器”,它显示CCD图像(例如,天文图片)的渲染。在此渲染之上是叠加文本,例如视野、对象细节等。
在我的第一遍中,我选择了在“drawRect”函数中实现渲染和覆盖代码的路线。这意味着使用 CGContextDrawImage,将位图数据传递给它,然后在其上应用覆盖层(全部在 drawRect 内)。结果产生了非常清晰的文本和图形,但我可以看出,当平移(用鼠标)时,它只有一丝滞后,还有一些重影效果(页面撕裂?所以我想改进一下。
进入“基于图层”的视图方法。首先,在我的自定义 NSView 中,我覆盖了“wantsUpdateLayer”,以便调用“updateLayer”函数。这允许我直接向 NSView 层的内容提供位图数据。哇 - 不再有延迟,不再有重影伪影 - 它非常活泼且保真度极高。这绝对是正确的方向。但。。。。现在我无法绘制叠加信息!因为“updateLayer”只允许设置位图,所以它不允许发生任何绘图代码(没有可用的活动CGContext)。
因此,经过更多的探索,我想出了一个可能的解决方案来创建两个自定义 CALayer 对象。一个我称之为“MyLayerBitmap”,另一个称为“MyLayerOverlay”。前者响应“display”覆盖,类似于之前的“updateLayer”覆盖,它只是将内容设置为位图数据。很简单。后者响应“drawInContext”,它有一个提供的 CGContextRef,理论上我可以在这里在这个层中进行覆盖绘制。所以,我把它连接起来如下:
m_layer1 = [MyLayerBitmap layer];
m_layer2 = [MyLayerOverlay layer];
[m_layer1 setParent:self];
[m_layer2 setParent:self];
[m_layer1 setNeedsDisplayOnBoundsChange:YES];
[m_layer2 setNeedsDisplayOnBoundsChange:YES];
m_layer1.frame = self.bounds;
m_layer2.frame = self.bounds;
[m_layer1 addSublayer:m_layer2];
[self setWantsLayer:YES];
MyLayerBitmap:
- (void)display
{
NSLog(@"MyLayerBitmap display is called");
self.contents = (__bridge id)[m_pParent getImgRef_Main];
}
MyLayerOverlay:
- (void)drawInContext:(CGContextRef)ctx
{
NSLog(@"MyLayerOverlay drawInContext is called");
NSGraphicsContext *myCtx;
myCtx = [NSGraphicsContext graphicsContextWithCGContext:ctx flipped:true];
[NSGraphicsContext saveGraphicsState];
NSGraphicsContext.currentContext = myCtx;
...drawing code here...
[NSGraphicsContext restoreGraphicsState];
}
每当鼠标移动时,我都会调用:
m_layer1.frame = self.frame; // Not sure why, but the first layer has to use frame rather than bounds
m_layer2.frame = self.bounds;
[m_layer1 setNeedsDisplay];
[m_layer2 setNeedsDisplay];
但我现在有两个问题:
- 叠加文本看起来模糊不清。CALayer 是否只是创建一个“一刀切”的位图,然后对其进行拉伸?或者对如何做到这一点有任何控制?我使用与drawRect完全相同的绘图代码,区别在于必须使用提供的CGContext。
- 第二层在调整视图大小时未收到任何显示更改通知...只有第一层。这似乎是一个错误,因为我专门将两个图层的“setNeedsDisplayOnBoundsChange”设置为 YES。因此,现在发生的情况是,每当调整视图大小时,叠加图形都会保留在原位,这是不正确的。只有当用户平移视图时,视图才会更新。
这是基于图层的方法,它很快但很模糊:
这是“drawRect”方法,它速度较慢,但文本清晰:
是否只需要使用 drawRect 即可获得清晰/清晰的图形?如果是这样,有没有办法优化它,以便CGContextDrawImage可以运行得更快?我运行了分析,发现 BitBlt 消耗了超过 50% 的总处理时间,因此希望不必使用 CGContextDrawImage,只需将图层内容设置为位图。
任何想法将不胜感激。谢谢。
答:
我做了更多的挖掘,发现另一个人也有类似的问题:CGContextDrawImage 绘制的大图像非常模糊
使用解决了文本模糊的问题。setContentsScale
这很难追踪,因为显然在使用常规 NSView 时,它会根据当前屏幕自动调整比例因子。但是,当使用自定义图层/视图时,必须手动设置比例因子:https://developer.apple.com/documentation/quartzcore/calayer/1410746-contentsscale?language=objc
我仍然必须弄清楚为什么第二层不响应调整大小通知(如问题#2所示),但那是另一天。
编辑:解决了问题#2。必须使用以下内容:
[m_layer2 setLayoutManager:[CAConstraintLayoutManager layoutManager]];
[m_layer2 setAutoresizingMask:(kCALayerHeightSizable | kCALayerWidthSizable)];
[m_layer2 setContentsGravity:kCAGravityResizeAspect];
现在,当用户调整视图大小时,第二层(叠加层)会相应地重新定位。
评论