将UIView的内容渲染为OpenGL纹理

时间:2022-09-10 18:54:14

Is there a way to render the contents of a UIView as a texture with OpenGL in iOS? Or the contents of a CGLayer?

有没有办法在iOS中使用OpenGL将UIView的内容渲染为纹理?还是CGLayer的内容?

2 个解决方案

#1


19  

You can use the view's layer property to get the CALayer and use renderInContext: on that to draw into a CoreGraphics context. You can set up a CoreGraphics context with memory you allocate yourself in order to receive a pixel buffer. You can then upload that to OpenGL by the normal method.

您可以使用视图的图层属性来获取CALayer并使用renderInContext:来绘制CoreGraphics上下文。您可以使用自己分配的内存设置CoreGraphics上下文,以便接收像素缓冲区。然后,您可以通过常规方法将其上载到OpenGL。

So: there's a means to get the pixel contents of a UIView and OpenGL will accept pixel buffers. There's no specific link between the two.

所以:有一种方法可以获得UIView的像素内容,OpenGL将接受像素缓冲区。这两者之间没有具体的联系。

Coding extemporaneously, the process would be something like:

即席编码,过程如下:

UIView *view = ... something ...;

// make space for an RGBA image of the view
GLubyte *pixelBuffer = (GLubyte *)malloc(
                               4 * 
                               view.bounds.size.width * 
                               view.bounds.size.height);

// create a suitable CoreGraphics context
CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context =
    CGBitmapContextCreate(pixelBuffer, 
                          view.bounds.size.width, view.bounds.size.height, 
                          8, 4*view.bounds.size.width, 
                          colourSpace, 
                          kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colourSpace);

// draw the view to the buffer
[view.layer renderInContext:context];

// upload to OpenGL
glTexImage2D(GL_TEXTURE_2D, 0, 
             GL_RGBA,
             view.bounds.size.width, view.bounds.size.height, 0,
             GL_RGBA, GL_UNSIGNED_BYTE, pixelBuffer);

// clean up
CGContextRelease(context);
free(pixelBuffer);

That doesn't deal with issues surrounding non-power-of-two sized views on hardware without the non-power-of-two texture extension and assumes a suitable GL texture name has already been generated and bound. Check for yourself, but I think non-power-of-two is supported on SGX hardware (ie, iPhone 3GS onwards, the iPad and all but the 8gb third generation iPod Touch onwards) but not on MBX.

这不涉及围绕硬件的非二次幂大小视图的问题,而没有非二次幂纹理扩展,并且假设已经生成并绑定了合适的GL纹理名称。检查一下你自己,但我认为SGX硬件(即iPhone 3GS以上,iPad以及除了8gb第三代iPod Touch之外的所有硬件)都支持非二次幂,但不支持MBX。

The easiest way to deal with non-power-of-two textures here is probably to create a large enough power of two texture and to use glTexSubImage2D to upload just the portion from your source UIView.

在这里处理非幂二纹理的最简单方法可能是创建两个纹理足够大的功能,并使用glTexSubImage2D只上传源UIView中的部分。

#2


5  

Here is another method of grabbing an OpenGL Layer, which is to use glReadPixels. This will grab the visible layers BEHIND your openGL layer, as well (basically, your visible screen). Check out this question: How do I grab an image form my EAGLLayer ?

这是获取OpenGL Layer的另一种方法,即使用glReadPixels。这将抓住你的openGL层后面的可见层(基本上,你的可见屏幕)。看看这个问题:如何从我的EAGLLayer中获取图像?

Once you have your image, it must be resized to power of two. You can try and stretch the image, but that will cause quality issues when you shrink it again, or if you do it over and over again. The best way is draw your image normal size into a base-2 texture with extra pixels to make a buffer. Here is the code (I modified this from someone else's code, but I can't find the original code, so if someone sees the original code, please let me know where it came from to give credit):

获得图像后,必须将其调整为2的幂。您可以尝试拉伸图像,但是当您再次缩小图像时,或者一次又一次地执行此操作时,这会导致质量问题。最好的方法是将图像的正常大小绘制成具有额外像素的base-2纹理以制作缓冲区。这是代码(我从其他人的代码中修改了这个代码,但我找不到原始代码,所以如果有人看到原始代码,请告诉我它来自哪里来表示信用):

-(UIImage*)base2Image:(UIImage *) srcImg {
    int frame2Base = 512;

    CGSize srcSize = [srcImg size];
    CGRect rec = CGRectMake(0, 0, frame2Base, frame2Base);

    [srcImg drawInRect:rec blendMode:kCGBlendModeNormal alpha:1.0];

    //create a context to do our clipping in
    UIGraphicsBeginImageContext(CGSizeMake(frame2Base, frame2Base));
    CGContextRef currentContext = UIGraphicsGetCurrentContext();

    //create a rect with the size we want to crop the image to
    CGRect clippedRect = CGRectMake(0, 0, frame2Base, frame2Base);
    CGContextClipToRect(currentContext, clippedRect);

    //create a rect equivalent to the full size of the image
    CGRect drawRect = CGRectMake(0, 0, srcSize.width, srcSize.height);

    //draw the image to our clipped context using our offset rect
    CGContextDrawImage(currentContext, drawRect, srcImg.CGImage);

    UIImage *dstImg =  UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return dstImg;
}

Now, once that is done you use a texture coordinate array to ONLY pull the correct sized section from your image. Such as this:

现在,完成后,您可以使用纹理坐标数组来仅从图像中拉出正确大小的部分。如:

GLfloat texCoords[] = {
    0.0, (float)frameHeight/2BaseHeight,
    0.0, 0.0,
    (float)frameWidth/2BaseWidth, (float)frameHeight/2BaseHeight,
    (float)frameWidth/2BaseWidth, 0.0
};

