iOS UIView渲染到纹理 - 保持质量

时间:2016-02-12 01:14:16

标签: ios objective-c uiview opengl-es

我已经实现了一个程序来将UILabel渲染到纹理,然后将相同的纹理映射到OpenGL中的四边形。然后,我通常通过将其添加到UIView层次结构来呈现相同的UILabel,以便在视觉上对它们进行比较。

我立即注意到我渲染到纹理的UILabel的质量低于正常渲染的UILabel。我无法弄清楚质量降低的原因,并希望得到任何建议。

我使用了Render contents of UIView as an OpenGL texture

中的渲染到纹理技术

Screenshot of simple test case

以下是一些相关代码

    // setup test UIView (label for now) to be rendered to texture
    _testLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 50)];
    [_testLabel setText:@"yo"];
    _testLabel.textAlignment = NSTextAlignmentCenter;
    [_testLabel setBackgroundColor:RGB(0, 255, 0)];

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    _testLabelContext = CGBitmapContextCreate(NULL, _testLabel.bounds.size.width, _testLabel.bounds.size.height, 8, 4*_testLabel.bounds.size.width, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);

    CGColorSpaceRelease(colorSpace);

    // setup texture handle for view to be rendered to texture
    glGenTextures(1, &_testLabelTextureHandle);
    glBindTexture(GL_TEXTURE_2D, _testLabelTextureHandle);

    // these must be defined for non mipmapped nPOT textures (double check)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _testLabel.bounds.size.width, _testLabel.bounds.size.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);

然后在我的渲染功能......

GLubyte *labelPixelData = (GLubyte*)CGBitmapContextGetData(_testLabelContext);

glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _testLabel.bounds.size.width, _testLabel.bounds.size.height, GL_RGBA, GL_UNSIGNED_BYTE, labelPixelData);

2 个答案:

答案 0 :(得分:1)

你不需要将渲染宽度和高度乘以@ 2x / @ 3x吗? OpenGL以真实像素工作。

答案 1 :(得分:1)

@soprof的答案是正确的。在创建视图屏幕截图时,您需要在每种情况下使用主屏幕边界缩放内容。将数据上传到纹理时,需要稍后使用相同的坐标。仅仅创建一个更大的上下文是不够的。

这实际上适用于openGLUIView之间的所有连接。您需要了解坐标系保持使用1x,这对于视图布局非常有用。 Apple使用contentScaleFactor上的UIView属性来实际扩展内部内容,但在framebounds属性中仍然无法显示。因此,例如,要从UIView获取图像,您需要执行以下操作:

+ (UIImage *)imageFromView:(UIView *)view
{
    CGRect rect = [view bounds];
    UIGraphicsBeginImageContextWithOptions(rect.size, NO, view.contentScaleFactor);

    CGContextRef context = UIGraphicsGetCurrentContext();
    [view.layer renderInContext:context];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return image;
}

请参阅可以使用选项创建的上下文,并使用视图比例。但问题并不止于此。将视图添加到视图层次结构时,将设置实际内容比例因子。这意味着简单地初始化标签不足以使比例因子正确(默认情况下为1.0f)。因此,您可以在视图上手动将比例设置为您想要的,您可以使用[UIScreen mainScreen].scale从屏幕指定比例,在创建上下文或其间的任何内容时直接插入此值。

无论如何,如果你修改你的代码以包含比例,你将需要新的框架,总是

rect.size.width *= view.contentScaleFactor;
rect.size.height *= view.contentScaleFactor;

您需要对所有连续代码使用此类rect,但不要更改您尝试绘制的视图的框架,因为字体不会使用它进行缩放。可能有用的是在实际的输入视图中应用缩放变换矩阵(如果我没记错的话,帧也会缩放,所以即使你的代码应该自然地工作)。

正如我所提到的,这涉及到所有连接,因此如果您要从图层创建渲染缓冲区,则还需要使用其中的比例。 renderbufferStorage:GL_RENDERBUFFER fromDrawable:将采用UIView图层,但您必须使用layer.contentsScale = [UIScreen mainScreen].scale将比例显式设置为图层,以获得正确的渲染缓冲区大小。因此,如果你正在使用它而你没有设置比例,整个场景将具有1倍的质量,甚至修复标签纹理也不会产生足够高的质量。