开发者

Changing the Color Space of NSImage

开发者 https://www.devze.com 2023-01-07 22:40 出处:网络
Is there a way I can change the color space of an NSimage or NSBitmapImageRep/CGimage 开发者_运维技巧or the like. I am open to any way. Preferably the way photoshop does it.The problem with the code y

Is there a way I can change the color space of an NSimage or NSBitmapImageRep/CGimage 开发者_运维技巧or the like. I am open to any way. Preferably the way photoshop does it.


The problem with the code you see below

CGImageRef CGImageCreateCopyWithColorSpace (
CGImageRef image,
CGColorSpaceRef colorspace
);

is not working because Quartz2D does not support alpha for CMYK images or gray images, it is supported only by RGB images. What you should do is creating 2 images then combine then in case of a CMYK color space and image with alpha. I have searched a lot about this topic and finally found. May be author of this question doesn't need it anymore but someone else may need it.

  1. Create new NSBitmapImageRep

    let imageRep = NSBitmapImageRep(bitmapDataPlanes: nil,
                                          pixelsWide: Int(round(imageSize.width)),
                                          pixelsHigh: Int(round(imageSize.height)),
                                       bitsPerSample: 8,
                                     samplesPerPixel: 4,
                                            hasAlpha: false,
                                            isPlanar: false,
                                      colorSpaceName: NSDeviceCMYKColorSpace,
                                        bitmapFormat: NSBitmapFormat(rawValue: 0),
                                         bytesPerRow: Int(round(imageSize.width) * CGFloat(4)),
                                        bitsPerPixel: 0)
    
  2. You need to draw your image into the new bitmap lets say you have colorSpace: NSColorSpace

    let context = NSGraphicsContext(bitmapImageRep: imageRep)
    NSGraphicsContext.saveGraphicsState()
    NSGraphicsContext.setCurrentContext(context)
    imageRep.setProperty(NSImageColorSyncProfileData, withValue: colorSpace.ICCProfileData)
    // Do your drawing here
    NSGraphicsContext.restoreGraphicsState()
    
  3. After this you will have an imageRep that is the image with correct color space but no alpha (transparency).

  4. You need a mask bitmap. Obtaining mask is tricky.

    let imageRep = NSBitmapImageRep(bitmapDataPlanes: nil,
                                          pixelsWide: Int(round(imageSize.width)),
                                          pixelsHigh: Int(round(imageSize.height)),
                                       bitsPerSample: 8,
                                     samplesPerPixel: 1,
                                            hasAlpha: false,
                                            isPlanar: false,
                                      colorSpaceName: NSDeviceWhiteColorSpace,
                                        bitmapFormat: NSBitmapFormat(rawValue: 0),
                                         bytesPerRow: Int(round(imageSize.width)),
                                        bitsPerPixel: 0)
    
  5. Clip white image to your image with transparency

    if let graphicsPort = NSGraphicsContext.currentContext()?.graphicsPort {
        let context = unsafeBitCast(graphicsPort, CGContextRef.self)
        let imgRect = NSRect(origin: NSPoint.zero, size: image.extent.size)
        let ciContext = CIContext()
        let cgImage = ciContext.createCGImage(image, fromRect: image.extent)
        CGContextClipToMask(context, imgRect, cgImage)
    }
    
  6. Colorize all pixel in white color, this will be clipped to image.

    let context = NSGraphicsContext(bitmapImageRep: imageRep)
    NSGraphicsContext.saveGraphicsState()
    NSGraphicsContext.setCurrentContext(context)
    imageRep.setProperty(NSImageColorSyncProfileData, withValue: colorSpace.ICCProfileData)
    NSColor.whiteColor().setFill()
    NSBezierPath.fillRect(NSRect(origin: NSPoint.zero, size: imageSize))
    NSGraphicsContext.restoreGraphicsState()
    
  7. At this step you have a image and a mask. Next we need to combine them. I am using this little algorithm (you need to verify that mask and source image have same sizes and same colorSpaceModel):

    func createCMYKAImageRepByApplingAlphaMask(srcImageRep: NSBitmapImageRep, alphaMaskImageRep alphaMask: NSBitmapImageRep) -> NSBitmapImageRep? {
        if canApplyMaskRepOnImageRep(srcImageRep, maskRep: alphaMask) == false {
            return nil
        }
        let alphaData = alphaMask.bitmapData
        let srcData = srcImageRep.bitmapData
        if let imageWithAlphaMaskRep = createEmptyCMYKABitmapImageRep(alphaMask.size) {
            if let colorSpaceData = imageColorSpace?.ICCProfileData {
                 imageWithAlphaMaskRep.setProperty(NSImageColorSyncProfileData, withValue: colorSpaceData)
            }
            fillPixelsWithComponentsData(imageWithAlphaMaskRep, components: { (pixelIdx: Int) -> (UInt8, UInt8, UInt8, UInt8, UInt8) in
                let cyan = srcData[pixelIdx * 4 + 0]
                let magenta = srcData[pixelIdx * 4 + 1]
                let yellow = srcData[pixelIdx * 4 + 2]
                let black = srcData[pixelIdx * 4 + 3]
                let alpha = alphaData[pixelIdx]
                return (cyan, magenta, yellow, black, alpha)
            })
            return imageWithAlphaMaskRep
        }
        return nil
    }
    
    func createEmptyBitmapImageRep() -> NSBitmapImageRep? {
        let imageRep = NSBitmapImageRep(bitmapDataPlanes: nil,
                                              pixelsWide: Int(round(imageSize.width)),
                                              pixelsHigh: Int(round(imageSize.height)),
                                           bitsPerSample: 8,
                                         samplesPerPixel: 5,
                                                hasAlpha: true,
                                                isPlanar: false,
                                          colorSpaceName: NSDeviceCMYKColorSpace,
                                            bitmapFormat: NSBitmapFormat(rawValue: 0),
                                             bytesPerRow: Int(round(imageSize.width) * CGFloat(5)),
                                            bitsPerPixel: 0)
        return imageRep
    }
    
    private func fillPixelsWithComponentsData(imgRep: NSBitmapImageRep,
    components: (Int) -> (cyan:UInt8, magenta:UInt8, yellow:UInt8, black:UInt8, alpha:UInt8)) {
        let imageRawData = imgRep.bitmapData
        let imageWidth = Int(imgRep.size.width)
        let imageHeight = Int(imgRep.size.height)
        for pixelIdx in 0 ..< (imageWidth * imageHeight) {
            let (cyan, magenta, yellow, black, alpha) = components(pixelIdx)
            let fAlpha = Float(alpha) / 255
            imageRawData[pixelIdx * 5 + 0] = UInt8(Float(cyan) * fAlpha)
            imageRawData[pixelIdx * 5 + 1] = UInt8(Float(magenta) * fAlpha)
            imageRawData[pixelIdx * 5 + 2] = UInt8(Float(yellow) * fAlpha)
            imageRawData[pixelIdx * 5 + 3] = UInt8(Float(black) * fAlpha)
            imageRawData[pixelIdx * 5 + 4] = alpha
        }
    }
    


CGImageRef CGImageCreateCopyWithColorSpace (
   CGImageRef image,
   CGColorSpaceRef colorspace
);

To get a CGColorSpaceRef, you can do things like

CGColorSpaceRef CGColorSpaceCreateDeviceRGB();

OR see this page for how to create other color spaces.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号