Составление изображения с использованием Core Graphics и сохранение альфа-канала

У меня есть PNG (с альфа-каналом), который я хочу объединить в CGContextRef с помощью CGContextDrawImage. Я бы хотел, чтобы каналы RBG были объединены, но я также хотел бы, чтобы альфа-канал исходных изображений также копировался.

В конечном итоге я передам окончательный CGContextRef (в форме CGImageRef) в GLKit, где я надеюсь управлять альфа-каналом для целей тонирования цвета с помощью фрагментного шейдера.

К сожалению, я сталкиваюсь с проблемами, когда дело доходит до создания атласа текстур с использованием Core Graphics. Похоже, что окончательный CGImageRef не может скопировать альфа-канал из моего исходного изображения и непрозрачен. Я прикрепил свой текущий код компоновки и копию моего тестового изображения ниже:

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
UInt8 * m_PixelBuf = malloc(sizeof(UInt8) * atlasSize.height * atlasSize.width * 4);

NSUInteger bytesPerPixel = 4;
NSUInteger bytesPerRow = bytesPerPixel * atlasSize.width;
NSUInteger bitsPerComponent = 8;
CGContextRef context = CGBitmapContextCreate(m_PixelBuf,
                                            atlasSize.width,
                                            atlasSize.height,
                                            bitsPerComponent,
                                            bytesPerRow,
                                            colorSpace
                                            kCGImageAlphaPremultipliedFirst);

CGContextDrawImage(context, CGRectMake(x, y, image.size.width, image.size.height), image.CGImage);

CGImageRef imgRef = CGBitmapContextCreateImage(context);
CGColorSpaceRelease(colorSpace);
CGContextRelease(context);

введите описание изображения здесь


person ndg    schedule 11.06.2014    source источник


Ответы (1)


Где вы, люди, находите эти процедуры использования CGBitmapContextCreate, поскольку это одна из наиболее распространенных проблем: kCGImageAlphaPremultipliedFirst установит альфа-канал на 1 и ПРЕДВАРИТЕЛЬНО RGB с альфа-значением.

Если вы используете Xcode, пожалуйста, щелкните kCGImageAlphaPremultipliedFirst и найдите подходящую замену, например kCGImageAlphaLast.

Добавление примера использования альфа-канала в качестве последнего канала:

+ (UIImage *)generateRadialGradient {
    int size = 256;
    uint8_t *buffer = malloc(size*size*4);
    memset(buffer, 255, size*size*4);
    for(int i=0; i<size; i++) {
        for(int j=0; j<size; j++) {
            float x = ((float)i/(float)size)*2.0f - 1.0f;
            float y = ((float)j/(float)size)*2.0f - 1.0f;
            float relativeRadius = x*x + y*y;
            if(relativeRadius >= 0.0 && relativeRadius < 1.0) { buffer[(i*size + j)*4 + 3] = (uint8_t)((1.0-sqrt(relativeRadius))*255.0); }
            else buffer[(i*size + j)*4 + 3] = 0;
        }
    }

    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL,
                                                              buffer,
                                                              size*size*4,
                                                              NULL);

    int bitsPerComponent = 8;
    int bitsPerPixel = 32;
    int bytesPerRow = 4*size;
    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
    CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Big|kCGImageAlphaLast;
    CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
    CGImageRef imageRef = CGImageCreate(size,
                                        size,
                                        bitsPerComponent,
                                        bitsPerPixel,
                                        bytesPerRow,
                                        colorSpaceRef,
                                        bitmapInfo,
                                        provider,
                                        NULL,
                                        NO,
                                        renderingIntent);
    /*I get the current dimensions displayed here */
    return [UIImage imageWithCGImage:imageRef];
}

Таким образом, этот код создает радиальный градиент из кода. Внутренняя часть полностью непрозрачна, но становится прозрачной при удалении от центра.

Мы также можем использовать kCGImageAlphaFirst, что приводит к желтоватому градиенту. Альфа всегда равна 1, и только первый (красный) канал уменьшается. В результате получается белый цвет посередине, а по мере уменьшения красного канала начинает отображаться желтый цвет.

