Objective c 绘制标准NSImage反转(白色而非黑色)

Objective c 绘制标准NSImage反转(白色而非黑色),objective-c,macos,drawing,nsimage,invert,Objective C,Macos,Drawing,Nsimage,Invert,我想用白色而不是黑色画一幅标准的NSImage。在当前NSGraphicsContext中以黑色绘制图像时,以下操作非常有效: NSImage* image = [NSImage imageNamed:NSImageNameEnterFullScreenTemplate]; [image drawInRect:r fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; 我希望NSCompositeXOR能做到这一点,

我想用白色而不是黑色画一幅标准的NSImage。在当前NSGraphicsContext中以黑色绘制图像时,以下操作非常有效:

NSImage* image = [NSImage imageNamed:NSImageNameEnterFullScreenTemplate];
[image drawInRect:r fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
我希望NSCompositeXOR能做到这一点,但没有。我需要走复杂的[CIFilter filter with name:@“CIColorInvert”]路径吗?我觉得我一定错过了一些简单的事情


Anders

核心图像路径将是最可靠的。这其实并不复杂,我在下面发布了一个示例。如果您知道没有图像将被翻转,则可以删除转换代码。需要注意的主要事项是,从
NSImage
CIImage
的转换在性能上可能代价高昂,因此您应该确保缓存
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]];

只需注意一点:我发现CIColorInvert过滤器并不总是可靠的。例如,如果您想反转在Photoshop中反转的图像,CIFilter将生成更亮的图像。据我所知,这是因为CIFilter的gamma值(gamma为1)和来自其他来源的图像不同

当我在寻找更改CIFilter的gamma值的方法时,我发现CIContext中有一个bug:将其gamma值从默认值1更改将产生不可预测的结果

无论如何,还有另一种反转NSImage的解决方案,它总是通过反转NSBitmapImageRep的像素来产生正确的结果

我正在从etutorials.org()重新发布代码:

//srcImageRep是源映像的NSBitmapImageRep
int n=[srcmagerep bitsPerPixel]/8;//每像素字节数
int w=[srcImageRep pixelsWide];
int h=[srcmagerep pixelsHigh];
int rowBytes=[srcmagerep bytesPerRow];
int i;
NSImage*desimage=[[NSImage alloc]initWithSize:NSMakeSize(w,h)];
NSBitmapImageRep*DestinmageRep=[[NSBitmapImageRep alloc]
initWithBitmapDataPlanes:NULL
像素宽度:w
像素h:h
位示例:8
采样每像素:n
hasAlpha:[srcImageRep hasAlpha]
伊斯普兰:没有
colorSpaceName:[srcImageRep colorSpaceName]
字节数:行字节数
bitsPerPixel:NULL]自动释放];
无符号字符*srcData=[srcmagerep bitmapData];
无符号字符*destData=[destmagerep bitmapData];
对于(i=0;i
以下是一个使用Swift 5.1的解决方案,它在一定程度上基于上述解决方案。请注意,我没有缓存图像,因此它可能不是最有效的,因为我的主要用例是根据当前颜色方案是浅还是暗来翻转工具栏按钮中的小单色图像

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
    }
}

谢谢,这确实解决了颜色反转问题。不幸的是,它没有给我完整的分辨率:看起来初始的TIFF表示比我想要绘制它的大小要小得多。如何确保以高分辨率获取数据?您可以使用所需大小从
NSImage
创建
NSBitmapImageRep
,然后从位图创建
CIImage
,请参见此处:无需担心。我将pastie.org的代码添加到我的答案中,这样人们就不必去打猎了。-[NSImage lockFocus]修改了原始图像。这就像在一个像窗口一样大小的上下文中绘制图像,将焦点锁定在那里以进行更多绘制,然后将结果快照回unlockFocus中的图像中。如果原始图像不是带有一个切片的位图,则会丢失数据。它的效率也很低——lockFocus创建了一个新的缓冲区,如前所述。然后TIFFRepresentation创建图像的未压缩副本-基本上是另一个缓冲区。然后用它初始化CIImage,另一个副本用于解码编码的TIFF数据。没有复印件。最好的答案,谢谢
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
    }
}