且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

将 NSData 转换为 CGImage 然后再转换回 NSData 会使文件太大

更新时间:2023-02-17 20:43:16

我在做一些图像处理时遇到了你关于 SO 的问题.似乎没有其他人想出答案,所以这是我的理论.

I was doing some image manipulation and came across your question on SO. Seems like no one else came up with an answer, so here's my theory.

虽然理论上可以按照您描述的方式将 CGImageRef 转换回 NSData,但数据本身是无效的,而不是真正的 JPEGPNG,因为您发现它不可读.所以我不认为 NSData.length 是正确的.您实际上必须跳过许多步骤才能重新创建 CGImageRefNSData 表示:

While it's theoretically possible to convert a CGImageRef back to NSData in the manner that you described, the data itself is invalid and not a real JPEG or PNG, as you discovered by it not being readable. So I don't think that the NSData.length is correct. You have to actually jump through a number of steps to recreate an NSData representation of a CGImageRef:

// incoming image data
NSData *image;

// create the image ref
CGDataProviderRef imgDataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef) image);
CGImageRef imageRef = CGImageCreateWithJPEGDataProvider(imgDataProvider, NULL, true, kCGRenderingIntentDefault);

// image metadata properties (EXIF, GPS, TIFF, etc)
NSDictionary *properties;

// create the new output data
CFMutableDataRef newImageData = CFDataCreateMutable(NULL, 0);
// my code assumes JPEG type since the input is from the iOS device camera
CFStringRef type = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (__bridge CFStringRef) @"image/jpg", kUTTypeImage);
// create the destination
CGImageDestinationRef destination = CGImageDestinationCreateWithData(newImageData, type, 1, NULL);
// add the image to the destination
CGImageDestinationAddImage(destination, imageRef, (__bridge CFDictionaryRef) properties);
// finalize the write
CGImageDestinationFinalize(destination);

// memory cleanup
CGDataProviderRelease(imgDataProvider);
CGImageRelease(imageRef);
CFRelease(type);
CFRelease(destination);

NSData *newImage = (__bridge_transfer NSData *)newImageData;

通过这些步骤,newImage.length 应该与 image.length 相同.我没有测试,因为我实际上在输入和输出之间进行了裁剪,但是根据裁剪,大小大致是我的预期(输出大约是输入像素的一半,因此输出长度大约是大小的一半输入长度).

With these steps, the newImage.length should be the same as image.length. I haven't tested since I actually do cropping between the input and the output, but based on the crop, the size is roughly what I expected (the output is roughly half the pixels of the input and thus the output length roughly half the size of the input length).