Pergunta

I have a function where I convert a UIImage to a vImage (to be used in some vImage methods in iOS's Accelerate.framework.

The method I have is:

-(vImage_Buffer)convertImage:(UIImage *)image {
    CGImageRef sourceRef = [image CGImage];
    NSUInteger sourceWidth = CGImageGetWidth(sourceRef);
    NSUInteger sourceHeight = CGImageGetHeight(sourceRef);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    unsigned char *sourceData = (unsigned char*)calloc(sourceHeight * sourceWidth * 4, sizeof(unsigned char));
    NSUInteger bytesPerPixel = 4;
    NSUInteger sourceBytesPerRow = bytesPerPixel * sourceWidth;
    NSUInteger bitsPerComponent = 8;
    CGContextRef context = CGBitmapContextCreate(sourceData, sourceWidth, sourceHeight,
                                                       bitsPerComponent, sourceBytesPerRow, colorSpace,
                                                       kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Big);
    CGContextDrawImage(context, CGRectMake(0, 0, sourceWidth, sourceHeight), sourceRef);
    CGContextRelease(context);
    vImage_Buffer v_image = {
        .data = sourceData,
        .height = sourceHeight,
        .width = sourceWidth,
        .rowBytes = sourceBytesPerRow
    };

    return v_image;
}

I built this by mix and matching a few code snippets on the web.

My question is, I have a calloc call which allocates the space for sourceData. But how, and where would I free this memory?

Given that I may call this method many times, it seems like as-is, there would be a huge memory leak. I don't think I would release it here because it supplies the backing .data for my vImage_Buffer variable. But then would I release it in my calling function? Or would setting v_image to nil (or whatever the returned variable is named in the calling method) ultimately release the memory allocated by the calloc line?

Does anyone have any pointers?

Foi útil?

Solução

First of all You also need to release colorSpace by calling CGColorSpaceRelease(colorSpace)

Memory allocated by calloc can later be released in calling function using free(v_image.data) (or whatever the returned variable is named in the calling method).

You can also change Your method implementation to get rid of CGContext and get bytes from CGImage using CGDataProviderRef it would be something like:

-(vImage_Buffer)convertImage:(UIImage *)image
{
    CGImageRef sourceRef = [image CGImage];
    NSUInteger sourceWidth = CGImageGetWidth(sourceRef);
    NSUInteger sourceHeight = CGImageGetHeight(sourceRef);

    CGDataProviderRef provider = CGImageGetDataProvider(sourceRef);
    CFDataRef bitmapData = CGDataProviderCopyData(provider);

    unsigned char *sourceData = (unsigned char*)calloc(sourceHeight * sourceWidth * 4, sizeof(unsigned char));
    NSUInteger bytesPerPixel = 4;
    NSUInteger sourceBytesPerRow = bytesPerPixel * sourceWidth;

    CFDataGetBytes(bitmapData, CFRangeMake(0, CFDataGetLength(bitmapData)), sourceData);

    vImage_Buffer v_image = {
        .data = (void *)sourceData,
        .height = sourceHeight,
        .width = sourceWidth,
        .rowBytes = sourceBytesPerRow
    };

    CFRelease(bitmapData);

    return v_image;
}

However I didn't check the performance.

I'd also suggest to rename Your method to something like vImageCreateFromImage. You'll be aware that later You are responsible of cleaning up the memory.

Outras dicas

First, and most importantly, this was greatly simplified in iOS7.0 / OSX 10.9, with the introduction of the vImageBuffer_InitWithCGImage function (declared in vImage_Utilities.h, with fairly extensive documentation in the header). If you don't need to support older OSes, you should consider using this new function.

Second, whether you use your existing code or InitWithCGImage, once you are done using a vImage_Buffer object created by either means, you should free the memory associated with the data field: free(buffer.data);.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top