And there you have it. A screenshot from your visible screen that is now a texture.

你有它。来自可见屏幕的屏幕截图,现在是纹理。

#1


19  

You can use the view's layer property to get the CALayer and use renderInContext: on that to draw into a CoreGraphics context. You can set up a CoreGraphics context with memory you allocate yourself in order to receive a pixel buffer. You can then upload that to OpenGL by the normal method.

您可以使用视图的图层属性来获取CALayer并使用renderInContext:来绘制CoreGraphics上下文。您可以使用自己分配的内存设置CoreGraphics上下文,以便接收像素缓冲区。然后,您可以通过常规方法将其上载到OpenGL。

So: there's a means to get the pixel contents of a UIView and OpenGL will accept pixel buffers. There's no specific link between the two.

所以:有一种方法可以获得UIView的像素内容,OpenGL将接受像素缓冲区。这两者之间没有具体的联系。

Coding extemporaneously, the process would be something like:

即席编码,过程如下:

UIView *view = ... something ...;

// make space for an RGBA image of the view
GLubyte *pixelBuffer = (GLubyte *)malloc(
                               4 * 
                               view.bounds.size.width * 
                               view.bounds.size.height);

// create a suitable CoreGraphics context
CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context =
    CGBitmapContextCreate(pixelBuffer, 
                          view.bounds.size.width, view.bounds.size.height, 
                          8, 4*view.bounds.size.width, 
                          colourSpace, 
                          kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
CGColorSpaceRelease(colourSpace);

// draw the view to the buffer
[view.layer renderInContext:context];

// upload to OpenGL
glTexImage2D(GL_TEXTURE_2D, 0, 
             GL_RGBA,
             view.bounds.size.width, view.bounds.size.height, 0,
             GL_RGBA, GL_UNSIGNED_BYTE, pixelBuffer);

// clean up
CGContextRelease(context);
free(pixelBuffer);

That doesn't deal with issues surrounding non-power-of-two sized views on hardware without the non-power-of-two texture extension and assumes a suitable GL texture name has already been generated and bound. Check for yourself, but I think non-power-of-two is supported on SGX hardware (ie, iPhone 3GS onwards, the iPad and all but the 8gb third generation iPod Touch onwards) but not on MBX.

这不涉及围绕硬件的非二次幂大小视图的问题,而没有非二次幂纹理扩展,并且假设已经生成并绑定了合适的GL纹理名称。检查一下你自己,但我认为SGX硬件(即iPhone 3GS以上,iPad以及除了8gb第三代iPod Touch之外的所有硬件)都支持非二次幂,但不支持MBX。

The easiest way to deal with non-power-of-two textures here is probably to create a large enough power of two texture and to use glTexSubImage2D to upload just the portion from your source UIView.

在这里处理非幂二纹理的最简单方法可能是创建两个纹理足够大的功能,并使用glTexSubImage2D只上传源UIView中的部分。

#2


5  

Here is another method of grabbing an OpenGL Layer, which is to use glReadPixels. This will grab the visible layers BEHIND your openGL layer, as well (basically, your visible screen). Check out this question: How do I grab an image form my EAGLLayer ?

这是获取OpenGL Layer的另一种方法,即使用glReadPixels。这将抓住你的openGL层后面的可见层(基本上,你的可见屏幕)。看看这个问题:如何从我的EAGLLayer中获取图像?

Once you have your image, it must be resized to power of two. You can try and stretch the image, but that will cause quality issues when you shrink it again, or if you do it over and over again. The best way is draw your image normal size into a base-2 texture with extra pixels to make a buffer. Here is the code (I modified this from someone else's code, but I can't find the original code, so if someone sees the original code, please let me know where it came from to give credit):

获得图像后,必须将其调整为2的幂。您可以尝试拉伸图像,但是当您再次缩小图像时,或者一次又一次地执行此操作时,这会导致质量问题。最好的方法是将图像的正常大小绘制成具有额外像素的base-2纹理以制作缓冲区。这是代码(我从其他人的代码中修改了这个代码,但我找不到原始代码,所以如果有人看到原始代码,请告诉我它来自哪里来表示信用):

-(UIImage*)base2Image:(UIImage *) srcImg {
    int frame2Base = 512;

    CGSize srcSize = [srcImg size];
    CGRect rec = CGRectMake(0, 0, frame2Base, frame2Base);

    [srcImg drawInRect:rec blendMode:kCGBlendModeNormal alpha:1.0];

    //create a context to do our clipping in
    UIGraphicsBeginImageContext(CGSizeMake(frame2Base, frame2Base));
    CGContextRef currentContext = UIGraphicsGetCurrentContext();

    //create a rect with the size we want to crop the image to
    CGRect clippedRect = CGRectMake(0, 0, frame2Base, frame2Base);
    CGContextClipToRect(currentContext, clippedRect);

    //create a rect equivalent to the full size of the image
    CGRect drawRect = CGRectMake(0, 0, srcSize.width, srcSize.height);

    //draw the image to our clipped context using our offset rect
    CGContextDrawImage(currentContext, drawRect, srcImg.CGImage);

    UIImage *dstImg =  UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return dstImg;
}

Now, once that is done you use a texture coordinate array to ONLY pull the correct sized section from your image. Such as this:

现在,完成后,您可以使用纹理坐标数组来仅从图像中拉出正确大小的部分。如:

GLfloat texCoords[] = {
    0.0, (float)frameHeight/2BaseHeight,
    0.0, 0.0,
    (float)frameWidth/2BaseWidth, (float)frameHeight/2BaseHeight,
    (float)frameWidth/2BaseWidth, 0.0
};

And there you have it. A screenshot from your visible screen that is now a texture.

你有它。来自可见屏幕的屏幕截图,现在是纹理。