【问题标题】:NSImage to NSBitmapImageRepNSImage 到 NSBitmapImageRep
【发布时间】:2012-10-15 13:38:44
【问题描述】:

如何将 NSImage 转换为 NSBitmapImageRep?我有代码:

- (NSBitmapImageRep *)bitmapImageRepresentation
{
    NSBitmapImageRep *ret = (NSBitmapImageRep *)[self representations];

    if(![ret isKindOfClass:[NSBitmapImageRep class]])
    {
        ret = nil;
        for(NSBitmapImageRep *rep in [self representations])
            if([rep isKindOfClass:[NSBitmapImageRep class]])
            {
                ret = rep;
                break;
            }
    }

    if(ret == nil)
    {
        NSSize size = [self size];

        size_t width         = size.width;
        size_t height        = size.height;
        size_t bitsPerComp   = 32;
        size_t bytesPerPixel = (bitsPerComp / CHAR_BIT) * 4;
        size_t bytesPerRow   = bytesPerPixel * width;
        size_t totalBytes    = height * bytesPerRow;

        NSMutableData *data = [NSMutableData dataWithBytesNoCopy:calloc(totalBytes, 1) length:totalBytes freeWhenDone:YES];

        CGColorSpaceRef space = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);

        CGContextRef ctx = CGBitmapContextCreate([data mutableBytes], width, height, bitsPerComp, bytesPerRow, CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB), kCGBitmapFloatComponents | kCGImageAlphaPremultipliedLast);

        if(ctx != NULL)
        {
            [NSGraphicsContext saveGraphicsState];
            [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:ctx flipped:[self isFlipped]]];

            [self drawAtPoint:NSZeroPoint fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0];

            [NSGraphicsContext restoreGraphicsState];

            CGImageRef img = CGBitmapContextCreateImage(ctx);

            ret = [[NSBitmapImageRep alloc] initWithCGImage:img];
            [self addRepresentation:ret];

            CFRelease(img);
            CFRelease(space);

            CGContextRelease(ctx);
        }
    }


    return ret;
}

它可以工作,但会导致内存泄漏。至少当我将它与 ARC 一起使用时。使用initWithData:[nsimagename TIFFRepresentation] 无法正常工作。某些图像的表示效果不佳。我认为这取决于图像的格式和色彩空间。还有其他方法可以实现吗?


mrwalker 建议解决方案的结果:

原图:

转换为 bitmapimagerep 并返回图像 1 次:

转换为bitmapimagerep并返回图像3次:

如您所见,每次转换为 NSBitmapImageRep 后图像都会变暗

【问题讨论】:

  • Hockeyman,您的问题还有完整的解决方案吗?我正在寻找一种无需太多过程即可识别像素颜色的方法。也许 NSBitmapImageRep 可以以某种方式帮助我。谢谢。
  • 您正在进行一些色彩空间转换,色彩空间转换通常不是无损的,因为它通常涉及浮点计算,稍后需要将其四舍五入为整数值。随着时间的推移,你会得到越来越多的舍入错误。你的代码太复杂了,看这里:stackoverflow.com/a/17510651/15809

标签: objective-c macos bitmap nsimage nsbitmapimagerep


【解决方案1】:

@Julius:你的代码太复杂了,而且有几个错误。我将仅对第一行进行更正:

- (NSBitmapImageRep *)bitmapImageRepresentation
{
   for( NSImageRep *rep in [self representations] )
      if( [rep isKindOfClass:[NSBitmapImageRep class]] ) return rep;
   return nil;
}

如果第一个 NSBitmapImageRep 是表示中的成员,这将提取第一个 NSBitmapImageRep,如果没有 NSBitmapImageRep,则返回 nil。我将为您提供另一种解决方案,无论NSImageReps 在表示中如何,它都将始终有效:NSBitmapImageRepNSPDFImageRepNSCGImageSnapshotRep 或 ...

