绘制标准NSImage倒置(用白色代替黑色)

问题描述 投票:3回答:3

我正在尝试用白色而不是黑色绘制标准的NSImage。对于在当前NSGraphicsContext中以黑色绘制图像,以下工作正常:

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

我希望NSCompositeXOR可以解决这个问题,但是没有。我是否需要沿着复杂的[CIFilter filterWithName:@“ CIColorInvert”]路径浏览?我觉得我必须缺少一些简单的东西。

安德斯

objective-c macos drawing nsimage invert
3个回答
6
投票

Core Image路由将是最可靠的。它实际上不是很复杂,我在下面发布了一个示例。如果您知道所有图像都不会翻转,则可以删除转换代码。要注意的主要事情是,从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]];

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];

0
投票

这里是使用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
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.