我正在编写一些测试以检测无损图像格式的更改(从PNG开始),并发现在Linux和Windows上,图像加载机制按预期工作-但在iOS(未在macOS上尝试过)上如果我从Apple的磁盘上的PNG文件加载或使用Apple的方法保存到磁盘上的PNG文件,则数据总是会稍作更改。
如果我使用任意数量的工具(GIMP / Paint.NET /其他)创建PNG,并且使用跨平台PNG读取代码来检查生成的加载数据的每个像素-它与我在工具中所做的完全匹配(或使用我的跨平台PNG编写代码以编程方式生成。)随后重新加载到创建工具中,将产生完全相同的RGBA8888组件。
如果我使用苹果的从磁盘加载PNG:
NSString* pPathToFile = nsStringFromStdString( sPathToFile );
UIImage* pImageFromDiskPNG = [UIImage imageWithContentsOfFile:pPathToFile];
...然后检查结果像素是否相似但不相同。我希望像在其他平台上一样,数据是相同的。
现在,有趣的是,如果我使用自己的代码从PNG加载数据,并使用该代码创建UIImage(使用下面显示的一些代码),则可以使用该UIImage进行显示,复制,复制任何内容,如果我检查像素数据-正是我首先提供的(这就是为什么我认为这是Apple修改图像数据的节省负载的部分。)
当我指示它保存我认为是具有完美像素数据的好的UIImage,然后用我的PNG加载代码加载该Apple保存的图像时,我可以看到它不是完全相同的数据。苹果使用了几种建议将UIImage保存到PNG(主要是UIImagePNGRepresentation)的方法。
我唯一能想到的是,Apple在iOS上加载或保存时并不真正支持RGBA8888,并且正在对alpha通道进行某种预乘-我对此进行了推测,因为当我第一次开始使用代码时我张贴在我选择的下方
kCGImageAlphaLast
...而不是我最终不得不使用的
kCGImageAlphaPremultipliedLast
因为某些原因,iOS不支持前者。
有人在iOS上有解决此问题的经验吗?
干杯!
我用于将RGBA8888数据推入/拉出UIImage的代码如下:
- (unsigned char *) convertUIImageToBitmapRGBA8:(UIImage*)image dataSize:(NSUInteger*)dataSize
{
CGImageRef imageRef = image.CGImage;
// Create a bitmap context to draw the uiimage into
CGContextRef context = [self newBitmapRGBA8ContextFromImage:imageRef];
if(!context) {
return NULL;
}
size_t width = CGImageGetWidth(imageRef);
size_t height = CGImageGetHeight(imageRef);
CGRect rect = CGRectMake(0, 0, width, height);
// Draw image into the context to get the raw image data
CGContextDrawImage(context, rect, imageRef);
// Get a pointer to the data
unsigned char *bitmapData = (unsigned char *)CGBitmapContextGetData(context);
// Copy the data and release the memory (return memory allocated with new)
size_t bytesPerRow = CGBitmapContextGetBytesPerRow(context);
size_t bufferLength = bytesPerRow * height;
unsigned char *newBitmap = NULL;
if(bitmapData) {
*dataSize = sizeof(unsigned char) * bytesPerRow * height;
newBitmap = (unsigned char *)malloc(sizeof(unsigned char) * bytesPerRow * height);
if(newBitmap) { // Copy the data
for(int i = 0; i < bufferLength; ++i) {
newBitmap[i] = bitmapData[i];
}
}
free(bitmapData);
} else {
NSLog(@"Error getting bitmap pixel data\n");
}
CGContextRelease(context);
return newBitmap;
}
- (CGContextRef) newBitmapRGBA8ContextFromImage:(CGImageRef) image
{
CGContextRef context = NULL;
CGColorSpaceRef colorSpace;
uint32_t *bitmapData;
size_t bitsPerPixel = 32;
size_t bitsPerComponent = 8;
size_t bytesPerPixel = bitsPerPixel / bitsPerComponent;
size_t width = CGImageGetWidth(image);
size_t height = CGImageGetHeight(image);
size_t bytesPerRow = width * bytesPerPixel;
size_t bufferLength = bytesPerRow * height;
colorSpace = CGColorSpaceCreateDeviceRGB();
if(!colorSpace) {
NSLog(@"Error allocating color space RGB\n");
return NULL;
}
// Allocate memory for image data
bitmapData = (uint32_t *)malloc(bufferLength);
if(!bitmapData) {
NSLog(@"Error allocating memory for bitmap\n");
CGColorSpaceRelease(colorSpace);
return NULL;
}
//Create bitmap context
context = CGBitmapContextCreate( bitmapData, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast|kCGBitmapByteOrder32Big );
if( !context )
{
free( bitmapData );
NSLog( @"Bitmap context not created" );
}
CGColorSpaceRelease( colorSpace );
return context;
}
- (UIImage*) convertBitmapRGBA8ToUIImage:(unsigned char*) pBuffer withWidth:(int) nWidth withHeight:(int) nHeight
{
// Create the bitmap context
const size_t nColorChannels = 4;
const size_t nBitsPerChannel = 8;
const size_t nBytesPerRow = ((nBitsPerChannel * nWidth) / 8) * nColorChannels;
CGColorSpaceRef oCGColorSpaceRef = CGColorSpaceCreateDeviceRGB();
CGContextRef oCGContextRef = CGBitmapContextCreate( pBuffer, nWidth, nHeight, nBitsPerChannel, nBytesPerRow , oCGColorSpaceRef, kCGImageAlphaPremultipliedLast|kCGBitmapByteOrder32Big );
// create the image:
CGImageRef toCGImage = CGBitmapContextCreateImage(oCGContextRef);
UIImage* pImage = [[UIImage alloc] initWithCGImage:toCGImage];
return pImage;
}
答案 0 :(得分:0)
根据您的源代码,您似乎正在使用从PNG源图像导入的BGRA(RGB + alpha通道)数据。将图像附加到iOS项目时,出于性能原因,Xcode将对每个图像进行预处理以将RGB和A通道数据预乘。因此,在将图像加载到iPhone设备上时,可以更改非不透明(非A = 255)像素的RGB值。修改了RGB编号,但是当通过iOS渲染到屏幕上时,实际的图像数据将相同。这就是“直接Alpha”与“预乘Alpha”。
答案 1 :(得分:0)
直接存储图像数据,请勿使用UIImage.pngData()将图像转换为数据,如果像素具有alpha通道,此方法将更改像素的rgb值。