将UIImage(或JPEG)绘制到EAGLEView中

时间:2012-08-14 17:57:58

标签: objective-c ios opengl-es uiimageview eaglview

我正在制作PDF注释器,当你切换页面时,它必须重绘以前绘制的所有OpenGL内容(以JSON格式保存到文件中)。问题是需要的时间越长,绘制的内容就越多。我为每个页面保存了一个UIImage到磁盘,所以我希望通过将UIImage绘制到EAGLContext上来加快这个过程。

我想知道如何拍摄UIImage(或JPEG / PNG文件)并将其直接绘制到屏幕上。它必须在EAGLView上的原因是因为它需要支持橡皮擦,并且使用常规的UIKit方式不适用于它。

我认为有一种方法可以将画笔设置为整个图像,只需将画面标记一次即可。有什么建议吗?

1 个答案:

答案 0 :(得分:3)

作为一个迂腐的笔记,there is no standard class named EAGLView,但我假设您指的是Apple的一个样本UIView子类,它承载OpenGL ES内容。

这样做的第一步是将UIImage加载到纹理中。以下是我在图像处理框架中使用的一些代码(newImageSource是输入UIImage):

CGSize pointSizeOfImage = [newImageSource size];
CGFloat scaleOfImage = [newImageSource scale];
pixelSizeOfImage = CGSizeMake(scaleOfImage * pointSizeOfImage.width, scaleOfImage * pointSizeOfImage.height);
CGSize pixelSizeToUseForTexture = pixelSizeOfImage;

BOOL shouldRedrawUsingCoreGraphics = YES;

// For now, deal with images larger than the maximum texture size by resizing to be within that limit
CGSize scaledImageSizeToFitOnGPU = [GPUImageOpenGLESContext sizeThatFitsWithinATextureForSize:pixelSizeOfImage];
if (!CGSizeEqualToSize(scaledImageSizeToFitOnGPU, pixelSizeOfImage))
{
    pixelSizeOfImage = scaledImageSizeToFitOnGPU;
    pixelSizeToUseForTexture = pixelSizeOfImage;
    shouldRedrawUsingCoreGraphics = YES;
}

if (self.shouldSmoothlyScaleOutput)
{
    // In order to use mipmaps, you need to provide power-of-two textures, so convert to the next largest power of two and stretch to fill
    CGFloat powerClosestToWidth = ceil(log2(pixelSizeOfImage.width));
    CGFloat powerClosestToHeight = ceil(log2(pixelSizeOfImage.height));

    pixelSizeToUseForTexture = CGSizeMake(pow(2.0, powerClosestToWidth), pow(2.0, powerClosestToHeight));

    shouldRedrawUsingCoreGraphics = YES;
}

GLubyte *imageData = NULL;
CFDataRef dataFromImageDataProvider;

if (shouldRedrawUsingCoreGraphics)
{
    // For resized image, redraw
    imageData = (GLubyte *) calloc(1, (int)pixelSizeToUseForTexture.width * (int)pixelSizeToUseForTexture.height * 4);

    CGColorSpaceRef genericRGBColorspace = CGColorSpaceCreateDeviceRGB();    
    CGContextRef imageContext = CGBitmapContextCreate(imageData, (int)pixelSizeToUseForTexture.width, (int)pixelSizeToUseForTexture.height, 8, (int)pixelSizeToUseForTexture.width * 4, genericRGBColorspace,  kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
    CGContextDrawImage(imageContext, CGRectMake(0.0, 0.0, pixelSizeToUseForTexture.width, pixelSizeToUseForTexture.height), [newImageSource CGImage]);
    CGContextRelease(imageContext);
    CGColorSpaceRelease(genericRGBColorspace);
}
else
{
    // Access the raw image bytes directly
    dataFromImageDataProvider = CGDataProviderCopyData(CGImageGetDataProvider([newImageSource CGImage]));
    imageData = (GLubyte *)CFDataGetBytePtr(dataFromImageDataProvider);
}    

glBindTexture(GL_TEXTURE_2D, outputTexture);
if (self.shouldSmoothlyScaleOutput)
{
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)pixelSizeToUseForTexture.width, (int)pixelSizeToUseForTexture.height, 0, GL_BGRA, GL_UNSIGNED_BYTE, imageData);

if (self.shouldSmoothlyScaleOutput)
{
    glGenerateMipmap(GL_TEXTURE_2D);
}

if (shouldRedrawUsingCoreGraphics)
{
    free(imageData);
}
else
{
    CFRelease(dataFromImageDataProvider);
}

正如您所看到的,这有一些函数用于调整超出设备最大纹理大小的图像(上面代码中的类方法仅查询最大纹理大小),以及是否为布尔标志生成纹理的mipmap以获得更平滑的下采样。如果您不关心这些情况,可以删除这些。这也是OpenGL ES 2.0代码,因此可能需要添加OES个后缀或两个以上的某些函数,以便它们与1.1一起使用。

在纹理中使用UIImage后,您可以使用纹理四边形(两个构成矩形的三角形,并为角设置适当的纹理坐标)将其绘制到屏幕上。如何执行此操作将在OpenGL ES 1.1和2.0之间有所不同。对于2.0,您使用直通着色器程序,该程序只读取纹理中该位置的颜色并将其绘制到屏幕上,对于1.1,您只需设置几何体的纹理坐标并绘制两个三角形。

我在this answer中有一些OpenGL ES 2.0代码。