2010-03-03 7 views
6

Ich habe eine 32-Bit NSBitmapImageRep, die einen Alpha-Kanal mit im Wesentlichen 1-Bit-Werte hat (die Pixel sind entweder an oder aus).Wie erstelle ich ein 8-Bit-PNG mit Transparenz von einem NSBitmapImageRep?

Ich möchte diese Bitmap in eine 8-Bit-PNG-Datei mit Transparenz speichern. Wenn ich die -representationUsingType:properties: Methode von NSBitmapImageRep benutze und NSPNGFileType übergebe, wird ein 32-Bit PNG erstellt, was ich nicht will.

Ich weiß, dass 8-Bit-PNGs gelesen werden können, öffnen sie in der Vorschau ohne Probleme, aber ist es möglich, diese Art von PNG-Datei mit allen integrierten Mac OS X-APIs zu schreiben? Ich freue mich wenn ich auf Core Image oder sogar QuickTime zurückgreifen kann. Eine oberflächliche Untersuchung der CGImage Dokumente ergab nichts Offensichtliches.

EDIT: Ich habe ein Kopfgeld auf diese Frage gestartet, wenn jemand arbeitet Quellcode bereitstellen kann, die eine 32-Bit nimmt NSBitmapImageRep und schreibt eine 256-Farben-PNG mit 1-Bit-Transparenz, gehört es Ihnen.

+0

8-Bit wie in 256-Farben? Ich hoffe, Sie haben nicht mehr als 256 Farben im Bild. Wenn Sie mehr als 256 Farben haben, können Sie stattdessen pngnq (bündeln es in Ihrer App und mit NSTask) verwenden: http://pgnq.sourceforge.net/ –

+0

Ja, 256 Farben. Ich suche etwas wie die Ausgabe mit '-representationUsingType: properties' mit' NSGIFFileType' außer mit einem 8-bit PNG als Ausgabe. pngnq ist eine Option (danke), aber ich hoffe, dass ich es ohne Laich-Aufgaben erledigen kann, wenn das überhaupt möglich ist. –

Antwort

1

pngnq (und new pngquant, die höhere Qualität erreicht) hat BSD-ähnliche Lizenz, so dass Sie es einfach in Sie aufnehmen können r Programm. Keine Notwendigkeit, als separate Aufgabe zu spawnen.

+0

Dies funktioniert tatsächlich, und behebt meine Problem, danke. –

0

Eine Sache zu versuchen wäre, ein NSBitmapImageRep mit 8 Bits zu erstellen und dann die Daten dorthin zu kopieren.

Dies wäre eigentlich eine Menge Arbeit, da Sie die Farbindextabelle selbst berechnen müssten.

+0

Korrigieren. Der Vorschlag von Peter Hosey, ** pngnq ** zu verwenden, löst dieses Problem der Palettenerstellung ziemlich gut, allerdings mit der Notwendigkeit, eine Aufgabe hervorzubringen. –

2

Wie wäre es mit pnglib? Es ist wirklich leicht und einfach zu bedienen.

+0

Dies ist definitiv eine Option, aber da ich nicht mit "pnglib" gearbeitet habe, bevor viel gelernt wurde, habe ich wirklich auf etwas Höheres gehofft. –

+0

@Rob, es gibt nicht viel lernen für PNGLib, es ist wirklich ziemlich einfach, soweit C-Bibliotheken gehen. Sie könnten für etwas anderes festgefahren sein, die meisten APIs auf höherer Ebene gehen von allgemeineren Fällen aus, was normalerweise 24 oder 32 bpp-Bilder bedeutet. –

0

CGImageDestination ist Ihr Mann für Low-Level-Bild schreiben, aber ich weiß nicht, ob es diese spezifische Fähigkeit unterstützt.

+0

Ja, es sieht so aus, als sollte es die Antwort sein, aber bis jetzt konnte ich keinen Weg finden, den Typ von PNG zu bestimmen, alles was ich versuche, schreibt ein 32-Bit PNG. –

1

Eine gute Referenz für mit niedrigeren Leveln-APIs arbeitet, ist Programming With Quartz

unter einem Teil des Codes auf Beispiele aus diesem Buch basiert.

Hinweis: Dies ist ungeprüftes Code nur ein Ausgangspunkt sein soll ....

