3
votes

How does one create a new CGImageRef without a UIImage? I can't use image.CGImage

I am receiving a base64 encoded image as a std::string from a server process. The first part of the code below simulates receiving the encoded string.

- (UIImage *)testChangeImageToBase64String
{
    UIImage *processedImage = [UIImage imageNamed:@"myFile.jpg"];

    // UIImage to unsigned char *
    CGImageRef imageRef = processedImage.CGImage;
    NSData *data = (NSData *) CFBridgingRelease(CGDataProviderCopyData(CGImageGetDataProvider(imageRef)));

    // encode data to Base64 NSString
    NSString *base64EncodedDataString = [data base64EncodedStringWithOptions:0];
    // create encoded std::string
    std::string encoded([base64EncodedDataString UTF8String]);


    // ***************************************************************************
    // This is where we call the server method and receive the bytes in a std::string
    std::string received = encoded;
    // ***************************************************************************


    // get Base64 encoded std::string into NSString
    NSString *base64EncodedCstring = [NSString stringWithCString:encoded.c_str() encoding:[NSString defaultCStringEncoding]];

    // NSData from the Base64 encoded std::string
    NSData *nsdataFromBase64String = [[NSData alloc]initWithBase64EncodedString:base64EncodedCstring options:0];

Everything is good!!!!..... until I try to populate the newImage.


When I get the encoded string, I need to get a CGImageRef to get the data back into the correct format to populate a UIImage. If the data is not in the correct format the UIImage will be nil.

I need to create a new CGImageRef with the nsdataFromBase64String.

Something like:

    CGImageRef base64ImageRef = [newCGImageRefFromString:nsdataFromBase64String];

Then I can use imageWithCGImage to put the data into a new UIImage.

Something like:

    UIImage *imageFromImageRef = [UIImage imageWithCGImage: base64ImageRef];

Then I can return the UIImage.

    return newImage;
}

Please note that the following line will NOT work:

    UIImage *newImage = [[UIImage alloc] initWithData:nsdataFromBase64String];

The data needs to be in the correct format or the UIImage will be nil. Hence, my question, "How do I create a CGImageRef with NSData?"

1

1 Answers

3
votes

Short-ish answer, since this is mostly just going over what I mentioned in NSChat:

  1. Figure out what the format of the image you're receiving is as well as its size (width and height, in pixels). You mentioned in chat that it's just straight ARGB8 data, so keep that in mind. I'm not sure how you're receiving the other info, if at all.

  2. Using CGImageCreate, create a new image using what you know about the image already (i.e., presumably you know its width, height, and so on — if you don't, you should be packing this in with the image you're sending). E.g., this bundle of boilerplate that nobody likes to write:

    // NOTE: have not tested if this even compiles -- consider it pseudocode.
    
    CGImageRef image;
    CFDataRef bridgedData;
    CGDataProviderRef dataProvider;
    CGColorSpaceRef colorSpace;
    CGBitmapInfo infoFlags = kCGImageAlphaFirst; // ARGB
    
    // Get a color space
    colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
    // Assuming the decoded data is only pixel data
    bridgedData  = (__bridge CFDataRef)decodedData;
    dataProvider = CGDataProviderCreateWithCFData(bridgedData);
    
    // Given size_t width, height which you should already have somehow
    image = CGImageCreate(
        width, height, /* bpc */ 8, /* bpp */ 32, /* pitch */ width * 4,
        colorSpace, infoFlags,
        dataProvider, /* decode array */ NULL, /* interpolate? */ TRUE,
        kCGRenderingIntentDefault /* adjust intent according to use */
      );
    
    // Release things the image took ownership of.
    CGDataProviderRelease(dataProvider);
    CGColorSpaceRelease(colorSpace);
    

    That code's written with the idea that it's guaranteed to be ARGB_8888, the data is correct, nothing could possibly return NULL, etc. Copy/pasting the above code could potentially cause everything in a three mile radius to explode. Error handling's up to you (e.g., CGColorSpaceCreateWithName can potentially return null).

  3. Allocate a UIImage using the CGImage. Since the UIImage will take ownership of/copy the CGImage, release your CGImageRef (actually, the docs say nothing about what UIImage does with the CGImage, but you're not going to use it anymore, so you must release yours).