【问题标题】:iOS UIView render to texture -- preserve qualityiOS UIView 渲染到纹理——保持质量
【发布时间】:2016-02-12 01:14:16
【问题描述】:

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

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

我正在使用Render contents of UIView as an OpenGL texture 此处找到的渲染到纹理技术

这里是一些相关代码

    // 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);

【问题讨论】:

    标签: ios objective-c uiview opengl-es


    【解决方案1】:

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

    【讨论】:

    • 增加 CGBitmapContext 的尺寸和传递给 glTexImage2D 的尺寸只是将相同的图像渲染成更大的纹理。例如,增加 3 会将相同的 UILabel 渲染为比填充像素大 3 倍的纹理,因此您最终会得到一个在左上角有标签的纹理,其余的纹理填充为黑色。
    【解决方案2】:

    @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 倍质量,即使修复标签纹理也不会产生足够高的质量。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-10-11
      • 2016-12-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-30
      相关资源
      最近更新 更多