NSData byte Access

Discussion in 'iOS Programming' started by xArtx, Aug 12, 2013.

  1. xArtx macrumors 6502a

    Joined:
    Mar 30, 2012
    #1
    Hi Guys,
    Seemingly the NSData object is kind of tight with giving up the data.
    Is there are way to index a single byte as you would in a C array
    without check in range, or some other slowy downy sounding thing?

    Currently, I'm modyfying pixel data in a C array, copying to NSData,
    and then slapping a BMP file header on that for a UIImage.

    It seems like it should be easier and quicker to access bytes in the
    NSData array, and not have to copy the C array to the NSData array
    each time the array is modified.

    No need for code, what I'm doing works,
    but the NSData class reference looks a little sad when going to access
    the bytes in a for loop.

    (Try to get useless demo in App Store)

    Cheers, Art.
     
  2. dantastic macrumors 6502

    dantastic

    Joined:
    Jan 21, 2011
    #2
    You can access the bytes property of the NSData object direct.
     
  3. xArtx thread starter macrumors 6502a

    Joined:
    Mar 30, 2012
    #3
    Do you have an example of just getting one byte as you would in a for loop in C?

    Code:
    
    unsigned char data[256];
    
    for(int i=0; i < 256; ++i) { // limit array values to 255
    if (data[i] > 255) {data[i] = 255;}
    } // i
    
    
     
  4. dantastic macrumors 6502

    dantastic

    Joined:
    Jan 21, 2011
    #4
    Code:
    NSMutableString *result = [NSMutableString string];
    const char *bytes = [myData bytes];
    for (int i = 0; i < [myData length]; i++)
    {
        [result appendFormat:@"%02hhx", (unsigned char)bytes[i]];
    }
    
    **robbed from stackoverflow ;)
     
  5. xArtx thread starter macrumors 6502a

    Joined:
    Mar 30, 2012
    #5
    Thanks, I'll try it.
    I still don't know if it's going to be faster than one full copy of the C array to the NSData array, but will find out.
    It might also be possible to assign the NSData object to the C array if it was created with malloc (without having to copy it) That would be good if you could still sneak in behind it and address the C array it was pointing to.
     
  6. robbieduncan Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #6
    The above is accessing the C array.
     
  7. dantastic macrumors 6502

    dantastic

    Joined:
    Jan 21, 2011
    #7
    Yea, so the above is accessing the c array,
    Code:
    const char *bytes = [myData bytes];
    here you are getting a pointer to the c array containing the actual data.

    This is direct access to the data and does not include the overhead of making a copy of the same.
     
  8. xArtx thread starter macrumors 6502a

    Joined:
    Mar 30, 2012
    #8
    Bah! :D
    So I get to save a copy, thanks :)
     
  9. xArtx thread starter macrumors 6502a

    Joined:
    Mar 30, 2012
    #9
    Does this actually copy the C array at all, or just create the pointer to it?

    Code:
    unsigned char rawdata[10000]; // explicitly declared C array
    int datalength = 10000; // default data length
    NSData* myData; // iOS data object
    
    myData = [NSData dataWithBytes:(const void *)rawdata length:sizeof(char)*datalength];
    
    I could probably find out by only doing it once,
    changing the C array, and seeing if the object contents have changed..
     
  10. Punicasoft macrumors member

    Joined:
    Jul 27, 2013
    #10
    Take a look at these nocopy version.

    Code:
        [NSData dataWithBytesNoCopy:<#(void *)#> length:<#(NSUInteger)#>]
        [NSData dataWithBytesNoCopy:<#(void *)#> length:<#(NSUInteger)#> freeWhenDone:<#(BOOL)#>]
     
  11. Duncan C macrumors 6502a

    Duncan C

    Joined:
    Jan 21, 2008
    Location:
    Northern Virginia
    #11

    The documentation is a wonderful thing:





     
  12. xArtx thread starter macrumors 6502a

    Joined:
    Mar 30, 2012
    #12
    You can already see I kind of read docs, or I wouldn't know what I posted above.

    There is some problem with it, but in a little time I can post entire source
    which is just a little plasma demo ported originally from Rockbox, iPodLinux,
    and then the Sony PSP.. probably been a lot of other platforms too.

    It appears if the NSData object is used for a UIImage, the call to create the
    UIImage object tries to free the data behind the NSData object.
    This is the version that works.

    Code:
        myData = [NSData dataWithBytes:(const void *)imgdata length:sizeof(char)*imgfilelength];
        myImage = [UIImage imageWithData: myData];
        [myImage drawInRect:CGRectMake(posx, posy, widx, widy)];
    
     
  13. Duncan C macrumors 6502a

    Duncan C

    Joined:
    Jan 21, 2008
    Location:
    Northern Virginia
    #13
    I'm confused as to what you're saying.

    Are you saying that imageWithData tries to free the data that backs the NSData object? But then you say that the code above works. Or are you saying that some other code seems to be freeing the NSData backing buffer but the code above works correct?

    Apple's docs for methods is usually quite clear about transfers of ownership. Unless it states explicitly that it takes ownership of the data, it won't.
     
  14. xArtx thread starter macrumors 6502a

    Joined:
    Mar 30, 2012
    #14
    The three line code does work, but it's the version that does the copy,
    so I assume it takes more time.
    I can avoid that by only ever accessing the NSData object instead of ever
    looking at the C array (suggested above), then I don't have to even use the
    first line of that three line code to send the data to an image object.
    So the actual practical problem for this app is already solved.

    It's when I use the no copy version that something is going on.
    It compiles and then crashes at run time on the second line.
    I din't create the C array with malloc, but I don't want it freed either.
    It's to be used for every frame, and then I'd lose the time allocating anyway.

    It will be clearer when I can post the source, it's really just a small program.
    It's intention is to help others, so it's important that the iOS code isn't sloppy.
    I plan to maintain the same demo on the Sony PSP, where you address
    absolute pixel values.
    The iOS one draws to a bitmap screen buffer, which is what I really needed
    to know when I started.
     
  15. Punicasoft macrumors member

    Joined:
    Jul 27, 2013
    #15
    I am guessing it is something to do with the stack.

    How do you declare imgdata and where?
     
  16. xArtx, Aug 15, 2013
    Last edited: Aug 15, 2013

    xArtx thread starter macrumors 6502a

    Joined:
    Mar 30, 2012
    #16

    The NSData and UIImage aren't used anywhere else so I can do it right there:
    Code:
        NSData* myData = [NSData dataWithBytesNoCopy:(void *)imgdata length:sizeof(char)*imgfilelength];
       //NSData* myData = [NSData dataWithBytes:(const void *)imgdata length:sizeof(char)*imgfilelength];
        UIImage *myImage = [UIImage imageWithData: myData];
    
    The error is "pointer being freed was not allocated" (at run time).

    So what is working is this:
    Code:
    UIImage *myImage = [UIImage imageWithData: [NSData dataWithBytes:(const void *)imgdata length:sizeof(char)*imgfilelength]];
    
     
  17. Punicasoft macrumors member

    Joined:
    Jul 27, 2013
    #17
    The version of dataWithBytesNoCopy seems to take ownership of the C array
    You can try this version instead: (with freeWhenDone set to NO)
    Code:
    [NSData dataWithBytesNoCopy:<#(void *)#> length:<#(NSUInteger)#> freeWhenDone:<#(BOOL)#>]
    
     
  18. xArtx thread starter macrumors 6502a

    Joined:
    Mar 30, 2012
    #18
    Looks like bingo! that's some fast plasma :)

    Code:
        myData = [NSData dataWithBytesNoCopy:(void *)imgdata length:sizeof(char)*imgfilelength freeWhenDone:NO];
        myImage = [UIImage imageWithData: myData];
        [myImage drawInRect:CGRectMake(posx+fsizea, posy, widx+fsizeb, widy)];
    
    So now I get to keep the C array right, since there's no overhead?
    The NSData and UIImage objects are not used at all until these three lines.
    I'm writing into a bitmap file that has a standard header,
    so it's ready to go for the UIImage object.

    If you are doing fire or plasma, you can reverse the order of lines to
    loose the overhead of the transformation on the bitmap to flip it both ways
    ...if there was any overhead.. sounds like it could be a gfx chip's job.










     
  19. xArtx, Aug 15, 2013
    Last edited by a moderator: Aug 15, 2013

    xArtx thread starter macrumors 6502a

    Joined:
    Mar 30, 2012
    #19
    Got the Fire demo working as well :)
    I got both of these for the Sony PSP back in 2006!

     
  20. Duncan C macrumors 6502a

    Duncan C

    Joined:
    Jan 21, 2008
    Location:
    Northern Virginia
    #20
    Cool. I hadn't seen the dataWithBytesNoCopy:freeWhenDone: variant before.

    That's exactly what the OP needs. It lets you pass in a memory buffer that was NOT created with malloc(), or that for some other reason you don't want NSData to take ownership of.
     
  21. xArtx thread starter macrumors 6502a

    Joined:
    Mar 30, 2012
    #21
    I still don't get out of the copy to the UIImage object if I understand correctly,
    as that object has it's own copy of image data behind it.
    Also, it is possible to email out a bitmap screenshot directly,
    ...bypassing the photo album.

    The idea is the app is open sourced as soon as it goes up,
    and so are both original Sony PSP demos which also do their work in C.
    The code for both platforms gets to stay largely identical now.

    The PSP 2D libs address absolute pixel values, their screen resolution never changed.
    I needed to know how to do this when I first started out.

    I would also appreciate critique on the iOS side.
    because much more important than helping others, is helping myself :)
    I would likely redo my fractal generator with this.
     
  22. Punicasoft macrumors member

    Joined:
    Jul 27, 2013
    #22
    There are possibly 2 copies for the original version.
    - One from the c byte array to the NSData
    - And another from the NSData to the UIImage (My guess is UIImage tries to decode the bitmap image, as a BMP header is stitched to it).

    Now the first copy is avoid using NoCopy version of NSData initializer.
    To avoid the second copy/decode in UIImage, may be you can take a look into CoreGraphics to create a bitmap context directly (avoiding the decode) and have it draw to a sub CALayer.

    eg:

    Code:
        CGBitmapContextCreate(<#void *data#>, <#size_t width#>, <#size_t height#>, <#size_t bitsPerComponent#>, <#size_t bytesPerRow#>, <#CGColorSpaceRef space#>, <#CGBitmapInfo bitmapInfo#>);
        CGBitmapContextCreateImage(<#CGContextRef context#>);
    
      and CALayer's delegate function :
       - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx;
    
    but it may or may not work, since the description for CGBitmapContextCreateImage says copy-on-write is on some cases only. :(

     
  23. xArtx thread starter macrumors 6502a

    Joined:
    Mar 30, 2012
    #23
    I have used the bitmap context before, and just suspected it's the same thing.
    Otherwise I don't see why the pixel data would be stored the same way
    as a bitmap file, upside down and with rows back to front.

    ie. whenever you do anything with it, iOS still needs to read it's dimensions
    from somewhere to align the rows and columns.


     
  24. Punicasoft macrumors member

    Joined:
    Jul 27, 2013
    #24
    Could it be the difference in coordinate system in UIKit and CoreGraphics?

    [​IMG]

    as in here http://developer.apple.com/library/...sDrawingOverview/GraphicsDrawingOverview.html

    some CTM has to be applied to the context so it display correctly.
     
  25. xArtx thread starter macrumors 6502a

    Joined:
    Mar 30, 2012
    #25
    I'll see what I can do.
    I should be able to still draw the image so that the orientation fixes it,
    and not have to do the transform.
     

Share This Page