Альфа-канал PNG в iOS остается неизменным при преобразовании UIImage в CGImage для OpenGL

У меня есть несколько изображений PNG с альфа-прозрачностью. Они отображаются правильно при использовании для общих вещей Quartz, таких как пользовательский интерфейс. Однако, когда я беру изображение и загружаю его в OpenGL, я обнаруживаю, что получаемые мной данные имеют твердую альфа 255. Я использую то, что кажется обычным преобразованием UIImage в CGImage и рендерингом в контексте. Я размещу соответствующие фрагменты кода ниже.

Я провел много поисков и, наконец, наткнулся на способ обойти это, и я хотел поделиться здесь, но он медленный, поскольку он должен перебирать каждый пиксель и копировать эту необработанную альфа-канал в альфа-канал. Я хотел бы знать способ обойти это. Я не переключал никакие параметры PNG в своей сборке (не уверен, где это установить), чтобы посмотреть, поможет ли это ..

Благодарность

Сначала я получаю UIImage из своего приложения, которое я добавил вот так:

UIImage *image = [UIImage imageNamed:imageName];

затем я конвертирую в CGImageRef и создаю обычный контекст, как только эта соответствующая строка:

CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height,
                                             bitsPerComponent, bytesPerRow,
                                             CGImageGetColorSpace(spriteImage),
                                                   kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);

Затем эти следующие две строки - это волшебство, чтобы получить меня к НАСТОЯЩИМ необработанным данным png ... Я, вероятно, должен проверить RGBA по сравнению с другими форматами, но я ленив ... После этого я рисую изображение, а затем возвращаюсь и исправляю альфа ... да Я знаю, что могу сделать это за один цикл и пропуская байты, это сейчас более читабельно ...

// THIS data actually HAS the alpha!!
CFDataRef data = CGDataProviderCopyData(CGImageGetDataProvider(spriteImage));
GLubyte *pixels = (GLubyte *)CFDataGetBytePtr(data);

// 3
CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), spriteImage);


// Sadly we now have to loop and fix the ALPHA directly from the raw PNG source
// data as otherwise we get wrong/missing alpha...
for (int y=0; y < height; ++y) {
    for (int x=0; x < width; ++x) {
        int byteIndex = (bytesPerRow * y) + (x * bytesPerPixel);

        spriteData[byteIndex + 3] = pixels[byteIndex +3 ];
    }
}

затем я выполняю обычные вызовы glTexture и т. д., и все работает нормально .. Если я пропущу цикл копирования выше, я получаю спрайты без альфы. Если я сделаю это выше, мои спрайты будут правильно рисоваться с прозрачностью.

Я знаю обо всем, что делает iOS до умножения, но это кажется сломанным ... По крайней мере, надеюсь, что вышесказанное может кому-то помочь, но должен быть способ получше ???

Мысли? Спасибо!


person mcomet    schedule 17.12.2013    source источник
comment
Если CGDataProviderCopyData () дает вам правильные необработанные данные, почему бы просто не использовать их для вашей текстуры, вместо того, чтобы перерисовывать ее с помощью CGContextDrawImage ()? Вы не должны нуждаться ни в последнем, ни в вашем цикле.   -  person Brad Larson    schedule 17.12.2013
comment
Хороший момент .... Я должен попробовать это. Я пытался использовать его другим способом, и он выдал ошибку, поскольку он, конечно, только для чтения, но также не позволил мне пройти ... Я забыл, что это было ... хорошая идея, хотя ... Я должен упомянуть этот код от Apple. Есть образец, в котором он есть, и правильный код для чтения файлов без предварительной альфа-версии. Но мой реальный вопрос остается, почему ЭТО правильно, а CGImage НЕ правильный ...   -  person mcomet    schedule 18.12.2013
comment
CGBitmapContextCreate () поддерживает только предварительно умноженную альфа (по крайней мере, на iOS), но CGDataProviderCopyData () извлекает необработанные данные пикселей из изображения, которые, как я считаю, обычно не умножаются предварительно.   -  person Brad Larson    schedule 18.12.2013
comment
Что ж, это сработало, мне пришлось изменить второй триплет формата в glTexImage2D на GL_BGRA, поскольку формат CopyData соответствует формату моих изображений. Так что я думаю, по крайней мере, это действительно быстро и чище, не требует контекста и т. Д.   -  person mcomet    schedule 18.12.2013
comment
О, как уже отмечалось, это из образца GLImageProcessing от Apple ... который имеет правильный код для перехода от RGB к BGR и т. Д ... Это доступно здесь: developer.apple.com/library/ios/samplecode/GLImageProcessing/   -  person mcomet    schedule 18.12.2013


Ответы (2)


Хорошо, я думаю, что отвечу на вопрос сам, основываясь на своих выводах и предположениях…

Во-первых, данные, предоставленные UIImage и CGImage, будут иметь предварительное умножение альфа. Если вы используете PNG, который оптимизируется, это так. Кажется, что в этот момент альфа бесполезна. Основываясь на образце кода GLImageProcessing, предоставленном непосредственно Apple, кажется, что правильный способ получить необработанный PNG-альфа - использовать метод CGImageGetDataProvider, как в их примере. Это конкретно касается изображения без какой-либо обработки. Одно замечание заключается в том, что, скорее всего, данные находятся в порядке BGR, а не в RGB, что имеет смысл, поскольку они делают это для оптимизации. В их образце кода есть преобразование, если это необходимо. В моем случае я делаю так:

CGImageRef spriteImage = image.CGImage;
if (!spriteImage) {
    NSLog(@"Failed to load image");
    exit(1);
}

CFDataRef data = CGDataProviderCopyData(CGImageGetDataProvider(CGImage));
pixels = (GLubyte *)CFDataGetBytePtr(data);

// … do other GL calls here like gl Gen and Bind…

// NOTE the reverse BGRA here for the 7th parameter only!:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)width, (int)height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels);

// Don't forget to release BOTH the CF data and the CGImage or you will get memory leaks!
CFRelease(data);        // Release the CF data

У меня это работает, я получаю правильную альфу, это быстро и правильно. Одно замечание. Мне не нужно масштабировать мое изображение ... если вам это небезразлично, тогда вы захотите сделать обычные вещи CGBitmapContextCreate и ContextDraw, как обычно, которые все равно будут работать с данными, предоставленными должным образом. Не забудьте отпустить ... Я забыл об этом, потому что у меня утечка памяти ... хех.

Надеюсь, это кому-то поможет ...

person mcomet    schedule 21.12.2013

Видно, что CGImageGetDataProvider (CGImage) выполняет альфа-предварительное умножение. У меня есть PNG с красным = 255 и альфа = 128, а у загруженного пикселя красный = 128. Что печально ....

person pkirill    schedule 01.09.2014