Как я создаю 8-разрядный PNG с прозрачностью от NSBitmapImageRep?

У меня есть 32-разрядное NSBitmapImageRep который имеет альфа-канал с чрезвычайно 1-разрядными значениями (пиксели или включены или выключены).

Я хочу сохранить этот битовый массив в 8-разрядный файл PNG с прозрачностью. Если я использую -representationUsingType:properties: метод NSBitmapImageRep и передача в NSPNGFileType, 32-разрядный PNG создается, который не является тем, что я хочу.

Я знаю, что 8-разрядный PNGs может быть считан, они открываются в Предварительном просмотре без проблем, но действительно ли возможно записать этот тип файла PNG с помощью каких-либо встроенных API Mac OS X? Я счастлив к выпадающему к Базовому Изображению или даже QuickTime при необходимости. Поверхностное исследование CGImage документы не показали ничего очевидного.

Править: Я запустил щедрость по этому вопросу, если кто-то может обеспечить рабочий исходный код, который берет 32-разрядное NSBitmapImageRep и пишет PNG с 256 цветами с 1-разрядной прозрачностью, это Ваше.

6
задан Rob Keniger 8 March 2010 в 01:40
поделиться

5 ответов

pngnq (и новый pngquant , который обеспечивает более высокое качество) имеет лицензию в стиле BSD, поэтому вы можете просто включить его в свою программу. Не нужно создавать отдельную задачу.

1
ответ дан 17 December 2019 в 07:03
поделиться

Отличным пособием по работе с API более низкого уровня является Programming With Quartz

Часть приведенного ниже кода основана на примерах из этой книги.

Примечание: Это непроверенный код, предназначенный только для отправной точки.

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

Функция создания контекста:

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

Использование:

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];
1
ответ дан 17 December 2019 в 07:03
поделиться

Один нужно попробовать создать NSBitmapImageRep с 8 битами, а затем скопировать в него данные.

На самом деле это потребует много работы, так как вам придется вычислять таблицу цветовых индексов самостоятельно.

0
ответ дан 17 December 2019 в 07:03
поделиться

CGImageDestination ваш человек для низкоуровневого написания изображений, но я не знаю, поддерживает ли он эту особую способность.

0
ответ дан 17 December 2019 в 07:03
поделиться

Как насчет pnglib ? Он действительно легкий и простой в использовании.

2
ответ дан 17 December 2019 в 07:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: