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.
- take a UIImage
- get the NSData from it
- convert the data to unsigned char *
- 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:
- convert the std::string to char array
- convert the char array to a const char *
- put the char * into NSData
- 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.
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");
}
}
I tried the answer. There were some minor changes I needed to make to get rid of errors. Also, I had changed some variable names to minimize confusion. This is still returning nil for the UIImage.
- (void)onObjectReceived:(NSNotification *)note {
if ([note.name isEqualToString:@"ObjectReceived"]) {
NSDictionary *userData = note.userInfo;
NSData *objectData = [userData objectForKey:@"ObjectData"];
// Added this because bytes is used below. Or did you mean something else?
const char *bytes = (const char *)[objectData bytes];
// Note the two-argument string constructor -- this is necessary for non-textual data!
std::string byteString = std::string(static_cast<const char*>(bytes), objectData.length);
// This is an out parameter in the parseMessage method.
long unsigned int *msgSize = (long unsigned *)malloc(sizeof(long unsigned int));
// We get the image back as a std::string
std::string imageStr = [self.message parseMessage:byteString outMsgSize:msgSize];
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");
}
}
I tried removing everything in between and it worked. Here's the code:
- (NSData *)testProcessedImage:(UIImage *)processedImage
{
// UIImage to unsigned char *
CGImageRef imageRef = processedImage.CGImage;
NSData *data1 = (NSData *) CFBridgingRelease(CGDataProviderCopyData(CGImageGetDataProvider(imageRef)));
UIImage *image = [UIImage imageWithCGImage:imageRef];
This tells me that the NSData dataWithBytes is not going to work, because I am using a CGImageRef. My image is raw data coming from the camera (not a PNG or JPEG).
I found this answer on SO that was helpful. The asker even posted this comment, "It looks quite simple to wrap (the) data in a CFData object, and then CGDataProviderCreateWithCFData."
I found another answer on SO that was also helpful. It shows how to create a CFDataRef using the string.
There is a lot of helpful information out there but still not finding what I need. I'm going to ask another question and reference it back here when I get an answer.
Thank you.