person Matic Oblak    schedule 12.06.2014
comment
Согласно Apple Документация по основной графике kCGImageAlphaLast в настоящее время не поддерживается в iOS, оставляя только kCGImageAlphaPremultipliedFirst или kCGImageAlphaPremultipliedLast в качестве альтернативы. - person ndg; 12.06.2014
comment
Я понятия не имею, где вы это взяли, и не мог найти это в документации ... В любом случае, я использую это довольно долгое время и до сих пор использую его ежедневно. Поэтому я думаю, если вы уверены, что это не сработает для вас, вам придется найти альтернативный способ получить необработанные данные изображения RGBA. - person Matic Oblak; 12.06.2014
comment
Возможно, даже значения по умолчанию - RGBA, поэтому, возможно, удаление этого аргумента даст вам ожидаемый результат ... - person Matic Oblak; 12.06.2014
comment
Вы можете увидеть поддерживаемые форматы пикселей в разделе Поддерживаемые форматы пикселей на странице ссылку, которую я предоставил. - person ndg; 12.06.2014
comment
Что ж, похоже, кто-то забыл обновить список поддерживаемых форматов пикселей, что по-прежнему не делает элементы, отсутствующие в списке, неподдерживаемыми ... В любом случае, если вы нажмете на любой из них в списке, вы перейдете к их описанию и в ЭТОМ списке вы также можете найти: kCGImageAlphaLast Альфа-компонент хранится в младших битах каждого пикселя. Например, RGBA без предварительного умножения. Доступно в iOS 2.0 и новее. Заявлено в CGImage.h. Теперь я бы сказал, что это сделало его вполне поддерживаемым ... - person Matic Oblak; 12.06.2014
comment
kCGImageAlphaLast больше не доступен по состоянию на 2019 год: D Просто не могу найти альтернативный метод для получения данных RGBA без предварительного умножения. - person atereshkov; 21.01.2019
comment
@atereshkov Какой язык вы сейчас используете? В Swift теперь это CGImageAlphaInfo.first. Что касается ObjectiveC, вроде бы тоже kCGImageAlphaFirst. - person Matic Oblak; 21.01.2019
comment
@MaticOblak Я использую ObjC и kCGImageAlphaFirst и получаю ошибку неподдерживаемой комбинации параметров: CGBitmapContextCreateImage: недопустимый контекст 0x0. Это означает, что ошибка связана с kCGImageAlphaFirst. Я думаю, он больше не доступен, начиная с iOS 8 (только что получил его с stackoverflow.com/a/26365180/5969121). Спасибо за ответ! Рад видеть его спустя почти 5 лет. Просто хотел получить замену, чтобы заставить его работать. - person atereshkov; 21.01.2019
comment
@atereshkov Я думаю, у вас может быть другая проблема. Возможно, отсутствует порядок байтов. См. Пример, который я добавил в этот ответ. Он работает правильно на более поздних версиях iOS. - person Matic Oblak; 22.01.2019
comment
@MaticOblak хорошо, спасибо, парень, мне очень помог. Еще одна вещь, которую следует учитывать: допустим, у меня есть изображение RGBA в формате UIImage, как я могу получить из него необработанные данные? (как и ваш буферный массив, я просто хочу изменить какое-то значение пикселей). Я пытался получить его с помощью поставщика CGImageData и CFDataGetBytePrt, но он по-прежнему возвращает мне значения RGB вместо RGBA. UInt8 * buffer = (UInt8 *) CFDataGetBytePtr (rawData); - person atereshkov; 22.01.2019
comment
@atereshkov Это не такая тривиальная задача, как может показаться. UIImage поддерживает множество форматов, поэтому, если вы хотите иметь определенные данные RGBA, вам необходимо перерисовать их в новом контексте с использованием вашего собственного поставщика данных. Попробуйте этот ответ здесь, stackoverflow.com/a/14363385/526828. Обратите внимание, что сначала выделяются данные, размер которых достаточно велик, чтобы уместить все пиксели. Затем создается контекст с указателем на эти данные вместо nil. Вы можете делать что угодно в этом контексте (рисовать его даже с помощью путей), и в конце вы получите данные в данном буфере. (Однако вам нужно будет изменить некоторые настройки). - person Matic Oblak; 22.01.2019
comment
@MaticOblak, это случай проблемы из-за предварительного умножения альфа-канала. Но в любом случае думаю я на правильном пути, спасибо за помощь, буду молоть дальше. - person atereshkov; 22.01.2019
comment
@atereshkov Я понимаю вашу точку зрения. Мне пришлось попробовать, и я, честно говоря, не вижу способа создать контекст, который обычно поддерживает альфа-канал. Из всех других потоков кажется, что это просто ошибка iOS, и по какой-то странной причине она с тех пор не исправлена. В любом случае можно получить доступ к необработанным данным пикселей. Через CFDataGetBytePtr(CGDataProviderCopyData(CGImageGetDataProvider(image))). Обязательно проверьте, требуется ли здесь какое-либо управление памятью ... Что касается изменения размера и ориентации, я думаю, все же лучше использовать просмотр изображения и создать его снимок. - person Matic Oblak; 22.01.2019