- (NSBitmapImageRep *)bitmapImageRepresentation
{
   CGImageRef CGImage = [self CGImageForProposedRect:nil context:nil hints:nil];
   return [[[NSBitmapImageRep alloc] initWithCGImage:CGImage] autorelease];
}

或者为了避免 NSImage 的子类化,你可以这样写:

NSImage *img = [[[NSImage alloc] initWithContentsOfFile:filename] autorelease];
CGImageRef CGImage = [img CGImageForProposedRect:nil context:nil hints:nil];
NSBitmapImageRep *rep = [[[NSBitmapImageRep alloc] initWithCGImage:CGImage] autorelease];

这只会返回一个 NSBitmapImageRep,如果图像包含多个表示形式(例如,来自 TIFF 文件的大量 NSBitmapImageReps),这可能不够好。但是添加一些代码很简单。

【讨论】:

  • 作为附加建议,您可能希望在迭代中将第二个[self representations] 替换为ret,因为现在ret 未使用。
  • 请注意,如果图像是从头开始创建而不是从文件中加载的,则它可能没有任何图像表示形式。
  • 谢谢!没有演员表,第一部分对我不起作用:if( [rep isKindOfClass:[NSBitmapImageRep class]] ) return (NSBitmapImageRep *)rep;
【解决方案2】:

你可以试试这个改编自Mike Ash's "Obtaining and Interpreting Image Data" blog post的方法:

- (NSBitmapImageRep *)bitmapImageRepresentation {
  int width = [self size].width;
  int height = [self size].height;

  if(width < 1 || height < 1)
      return nil;

  NSBitmapImageRep *rep = [[NSBitmapImageRep alloc]
                           initWithBitmapDataPlanes: NULL
                           pixelsWide: width
                           pixelsHigh: height
                           bitsPerSample: 8
                           samplesPerPixel: 4
                           hasAlpha: YES
                           isPlanar: NO
                           colorSpaceName: NSDeviceRGBColorSpace
                           bytesPerRow: width * 4
                           bitsPerPixel: 32];

  NSGraphicsContext *ctx = [NSGraphicsContext graphicsContextWithBitmapImageRep: rep];
  [NSGraphicsContext saveGraphicsState];
  [NSGraphicsContext setCurrentContext: ctx];  
  [self drawAtPoint: NSZeroPoint fromRect: NSZeroRect operation: NSCompositeCopy fraction: 1.0];
  [ctx flushGraphics];
  [NSGraphicsContext restoreGraphicsState];

  return [rep autorelease];
}

【讨论】:

  • 这个方法有效但不正确。它返回具有较暗图像的表示。我将在我的问题中添加示例。
  • 如果使用NSDeviceRGBColorSpace而不是NSCalibratedRGBColorSpace,结果会更好吗?
  • 不客气。更新了我的答案以使用 NSDeviceRGBColorSpace。
【解决方案3】:

可能迟到了,但这是来自@mrwalker 回复的 Swift 3 版本:

extension NSImage {
    func bitmapImageRepresentation(colorSpaceName: String) -> NSBitmapImageRep? {
        let width = self.size.width
        let height = self.size.height

        if width < 1 || height < 1 {
            return nil
        }

        if let rep = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: Int(width), pixelsHigh: Int(height), bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: colorSpaceName, bytesPerRow: Int(width) * 4, bitsPerPixel: 32)
        {
            let ctx = NSGraphicsContext.init(bitmapImageRep: rep)
            NSGraphicsContext.saveGraphicsState()
            NSGraphicsContext.setCurrent(ctx)
            self.draw(at: NSZeroPoint, from: NSZeroRect, operation: NSCompositingOperation.copy, fraction: 1.0)
            ctx?.flushGraphics()
            NSGraphicsContext.restoreGraphicsState()
            return rep
        }
        return nil
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-22
    • 2011-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-11
    • 2014-08-27
    相关资源
    最近更新 更多