¿Cómo se crea un PNG de 8 bits con transparencia desde un NSBitmapImageRep?
-
24-09-2019 - |
Pregunta
Tengo un NSBitmapImageRep
de 32 bits que tiene un canal alfa con esencialmente los valores de 1 bit (los píxeles son encendido o apagado).
Quiero guardar este mapa de bits a un archivo PNG de 8 bits con transparencia. Si utilizo el método de -representationUsingType:properties:
NSBitmapImageRep
y paso en NSPNGFileType
, se crea un archivo PNG de 32 bits, lo cual no es lo que quiero.
Sé que PNG de 8 bits se pueden leer, se abren en la vista previa sin problemas, pero ¿es posible escribir este tipo de archivo PNG utilizando cualquier incorporado en Mac OS X API? Estoy feliz de bajar a Core imagen o incluso QuickTime si es necesario. Un examen superficial de los documentos CGImage
no reveló nada obvio.
EDIT:
He empezado una recompensa por esta pregunta, si alguien puede proporcionar el código fuente de trabajo que toma un NSBitmapImageRep
de 32 bits y escribe un archivo PNG de 256 colores con la transparencia de 1 bit, es suyo.
Solución
pngnq (y nueva pngquant que logra una mayor calidad) tiene licencia BSD, por lo que sólo puede incluirlo en su programa. No hay necesidad de desove como tarea independiente.
Otros consejos
¿Qué hay de pnglib
? Es muy ligero y fácil de usar.
Una gran referencia para trabajar con las API de nivel inferior es de programación con cuarzo
Parte del código a continuación se basa en ejemplos de ese libro.
Nota: Este es el código de un-probado destinado a ser un punto de partida solamente ....
- (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;
}
Creación Contexto Función:
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;
}
El uso sería:
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];
Una cosa a tratar sería la creación de un NSBitmapImageRep con 8 bits, a continuación, copiar los datos en él.
En realidad, esto sería mucho trabajo, ya que tendría que calcular la tabla de índice de color usted mismo.
CGImageDestination
es su hombre para la escritura de la imagen de bajo nivel, pero no sé si es compatible con esa habilidad específica.