Я пишу несколько тестов для обнаружения изменений в форматах изображений без потерь (начиная с PNG) и обнаруживаю, что в Linux и Windows механизмы загрузки изображений работают, как и ожидалось, - но на iOS (не пробовал на macOS) изображение данные всегда меняются очень незначительно, если я загружаю их из файла PNG на диске или сохраняю в файл PNG на диске с помощью методов Apple.
Если я создаю PNG с использованием любого количества инструментов (GIMP / Paint.NET / что угодно) и использую свой кросс-платформенный код чтения PNG для проверки каждого пикселя полученных в результате загруженных данных - это точно соответствует тому, что я сделал в инструменте (или программно сгенерированный с помощью моего кроссплатформенного кода для записи PNG.) Последующая перезагрузка в инструменты создания приводит к точно таким же компонентам RGBA8888.
Если я загружаю PNG с диска, используя Apple:
NSString* pPathToFile = nsStringFromStdString( sPathToFile );
UIImage* pImageFromDiskPNG = [UIImage imageWithContentsOfFile:pPathToFile];
... затем изучите полученные пиксели, они похожи, но не совпадают. Я ожидаю, что, как и на других платформах, данные будут идентичны.
Теперь, что интересно, если я загружаю данные из PNG, используя мой код и создавая UIImage с ним (используя некоторый код, который я показываю ниже), я могу использовать этот UIImage и отобразить его, скопировать его, что угодно, и если я изучить данные пикселей - это именно то, что я дал для начала (вот почему я думаю, что это часть сохранения загрузки, где Apple модифицирует данные изображения.)
Когда я приказываю ему сохранить то, что я знаю, как хороший UIImage с идеальными данными пикселей, а затем загрузить это сохраненное Apple изображение с помощью кода загрузки PNG, я вижу, что это не совсем те же данные. Я использовал несколько методов, с помощью которых Apple предлагает сохранить UIImage в PNG (в первую очередь UIImagePNGRepresentation.)
Единственное, о чем я могу подумать, это то, что Apple при загрузке или сохранении на iOS не поддерживает RGBA8888 и выполняет какие-то предварительные операции с альфа-каналом - я размышляю об этом, потому что, когда я впервые начал использовать код Я написал ниже, я выбирал
kCGImageAlphaLast
... вместо того, что мне в конечном итоге пришлось использовать
kCGImageAlphaPremultipliedLast
потому что первое почему-то не поддерживается на iOS.
У кого-нибудь есть опыт решения этой проблемы на iOS?
Ура!
Код, который я использую для передачи / извлечения данных RGBA8888 в UIImages и из них, приведен ниже:
- (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;
}