2

摘要:将像素数据从 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];
}

对于文件写入,我使用 ImageKitIKSaveOptions附件面板设置输出文件类型和相关选项,然后使用以下代码进行写入:

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。

图像导出为 JPEG,颜色正确
(来源:walkytalky.net

图像导出为 PDF,移液器上有红色条带
(来源:walkytalky.net

复制到剪贴板在 的当前实现中没有出现这个红色条纹image,但原始实现中出现了,它生成了imageRepusingNSCalibratedRGBColorSpace而不是NSDeviceRGBColorSpace. 所以我猜我从 OpenGL 获得的像素中的颜色表示存在一些问题,无法正确通过后续转换,但我不知道该怎么做。

那么,谁能告诉我(i)是什么原因造成的,以及(ii)我怎样才能让它消失?我不太关心所有格式,但我真的希望至少 PDF 可以工作。

4

1 回答 1

3

好的。正如遇到这个问题的震耳欲聋的沉默所证明的那样,这个问题变得有点模糊。但是解决方法很简单,所以我在这里描述它以防万一有人想知道。

摘要:某些文件导出格式不能很好地处理渲染像素中的半透明度。

我不明白这个的确切原因,尽管它可能与 alpha 预乘的存在或不存在有关。所有格式似乎都可以使用完全透明的像素,如果格式不支持透明度,则将它们渲染为透明或白色。但是具有部分 alpha 的像素以及颜色通道中的某些内容可能会被破坏。

碰巧的是,我什至不希望图像的任何部分都是半透明的,并且确实设置glDisable(GL_BLEND)在相关的渲染代码之前。但是,对象是使用OpenGL 主页上这个看似规范的集合中的材质渲染的,其中一些在其镜面反射、漫反射和环境颜色中包含 1.0 以外的 alpha 值。我盲目地复制了这个,没有注意到它可能会导致一些不需要的半透明。

因此,就我的目的而言,解决方案很简单:更改材质定义,使 alpha 分量始终为 1.0

请注意,某些图像格式,例如 PNG 和 TIFF,确实完全支持半透明,所以如果您需要,那么这些是可以选择的。

事实上,这就是让我得到答案的原因。但是一开始并不明显,因为我使用的是 OS X Preview 来查看文件,并且在默认视图设置下半透明并不明显:

导出到 BMP 的半透明移液器
(来源:walkytalky.net

半透明移液器导出为 PNG,然后在预览中为 JPG
(来源:walkytalky.net

导出为 PNG 的半透明移液器,在预览中查看
(来源:walkytalky.net

导出为 PNG 的半透明移液器,在 Photoshop 中查看
(来源:walkytalky.net

因此,这一集的第二个教训是:启用视图 | 在预览中显示图像背景以获取棋盘并显示任何杂散透明度。

于 2010-05-26T23:23:11.670 回答