我正在制作一个PDF注释器,当您切换页面时,它必须重新绘制所有先前绘制的OpenGL内容(以JSON格式保存到文件中)。 问题是要绘制的内容越多,花费的时间就越长。 我为每个页面保存了一个UIImage到磁盘,所以我希望通过一次将UIImage绘制到EAGLContext上来加快这个过程。
我想知道如何获取UIImage(或JPEG/PNG文件)并将其直接绘制到屏幕上。 它必须在EAGLView上的原因是因为它需要支持橡皮擦,而使用常规的UIKit方式将无法使用它。
我假设有某种方法可以将画笔设置为整个图像,然后用它盖章一次屏幕。 有什么建议吗?
作为一个迂腐的注释,没有名为EAGLView的标准类,但我假设你指的是Apple托管OpenGL ES内容的示例UIView子类之一。
执行此操作的第一步是将 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,您只需为几何体设置纹理坐标并绘制两个三角形。
在这个答案中,我有一些OpenGL ES 2.0代码。