Wrong CGImageGetBytesPerRow value for vImageBoxConvolve_ARGB8888 usage

1.3k views Asked by At

At start I need to do a fast real-time image blur. Found a useful tutorial Perform a blur using vImage.

Here is the code sample

-(UIImage *)boxblurImage:(UIImage *)image boxSize:(int)boxSize {
    CGImageRef img = image.CGImage;

    vImage_Buffer inBuffer, outBuffer;

    vImage_Error error;
    void *pixelBuffer;

    //create vImage_Buffer with data from CGImageRef

    CGDataProviderRef inProvider = CGImageGetDataProvider(img);
    CFDataRef inBitmapData = CGDataProviderCopyData(inProvider);

    inBuffer.width = CGImageGetWidth(img);
    inBuffer.height = CGImageGetHeight(img);
    inBuffer.rowBytes = CGImageGetBytesPerRow(img);

    inBuffer.data = (void*)CFDataGetBytePtr(inBitmapData);

    //create vImage_Buffer for output

    pixelBuffer = malloc(CGImageGetBytesPerRow(img) * CGImageGetHeight(img));

    if(pixelBuffer == NULL)
        NSLog(@"No pixelbuffer");

    outBuffer.data = pixelBuffer;
    outBuffer.width = CGImageGetWidth(img);
    outBuffer.height = CGImageGetHeight(img);
    outBuffer.rowBytes = CGImageGetBytesPerRow(img);

    //perform convolution
    error = vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, NULL, 0, 0, boxSize, boxSize, NULL, kvImageEdgeExtend);

    if (error) {
        NSLog(@"error from convolution %ld", error);
    }

    //create CGImageRef from vImage_Buffer output
    //1 - CGBitmapContextCreateImage -

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

    CGContextRef ctx = CGBitmapContextCreate(outBuffer.data,
                                         outBuffer.width,
                                         outBuffer.height,
                                         8,
                                         outBuffer.rowBytes,
                                         colorSpace,
                                         kCGImageAlphaNoneSkipLast);
    CGImageRef imageRef = CGBitmapContextCreateImage (ctx);

    UIImage *returnImage = [UIImage imageWithCGImage:imageRef];

    //clean up
    CGContextRelease(ctx);
    CGColorSpaceRelease(colorSpace);

    free(pixelBuffer);
    CFRelease(inBitmapData);

    CGImageRelease(imageRef);

    return returnImage;
}

The problem is that function throw an exception on some images. I found only one image "Broken image" (it is JPEG, not PNG).

There is the exception

<Error>: CGBitmapContextCreate: unsupported parameter combination: 8 integer bits/component; 32 bits/pixel; 3-component color space; kCGImageAlphaNoneSkipLast; 538 bytes/row.
<Error>: CGBitmapContextCreateImage: invalid context 0x0

I started to google it. Found couple of solutions. After some time spent debugging, i found that CGImageGetBytesPerRow return wrong value for "broken image". It fits to original image width, instead of exceed it four times as it should.

I tried to change rowBytes in CGBitmapContextCreate manually, but there are a lot of artifacts beginning to appear on image. The point is that bug is mysterious, its appear from times to times, but still.

How i can resolve it without of checking the rowBytes of the buffers? I can do some precheck of rowBytes value and return original image, but it looks like hack. And how happended so that some of the images, like our one, returns wrong rowBytes value?

1

There are 1 answers

0
Ian Ollmann On

Have you looked at vImageBuffer_InitWithCGImage / vImageCreateCGImageFromBuffer?

vImage_Buffer buf = {0};
vImage_CGImageFormat fmt = {
    .bitsPerComponent = 8,
    .bitsPerPixel = 32,
    .colorSpace = CGImageGetColorSpace(image),
    .bitmapInfo = kCGImageAlphaLast | kCGBitmapByteOrderDefault
};

vImage_Error err = vImageBuffer_InitWithCGImage( &buf, &fmt, NULL, image, kvImageNoFlags ); // allocates buf.data 

vImage_Buffer outBuffer;
vImageBuffer_Init( &outBuffer, buf.height, buf.width, fmt.bitsPerPixel, kvImageNoFlags);
vImageBoxConvolve_ARGB8888(&buf, &outBuffer, ...);
free(buf.data);

CGImageRef newImage = vImageCreateCGImageFromBuffer( &outBuffer, &fmt, NULL, NULL, kvImageNoAllocate, &err );  // outBuffer.data will be freed when the CGImageRef is released.