- (NSBitmapImageRep*)convertImageRep:(NSBitmapImageRep*)startingImage{ 

    CGImageRef anImage = [startingImage CGImage]; 

    CGContextRef bitmapContext; 
    CGRect ctxRect; 
    size_t bytesPerRow, width, height; 

    width = CGImageGetWidth(anImage); 
    height = CGImageGetHeight(anImage); 
    ctxRect = CGRectMake(0.0, 0.0, width, height); 
    bytesPerRow = (width * 4 + 63) & ~63; 
    bitmapData = calloc(bytesPerRow * height, 1); 
    bitmapContext = createRGBBitmapContext(width, height, TRUE); 
    CGContextDrawImage (bitmapContext, ctxRect, anImage); 

    //Now extract the image from the context 
    CGImageRef  bitmapImage = nil; 
    bitmapImage = CGBitmapContextCreateImage(bitmapContext); 
    if(!bitmapImage){ 
     fprintf(stderr, "Couldn't create the image!\n"); 
     return nil; 
    } 

    NSBitmapImageRep *newImage = [[NSBitmapImageRep alloc] initWithCGImage:bitmapImage]; 
    return newImage; 
} 

Kontextfunktion Creation:

NSBitmapImageRep *startingImage; // assumed to be previously set. 
NSBitmapImageRep *endingImageRep = [self convertImageRep:startingImage]; 
// Write out as data 
NSData *outputData = [endingImageRep representationUsingType:NSPNGFileType properties:nil]; 
// somePath is set elsewhere 
[outputData writeToFile:somePath atomically:YES]; 
:

CGContextRef createRGBBitmapContext(size_t width, size_t height, Boolean needsTransparentBitmap) 
{ 
    CGContextRef context; 
    size_t bytesPerRow; 
    unsigned char *rasterData; 

    //minimum bytes per row is 4 bytes per sample * number of samples 
    bytesPerRow = width*4; 
    //round up to nearest multiple of 16. 
    bytesPerRow = COMPUTE_BEST_BYTES_PER_ROW(bytesPerRow); 

    int bitsPerComponent = 2; // to get 256 colors (2xRGBA) 

    //use function 'calloc' so memory is initialized to 0. 
    rasterData = calloc(1, bytesPerRow * height); 
    if(rasterData == NULL){ 
     fprintf(stderr, "Couldn't allocate the needed amount of memory!\n"); 
     return NULL; 
    } 

    // uses the generic calibrated RGB color space. 
    context = CGBitmapContextCreate(rasterData, width, height, bitsPerComponent, bytesPerRow, 
            CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB), 
            (needsTransparentBitmap ? kCGImageAlphaPremultipliedFirst : 
            kCGImageAlphaNoneSkipFirst) 
            ); 
    if(context == NULL){ 
     free(rasterData); 
     fprintf(stderr, "Couldn't create the context!\n"); 
     return NULL; 
    } 

    //Either clear the rect or paint with opaque white, 
    if(needsTransparentBitmap){ 
     CGContextClearRect(context, CGRectMake(0, 0, width, height)); 
    }else{ 
     CGContextSaveGState(context); 
     CGContextSetFillColorWithColor(context, getRGBOpaqueWhiteColor()); 
     CGContextFillRect(context, CGRectMake(0, 0, width, height)); 
     CGContextRestoreGState(context); 
    } 
    return context; 
} 

Verwendung wäre

+0

Danke, dies wird gut funktionieren, um die Bitmap zu erstellen, aber es löst nicht das Problem des Schreibens einer 256-Farben-8-Bit-PNG-Datei. –

+0

Entschuldigung, ich habe diesen Schritt verlassen. Ich bearbeite meine Antwort so, dass sie die beiden erforderlichen Anrufe enthält. –

+0

Ich habe das untersucht, und gemäß den "Unterstützten Pixelformaten" im Quartz 2D-Programmierhandbuch ist es nicht möglich, einen 8-Bit-RGB-Kontext zu erstellen, daher kann dieser Code nicht funktionieren. Wenn Sie versuchen, es auszuführen, kann der Kontext aufgrund einer "ungültigen Parameterkombination" nicht erstellt werden. http://developer.apple.com/mac/library/DOCUMENTATION/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_context/dq_context.html#//apple_ref/doc/uid/TP30001066-CH203-BCIBHHBB –