摘要:将像素数据从NSOpenGLView导出到某些文件格式会产生错误的颜色
我正在开发一个可视化一些实验数据的应用程序。它的一个功能是在NSOpenGLView
子类中呈现数据,并允许将生成的图像导出到文件或复制到剪贴板。
视图将数据导出为NSImage
,生成如下:
- (NSImage*) image
{
NSBitmapImageRep* imageRep;
NSImage* image;
NSSize viewSize = [self bounds].size;
int width = viewSize.width;
int height = viewSize.height;
[self lockFocus];
[self drawRect:[self bounds]];
[self unlockFocus];
imageRep=[[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
pixelsWide:width
pixelsHigh:height
bitsPerSample:8
samplesPerPixel:4
hasAlpha:YES
isPlanar:NO
colorSpaceName:NSDeviceRGBColorSpace
bytesPerRow:width*4
bitsPerPixel:32] autorelease];
[[self openGLContext] makeCurrentContext];
glReadPixels(0,0,width,height,GL_RGBA,GL_UNSIGNED_BYTE,[imageRep bitmapData]);
image=[[[NSImage alloc] initWithSize:NSMakeSize(width,height)] autorelease];
[image addRepresentation:imageRep];
[image setFlipped:YES]; // this is deprecated in 10.6
[image lockFocusOnRepresentation:imageRep]; // this will flip the rep
[image unlockFocus];
return image;
}
复制非常简单地使用此图像,如下所示:
- (IBAction) copy:(id) sender
{
NSImage* img = [self image];
NSPasteboard* pb = [NSPasteboard generalPasteboard];
[pb clearContents];
NSArray* copied = [NSArray arrayWithObject:img];
[pb writeObjects:copied];
}
对于文件编写,我使用ImageKit IKSaveOptions
附件面板设置输出文件类型和相关选项,然后使用以下代码进行编写:
NSImage* glImage = [glView image];
NSRect rect = [glView bounds];
rect.origin.x = rect.origin.y = 0;
img = [glImage CGImageForProposedRect:&rect
context:[NSGraphicsContext currentContext]
hints:nil];
if (img)
{
NSURL* url = [NSURL fileURLWithPath: path];
CGImageDestinationRef dest = CGImageDestinationCreateWithURL((CFURLRef)url,
(CFStringRef)newUTType,
1,
NULL);
if (dest)
{
CGImageDestinationAddImage(dest,
img,
(CFDictionaryRef)[imgSaveOptions imageProperties]);
CGImageDestinationFinalize(dest);
CFRelease(dest);
}
}
(我在这里修剪了一些无关的代码,但是就我所见,没有什么会影响结果。newUTType
来自IKSaveOptions
小组。)
当文件导出为GIF,JPEG,PNG,PSD或TIFF时,此工作正常,但导出为PDF,BMP,TGA,ICNS和JPEG-2000会在图像的一部分上产生红色伪影。示例图像如下,第一个导出为JPG,第二个导出为PDF。
Image exported as JPEG, with colours correct http://walkytalky.net/extern/ionx-jpg.jpg Image exported as PDF, with red banding on pipette http://walkytalky.net/extern/ionx-pdf.jpg
复制到剪贴板时,image
的当前实现没有显示此红色条带,但 与原始实现,后者使用{{1}生成imageRep
而不是NSCalibratedRGBColorSpace
。所以我猜测我从OpenGL获得的像素中的颜色表示存在一些问题,这些问题没有得到正确的后续转换,但是我不知道如何处理它。
那么,任何人都可以告诉我(i)造成这种情况的原因,以及(ii)我怎样才能让它消失?我并不在乎所有格式,但我真的至少像PDF一样工作。
答案 0 :(得分:3)
行。正如震耳欲聋的沉默所证明的那样,这个问题变得有点模糊。但是解决方法很简单,所以我在这里描述它以防万一有人想知道。
摘要:某些文件导出格式无法很好地处理渲染像素中的半透明效果。
我不明白这个的确切原因,尽管它可能与alpha预乘的存在与否有关。对于完全透明的像素,所有格式似乎都很好,如果格式不支持透明度,则将它们渲染为透明或白色。但是,具有部分alpha的像素加上颜色通道中的某些内容可能会受到损坏。
碰巧的是,我甚至没有希望图像的任何部分都是半透明的,并且确实在相关的渲染代码之前设置了glDisable(GL_BLEND)
。但是,使用来自this seemingly-canonical collection at the OpenGL home site的材质渲染对象,其中一些在其镜面反射,漫反射和环境颜色中包含除<1.0>以外的Alpha值。我曾盲目地复制了这个,却没有注意到这可能导致一些不必要的半透明的事实。
就我的目的而言,解决方案很简单:更改材质定义,使alpha组件始终为1.0 。
请注意,某些图片格式(例如PNG和TIFF, )完全支持半透明效果,因此如果您需要,则可以使用这些格式。
事实上,这让我想到了答案。然而,起初并不明显,因为我使用OS X Preview来查看文件,并且使用默认视图设置时半透明度不明显:
translucent pipette as exported to BMP http://walkytalky.net/extern/wbmp.jpg translucent pipette as exported to PNG and thence JPG in Preview http://walkytalky.net/extern/without-alpha.jpg
translucent pipette as exported to PNG, viewed in Preview http://walkytalky.net/extern/wpreview.jpg translucent pipette as exported to PNG, viewed in Photoshop http://walkytalky.net/extern/with-alpha.jpg
因此,整集的第二个教训是:启用查看|在预览中显示图像背景以获取棋盘并显示任何杂散透明度。