2012-10-15 10 views
12

Jak przekonwertować NSImage na NSBitmapImageRep? Mam kod:NSImage to 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; 
} 

Działa, ale powoduje wycieki pamięci. Przynajmniej kiedy używam go z ARC. Używanie initWithData:[nsimagename TIFFRepresentation] nie działa poprawnie. Niektóre obrazy nie są dobre. Myślę, że to zależy od formatu i przestrzeni kolorów obrazu. Czy istnieją inne sposoby, aby to osiągnąć?


Wynik z mrwalker Sugerowane rozwiązanie:

Obraz oryginalny:
enter image description here

Budynek do bitmapimagerep i powrót do obrazu 1 raz:
enter image description here

Budynek do bitm apimagerep i Tylna 3 razy:
enter image description here

Jak widać obraz staje się ciemniejszy każdym razem po konwersji do NSBitmapImageRep

+0

Hockeyman, czy nadal mają pełne rozwiązanie na swoje pytanie? Szukam sposobu identyfikacji pikseli kolorów bez zbytniego procesu. Może NSBitmapImageRep mógłby mi jakoś pomóc. Dzięki. – RickON

Odpowiedz

11

Można spróbować tej metody dostosowane od 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]; 
} 
+0

Ta metoda działa, ale nie jest poprawna. Zwraca reprezentację z ciemniejszym obrazem. Dodam przykład do mojego pytania. – hockeyman

+0

Czy wyniki są lepsze, jeśli użyjesz 'NSDeviceRGBColorSpace' zamiast' NSCalibratedRGBColorSpace'? – mrwalker

+0

Dziękuję bardzo! :) – hockeyman

11

@Julius: Twój kod jest zbyt skomplikowany i zawiera kilka błędów. Uczynię korekty tylko pierwszych linii:

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

To będzie wyodrębnić pierwszy NSBitmapImageRep jeśli jest członkiem reprezentacji lub zwróci nil, jeśli nie ma NSBitmapImageRep. dam ci inne rozwiązanie, które zawsze będzie działać niezależnie NSImageReps są w reprezentacji: NSBitmapImageRep, NSPDFImageRep lub NSCGImageSnapshotRep lub ...

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

lub w celu uniknięcia podklasy z NSImage można napisać:

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

Spowoduje to zwrócenie tylko pojedynczej wartości NSBitmapImageRep, która może nie być wystarczająco dobra, jeśli obraz zawiera więcej niż jedną reprezentację (np. Wiele plików NSBitmapImageRep z plików TIFF). Ale dodanie kodu jest proste.

+0

Jako dodatkową sugestię, możesz chcieć zastąpić drugą '[self reprezentations]' z 'ret' w iteracji, ponieważ teraz' ret' jest nieużywane. –

+0

@Brad, dobra sugestia! Dzięki. –

1

Może późno do partii, ale to jest Swift 3 wersja z odpowiedzi @mrwalker:

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 
    } 
} 
Powiązane problemy