Search code examples
c++objective-cstringuiimagensdata

UIImage with NSData initWithData returns nil


I have a new question here! As the real problem was not in the C++ conversion but rather that I need to convert the returned string data bytes into a CGImageRef. Anybody know how to do that please go to that link to answer the follow on to this question.

Thank you.


OK. Instead of muddying the question with protobuf stuff, I have simplified my test method to simulate the call that would be made to the protobuf stuff.

This test method does the following two parts. Part 1 takes a UIImage and converts it into a std::string.

  1. take a UIImage
  2. get the NSData from it
  3. convert the data to unsigned char *
  4. stuff the unsigned char * into a std::string

The string is what we would receive from the protobuf call. Part 2 takes the data from the string and converts it back into the NSData format to populate a UIImage. Following are the steps to do that:

  1. convert the std::string to char array
  2. convert the char array to a const char *
  3. put the char * into NSData
  4. return NSData
- (NSData *)testProcessedImage:(UIImage *)processedImage
{
    // UIImage to unsigned char *
    CGImageRef imageRef = processedImage.CGImage;
    NSData *data = (NSData *) CFBridgingRelease(CGDataProviderCopyData(CGImageGetDataProvider(imageRef)));
    unsigned char *pixels = (unsigned char *)[data bytes];
    unsigned long size = [data length];

    // ***************************************************************************
    // This is where we would call transmit and receive the bytes in a std::string
    // ***************************************************************************

    // unsigned char * to string
    std::string byteString(pixels, pixels + size);


    // string to char array to const char *
    char myArray[byteString.size()+1];//as 1 char space for null is also required
    strcpy(myArray, byteString.c_str());
    const char *bytes = (const char *)myArray;

    // put byte array back into NSData format
    NSUInteger usize = byteString.length();
    data = [NSData dataWithBytes:(const void *)bytes length:sizeof(unsigned char)*usize];
    NSLog(@"examine data");

    return data;
}

The is the code for when the data is returned:

    NSData *data = [self.messageCommand testProcessedImage:processedImage];

    // But when I try to alloc init a UIImage with the data, the image is nil
    UIImage *image = [[UIImage alloc] initWithData:data];
    NSLog(@"examine image");

Everything seems to go as planned until I try to create the UIImage with the data. Alloc initing the UIImage with that data returns nil. There must be some type of conversion that will make this work.


Solution

  • OK, so the problem is most likely with the repeated conversions between Objective-C, C and C++ data structures. Overall, you need to make sure to initialize the string as a byte array rather than a textual C string, and you want to get back the raw bytes without a null terminator. I think this will preserve the data correctly:

    - (void)onDataReceived:(NSNotification *)note {
        if ([note.name isEqualToString:@"DataReceived"]) {
            NSDictionary *userData = note.userInfo;
            NSData *imageData = [userData objectForKey:@"ImageData"];
     // Note the two-argument string constructor -- this is necessary for non-textual data!
            std::string byteString = std::string(static_cast<const char*>([imageData bytes]), imageData.length);
    
    
     // We get the image back as a std::string
            std::string imageStr = [self.message parseMessage:byteString ofSize:byteString.size()];
            NSLog(@"examine imageStr");
    
     // We get the data from the std::string
            char *imageCStr = new char[imageStr.size()];
            imageStr.copy(imageCStr, imageStr.size());
            NSData *data = [NSData dataWithBytes:imageCStr length:imageStr.size()];
            delete[] imageCStr;
    
     // But when I try to alloc init a UIImage with the data, the image is nil
            UIImage *image = [[UIImage alloc] initWithData:data];
            NSLog(@"examine image");
        }
    }