CoreGraphics:将RGBA数据编码为PNG

时间:2013-08-09 21:00:34

标签: c image core-graphics core-foundation

我正在尝试使用CoreGraphics&的C接口。 CoreFoundation将32位RGBA数据缓冲区(作为void *)保存到PNG文件中。当我尝试完成CGImageDestinationRef的初始化时,会在控制台上显示以下错误消息:

libpng error: No IDATs written into file

据我所知,CGImageRef我添加到CGImageDestinationRef是有效的。

相关代码:

void saveImage(const char* szImage, void* data, size_t dataSize, size_t width, size_t height)
{
    CFStringRef name = CFStringCreateWithCString(NULL, szImage, kCFStringEncodingASCII);
    CFURLRef texture_url = CFURLCreateWithFileSystemPath(
                                                     NULL,
                                                     name,
                                                     kCFURLPOSIXPathStyle,
                                                     false);

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, data, dataSize, NULL);
    CGImageRef image = CGImageCreate(width, height, 8, 32, 32 * width, colorSpace,
                                 kCGImageAlphaLast | kCGBitmapByteOrderDefault, dataProvider,
                                 NULL, FALSE, kCGRenderingIntentDefault);

    // From Image I/O Programming Guide, "Working with Image Destinations"
    float compression = 1.0; // Lossless compression if available.
    int orientation = 4; // Origin is at bottom, left.
    CFStringRef myKeys[3];
    CFTypeRef   myValues[3];
    CFDictionaryRef myOptions = NULL;
    myKeys[0] = kCGImagePropertyOrientation;
    myValues[0] = CFNumberCreate(NULL, kCFNumberIntType, &orientation);
    myKeys[1] = kCGImagePropertyHasAlpha;
    myValues[1] = kCFBooleanTrue;
    myKeys[2] = kCGImageDestinationLossyCompressionQuality;
    myValues[2] = CFNumberCreate(NULL, kCFNumberFloatType, &compression);
    myOptions = CFDictionaryCreate( NULL, (const void **)myKeys, (const void **)myValues, 3,
                               &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);

    CFStringRef type = CFStringCreateWithCString(NULL, "public.png", kCFStringEncodingASCII);
    CGImageDestinationRef dest = CGImageDestinationCreateWithURL(texture_url, type, 1, myOptions);

    CGImageDestinationAddImage(dest, image, NULL);

    if (!CGImageDestinationFinalize(dest))
    {
        // ERROR!
    }

    CFRelease(image);
    CFRelease(colorSpace);
    CFRelease(dataProvider);
    CFRelease(dest);
    CFRelease(texture_url);
}

这篇文章很相似,只是我没有使用Objective C界面:Saving a 32 bit RGBA buffer into a .png file (Cocoa OSX)

2 个答案:

答案 0 :(得分:0)

您的代码存在许多问题。

这里重写了我将如何做到这一点:

void saveImage(const char* szImage, void* data, size_t dataSize, size_t width, size_t height)
{
    CFStringRef name = CFStringCreateWithCString(NULL, szImage, kCFStringEncodingUTF8);
    CFURLRef texture_url = CFURLCreateWithFileSystemPath(
                                                     NULL,
                                                     name,
                                                     kCFURLPOSIXPathStyle,
                                                     false);

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, data,
                                                                dataSize, NULL);
    CGImageRef image = CGImageCreate(width, height, 8, 32, 32 * width, colorSpace,
                                 kCGImageAlphaLast | kCGBitmapByteOrderDefault,
                          dataProvider, NULL, FALSE, kCGRenderingIntentDefault);

    // From Image I/O Programming Guide, "Working with Image Destinations"
    float compression = 1.0; // Lossless compression if available.
    int orientation = 4; // Origin is at bottom, left.
    CFStringRef myKeys[3];
    CFTypeRef   myValues[3];
    CFDictionaryRef myOptions = NULL;
    myKeys[0] = kCGImagePropertyOrientation;
    myValues[0] = CFNumberCreate(NULL, kCFNumberIntType, &orientation);
    myKeys[1] = kCGImagePropertyHasAlpha;
    myValues[1] = kCFBooleanTrue;
    myKeys[2] = kCGImageDestinationLossyCompressionQuality;
    myValues[2] = CFNumberCreate(NULL, kCFNumberFloatType, &compression);
    myOptions = CFDictionaryCreate(NULL, (const void **)myKeys,
                    (const void **)myValues, 3, &kCFTypeDictionaryKeyCallBacks,
                                          &kCFTypeDictionaryValueCallBacks);

    CGImageDestinationRef dest = 
          CGImageDestinationCreateWithURL(texture_url, kUTTypePNG, 1, NULL);

    CGImageDestinationAddImage(dest, image, NULL);
    CGImageDestinationSetProperties(dest, myOptions);

    if (!CGImageDestinationFinalize(dest))
    {
        // ERROR!
    }

}

首先,在处理文件系统路径时不要使用ASCII,请使用UTF8。其次,您构建了一个用于设置图像属性的字典,但是您使用了错误的函数。 CGImageDestinationCreateWithURL()的文档说明如下:

  

<强> CGImageDestinationCreateWithURL

     

创建一个写入目标位置的图像目标   URL。

CGImageDestinationRef CGImageDestinationCreateWithURL (
   CFURLRef url,
   CFStringRef type,
   size_t count,
   CFDictionaryRef options
);
     

<强>参数    options - 保留供将来使用。通过NULL

当您应该通过NULL时,您试图传递属性字典。 (另外,您可以简单地使用kUTTypePNG统一类型标识符字符串常量而不是重新创建它)。首先调用CGImageDestinationCreateWithURL(),然后调用CGImageDestinationAddImage()添加图片,然后调用CGImageDestinationSetProperties()并传入您创建的属性字典。

[更新]:如果在完成这些更改后仍然遇到libpng error: No IDATs written into file个问题,请尝试以下操作:首先,确保dataProvider不是NULL - 换句话说,确保CGDataProviderCreateWithData()功能成功。其次,如果dataProvider有效,可以尝试将选项从kCGImageAlphaLast | kCGBitmapByteOrderDefault更改为简单kCGImageAlphaPremultipliedLast,看看是否成功。

答案 1 :(得分:0)

回答我自己的问题:

  1. 除了NSGod指出的问题之外,IDAT问题是CGImageCreate()的无效参数:参数5是bytesPerRow,而不是bitsPerRow。所以32 * width是不正确的; 4 * width是正确的。

  2. 尽管有this page官方文档列表,但UTCoreTypes.h位于MacOSX的CoreServices.framework,而不是MobileCoreServices.framework