0

我正在尝试使用 CoreGraphics & CoreFoundation 的 C 接口将 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 接口: 将 32 位 RGBA 缓冲区保存到 .png 文件中(Cocoa OSX)

4

2 回答 2

0

回答我自己的问题:

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

  2. 尽管官方文档的这个页面列出了什么,但 UTCoreTypes.h 位于CoreServices.frameworkMacOSX 中,而不是MobileCoreServices.framework.

于 2013-08-10T15:22:16.603 回答
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
);

参数 选项- 保留供将来使用。通过NULL

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

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

于 2013-08-09T21:40:02.573 回答