2016-02-12 60 views
0

我已经实现了一个程序,将UILabel渲染为纹理,然后将相同的纹理映射到OpenGL中的四边形。然后,我通常通过将它添加到UIView层次结构来直观地呈现相同的UILabel。iOS UIView渲染纹理 - 保留质量

我立即注意到,我渲染到纹理的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); 

回答

1

难道你不需要乘以撕裂呃宽度和高度由@ 2x/@ 3x? OpenGL以实像素工作。

+0

增加CGBitmapContext的尺寸以及传递给glTexImage2D的尺寸只会将相同的图像渲染成更大的纹理。因此,例如增加3,将使相同的UILabel成为比填充像素大3倍的纹理,因此您最终得到的纹理在左上角具有标签,其余纹理填充为黑色。 – mbradber

1

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

这实际上适用于openGLUIView之间的所有连接。您需要了解坐标系保持为1x,这对视图布局非常有用。 Apple在UIView上使用contentScaleFactor属性来实际缩放内部内容,但在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; 

您需要使用这种矩形的所有继续码,但不改变帧的新框架您正在尝试绘制的视图因为字体不会与其缩放。尽管在实际的输入视图上应用一个缩放变换矩阵(如果我没有记错的话,框架也会缩放,所以即使你的代码应该自然地工作)也可能工作。

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