objective-cmacosbitmapnsimagensbitmapimagerep

NSImage to NSBitmapImageRep


How to convert NSImage to NSBitmapImageRep? I have code:

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

It works, but it causes memory leaks. At least when I am using it with ARC. Using initWithData:[nsimagename TIFFRepresentation] it not working correctly. Some images`s representations are not good. I think it depends on format and colorspace of image. Is there any other ways to achieve that?


Result with mrwalker suggested solution:

Original image:
enter image description here

Converted to bitmapimagerep and back to image 1 time:
enter image description here

Converted to bitmapimagerep and back to image 3 times:
enter image description here

As you see image gets darker every time after converting to NSBitmapImageRep


Solution

  • You could try this method adapted from 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];
    }