Как мне написать CoreGraphics CGImageRef из пользовательского потока данных?

Я надеюсь закончить тем, что Core Graphics CGImageRef рисуется на экране в переопределенном — (void) drawRect: (CGRect) — методе rect. Вот что у меня так далеко.

- (void)drawRect:(CGRect)rect
{
CGContextRef currentContext = UIGraphicsGetCurrentContext();

const CGFloat _x = CGRectGetMinX(rect);
const CGFloat _y = CGRectGetMinY(rect);
const CGFloat _w = CGRectGetWidth(rect);
const CGFloat _h = CGRectGetHeight(rect);

const CGFloat scale = [[UIScreen mainScreen] scale];

const int pixelsWide = (_w * scale);
const int pixelsHigh = (_h * scale);

const size_t colorValues = 4;
const int rawMemorySize = (pixelsWide * pixelsHigh * colorValues);

// raw pixel data memory
UInt8 pixelData[rawMemorySize];

// fill the raw pixel buffer with arbitrary gray color for test

for(size_t ui = 0; ui < rawMemorySize; ui++)
{
pixelData[ui] = 255; // black
}

CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
CFDataRef rgbData = CFDataCreate(NULL, pixelData, rawMemorySize);
CGDataProviderRef provider = CGDataProviderCreateWithCFData(rgbData);

CGImageRef rgbImageRef = CGImageCreate(pixelsWide, pixelsHigh, 8, (colorValues * colorValues), (pixelsWide * colorValues), colorspace, kCGBitmapByteOrderDefault, provider, NULL, true, kCGRenderingIntentDefault);

CFRelease(rgbData);
CGDataProviderRelease(provider);
CGColorSpaceRelease(colorspace);

//Draw the bitmapped image
CGContextDrawImage(currentContext, CGRectMake(_x, _y, pixelsWide, 64), rgbImageRef);
CGImageRelease(rgbImageRef);
}

Я получил ошибку EXC_BAD_ACCESS в цикле for.

Я думаю, что я близко, но я не совсем уверен. Есть идеи, что может быть виновником? Заранее спасибо 🙂

2

Решение

Еще пара вещей, о которых нужно знать.

rect Аргументом для drawRect является только грязный прямоугольник, который может быть меньше self.bounds, использование self.bounds если ваш код не оптимизирован для рисования только грязного прямоугольника.

Выделение pixelData массив в стеке — плохая идея и может быть причиной ошибки. Все зависит от того, насколько велик вид. Я подозреваю, что AaronGolden использовал меньшее представление, чем вы, и, следовательно, не было никаких проблем. Безопаснее calloc массив, и освободите его в конце после того, как вы закончите использовать его. Обратите внимание, что calloc очистит массив до прозрачного черного цвета.

Вот пример кода, который я использовал для подобных вещей в прошлом. Хотя массив пикселей создается с callocвы по-прежнему можете обращаться к нему как к стандартному массиву (например, pixels[100] = 0xff0000ff; совершенно верно). Однако из соображений производительности я обычно использую указатели для доступа к отдельным пикселям.

const CGFloat scale = [[UIScreen mainScreen] scale];
int width  = self.bounds.size.width  * scale;
int height = self.bounds.size.height * scale;

uint32_t *pixels = calloc( width * height, sizeof(uint32_t) );
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate( pixels, width, height, 8, width * sizeof(uint32_t), colorSpace, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast );

uint8_t *bufptr = (uint8_t *)pixels;
for ( int y = 0; y < height; y++)
{
for ( int x = 0; x < width; x++ )
{
bufptr[0] = redValue;
bufptr[1] = greenValue;
bufptr[2] = blueValue;
bufptr[3] = alphaValue;

bufptr += 4;
}
}

CGImageRef newCGImage = CGBitmapContextCreateImage( context );
CGContextRelease( context );
CGColorSpaceRelease( colorSpace );
free( pixels );
3

Другие решения

Вот пара предложений. Я попробовал ваш код и не получил неправильный доступ в цикле for, как вы описали, но:

Вам не нужен цикл for, чтобы установить все на 255 таким образом. Ты можешь использовать memset:

memset(pixelData, 255, sizeof(pixelData));

И в вашем вызове CGImageCreate аргумент сразу после вашего литерала 8 неверен. Это должно быть количество бит на пиксель, которое 8 * colorValues не colorValues * colorValues, Так что создайте свой образ как:

CGImageRef rgbImageRef = CGImageCreate(pixelsWide, pixelsHigh, 8, 8 * colorValues, pixelsWide * colorValues, colorspace, kCGBitmapByteOrderDefault, provider, NULL, true, kCGRenderingIntentDefault);

Наконец, я заметил, что вы добавили комментарий о том, что 255 соответствует черному, но это не так. Если вы сделаете все свои байты данных пикселей 255, ваше изображение должно быть заполнено белый. Вы рисуете ваше изображение с высотой 64 точки в вашем вызове CGContextDrawImage, и действительно я вижу белый бар 64 балла, когда я запускаю ваш код с этими исправлениями.

1