绘制标准的NSImage反转(白色代替黑色)

4

我想要将标准的NSImage绘制成白色而不是黑色。以下代码可以在当前的NSGraphicsContext中以黑色绘制图像:

NSImage* image = [NSImage imageNamed:NSImageNameEnterFullScreenTemplate];
[image drawInRect:r fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];

我本以为NSCompositeXOR可以解决问题,但是不行。我需要走复杂的[CIFilter filterWithName:@"CIColorInvert"]路径吗?感觉一定是我遗漏了简单的东西。

3个回答

6

核心图像路线是最可靠的。实际上并不复杂,我已经发布了一个示例如下。如果您知道您的图像都没有翻转,那么可以删除变换代码。要小心的主要是从NSImageCIImage的转换可能会影响性能,因此如果可能,请确保缓存CIImage,并且不要在每个绘制操作期间重新创建它。

CIImage* ciImage = [[CIImage alloc] initWithData:[yourImage TIFFRepresentation]];
if ([yourImage isFlipped])
{
    CGRect cgRect    = [ciImage extent];
    CGAffineTransform transform;
    transform = CGAffineTransformMakeTranslation(0.0,cgRect.size.height);
    transform = CGAffineTransformScale(transform, 1.0, -1.0);
    ciImage   = [ciImage imageByApplyingTransform:transform];
}
CIFilter* filter = [CIFilter filterWithName:@"CIColorInvert"];
[filter setDefaults];
[filter setValue:ciImage forKey:@"inputImage"];
CIImage* output = [filter valueForKey:@"outputImage"];
[output drawAtPoint:NSZeroPoint fromRect:NSRectFromCGRect([output extent]) operation:NSCompositeSourceOver fraction:1.0];

注意:释放/保留内存管理作为一项练习,上述代码假定垃圾回收。
如果您想以任意大小呈现图像,可以执行以下操作:
NSSize imageSize = NSMakeSize(1024,768); //or whatever size you want
[yourImage setSize:imageSize];
[yourImage lockFocus];
NSBitmapImageRep* bitmap = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMakeRect(0, 0, imageSize.width, imageSize.height)];
[yourImage unlockFocus];
CIImage* image = [CIImage imageWithData:[bitmap TIFFRepresentation]];

谢谢,这确实解决了颜色反转的问题。不幸的是,它并没有给我完整的分辨率:似乎最初的TIFFRepresentation比我想要绘制的尺寸小得多。我该如何确保获得高分辨率的数据? - smartgo
您可以使用所需的大小从NSImage创建NSBitmapImageRep,然后从位图创建CIImage,请参见此处:http://pastie.org/798088 - Rob Keniger
没问题。我已经将pastie.org上的代码添加到我的答案中,这样人们就不必去寻找了。 - Rob Keniger
-[NSImage lockFocus] 会修改原始图像。就像在一个大小与图像相同的窗口上下文中绘制图像,留下焦点进行更多绘制,然后将结果快照回unlockFocus中的图像。如果原始图像不是具有一个片段的位图,则将在此处丢失数据。这也非常低效- lockFocus创建一个新缓冲区,如描述所示。然后TIFFRepresentation会创建您的图像的未压缩副本-基本上是另一个缓冲区。然后你使用它初始化CIImage,这个过程又解码了编码的TIFF数据,又是另一个副本。 - Ken
使用-CGImageForProposedRect:context:hints:方法。不要复制。 - Ken
谢谢提供信息。不幸的是,该方法仅适用于10.6版本。在Leopard中有更好的方法吗? - Rob Keniger

3

这里是使用Swift 5.1的解决方案,有些基于上面的解决方案。请注意,我没有缓存图像,因此可能不是最有效的,因为我的主要用例是根据当前颜色方案在工具栏按钮中翻转小的单色图像。

"Original Answer"的翻译是"最初的回答"

import os
import AppKit
import Foundation

public extension NSImage {

    func inverted() -> NSImage {
        guard let cgImage = self.cgImage(forProposedRect: nil, context: nil, hints: nil) else {
            os_log(.error, "Could not create CGImage from NSImage")
            return self
        }

        let ciImage = CIImage(cgImage: cgImage)
        guard let filter = CIFilter(name: "CIColorInvert") else {
            os_log(.error, "Could not create CIColorInvert filter")
            return self
        }

        filter.setValue(ciImage, forKey: kCIInputImageKey)
        guard let outputImage = filter.outputImage else {
            os_log(.error, "Could not obtain output CIImage from filter")
            return self
        }

        guard let outputCgImage = outputImage.toCGImage() else {
            os_log(.error, "Could not create CGImage from CIImage")
            return self
        }

        return NSImage(cgImage: outputCgImage, size: self.size)
    }
}

fileprivate extension CIImage {
    func toCGImage() -> CGImage? {
        let context = CIContext(options: nil)
        if let cgImage = context.createCGImage(self, from: self.extent) {
            return cgImage
        }
        return nil
    }
}

0

仅有一个注意事项:我发现CIColorInvert滤镜并不总是可靠的。例如,如果您想要反转在Photoshop中反转的图像,则CIFilter将产生一个明显更亮的图像。据我所知,这是因为CIFilter的伽马值(伽马值为1)与来自其他来源的图像之间的差异。

当我寻找改变CIFilter伽马值的方法时,我发现有一个注释说CIContext存在一个错误:将其伽马值从默认值1更改会产生不可预测的结果。

尽管如此,还有另一种解决NSImage反转的方法,它始终会产生正确的结果-通过反转NSBitmapImageRep的像素。

我正在重新发布来自etutorials.org的代码(http://bit.ly/Y6GpLn):

// srcImageRep is the NSBitmapImageRep of the source image
int n = [srcImageRep bitsPerPixel] / 8;           // Bytes per pixel
int w = [srcImageRep pixelsWide];
int h = [srcImageRep pixelsHigh];
int rowBytes = [srcImageRep bytesPerRow];
int i;

NSImage *destImage = [[NSImage alloc] initWithSize:NSMakeSize(w, h)];
NSBitmapImageRep *destImageRep = [[[NSBitmapImageRep alloc] 
      initWithBitmapDataPlanes:NULL
          pixelsWide:w
          pixelsHigh:h
          bitsPerSample:8
          samplesPerPixel:n
          hasAlpha:[srcImageRep hasAlpha] 
          isPlanar:NO
          colorSpaceName:[srcImageRep colorSpaceName]
          bytesPerRow:rowBytes 
          bitsPerPixel:NULL] autorelease];

unsigned char *srcData = [srcImageRep bitmapData];
unsigned char *destData = [destImageRep bitmapData];

for ( i = 0; i < rowBytes * h; i++ )
    *(destData + i) = 255 - *(srcData + i);

[destImage addRepresentation:destImageRep];

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接