Updating Programatically Created Bitmaps for iPhone 4

Discussion in 'iOS Programming' started by robbieduncan, Jun 26, 2010.

  1. robbieduncan Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #1
    In my apps I draw some custom graphics in-app. These images are drawn into CGContexts then retrieved as UIImages from the context.

    Before iPhone 4 life was simple. I simply did something like this:
    Code:
    UIGraphicsBeginImageContext(size);
    CGContextRef c = UIGraphicsGetCurrentContext();
    // Do some drawing
    UIImage *toReturn = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return toReturn
    
    The problem with this is that it draws at equivalent display resolution, rather than the hardware resolution of the device. So on the iPhone 4 it all looks a bit blocky/pixelated.

    The solution is relatively simply. Change the first line to
    Code:
    UIGraphicsBeginImageContextWithOptions(size,NO,0.0); 
    
    This works beautifully. My images are now rendered at the correct resolution and everything looks great. The problem is that UIGraphicsBeginImageContextWithOptions was only added in iOS 4.0. I have set my iPhone OS Deployment Target build variable to 3.0 and I don't get any errors/warnings about using this function but that doesn't seem to cover it: on an iOS 3.x device this function will not exist.

    So the question is do I need have some sort of conditional to handle this? Week link the references to the function so as the code loads on 3.x without linker errors? Any insight appreciated :)
     
  2. robbieduncan thread starter Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #2
    To reply to my own question! Apple's documentation tells me that the SDK settings will cause the additional symbols to be weak linked. So changing the line as I described above would still allow the code to load, but it would not work correctly on an iOS <4.0 device.

    Changing the code to this should make it work on either:

    Code:
    if (UIGraphicsBeginImageContextWithOptions!=NULL)
    {
    	UIGraphicsBeginImageContextWithOptions(size,NO,0.0); 
    }
    else 
    {
    	UIGraphicsBeginImageContext(size);
    }
    
     
  3. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #3
    I have similar code with the same solution.

    Curiously I also have code that uses UIKeyboardFrameEndUserInfoKey, which is a string. I need to check for it like this

    Code:
    BOOL     hasKeyboardFrameEndUserInfoKey = NULL != &UIKeyboardFrameEndUserInfoKey;
    
    which is slightly different. Without the & this crashes on an OS without that symbol.
     
  4. robbieduncan thread starter Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #4
    The difference may be down to one being a constant/variable and the other being a function.
     
  5. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #5
    The apple page you linked says

    A symbol has an address and a value. For a function pointer it really only has an address. Its value is its address. For a variable it does really have both an address and a value. But if the address is NULL then asking for the value is a null dereference.

    The C-language syntax for function pointers is the difference here.

    Code:
    if (MyWeakLinkedFunction != NULL)
    is the same as

    Code:
    if (&MyWeakLinkedFunction != NULL)
    But

    Code:
    if (MyWeakLinkedVariable != NULL)  // checks value
    is not the same as

    Code:
    if (&MyWeakLinkedVariable != NULL) // checks address
    The apple docs should explain this but don't.
     
  6. robbieduncan thread starter Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
  7. DanSelig macrumors newbie

    Joined:
    Jun 26, 2010
    #7
    // Do some drawing

    Will you please help me with the //do some drawing part? Before iPhone 4, I drew like this:

    Code:
    - (void) displaySubview:(CGRect)rect
    {
    	CGImageAlphaInfo alphaInfo = kCGImageAlphaNoneSkipLast;
    	CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
    	size_t components = 4;
    	size_t bitsPerComponent = 8;
    	size_t dotsPerRow = rect.size.width;
    	size_t numRows = rect.size.height;
    	size_t bytesPerRow = dotsPerRow * components;
    	
    	CGContextRef bimage 
    	= CGBitmapContextCreate(colorSet, dotsPerRow, numRows, bitsPerComponent, 
    							bytesPerRow, colorspace, alphaInfo);
    	CGColorSpaceRelease(colorspace);
    	
    	CGImageRef image = CGBitmapContextCreateImage(bimage);
    	CGContextRelease(bimage);
    	
    	UIImage *myImage = [UIImage imageWithCGImage:image];
    	UIImageView *uiv = [[UIImageView alloc] initWithImage:myImage];
    	CGImageRelease(image);
    	
    	[self addSubview:uiv];
    	[uiv release];
    }
    
    where colorSet is my RGBA set of bytes.
    With the new
    Code:
    UIGraphicsBeginImageContextWithOptions(CGSizeMake(pointsAcross, pointsDown), NO, 0.0);
    
    How do I get my colorSet into a CGImageRef or UIImage?
    Am I right to use points instead of pixels?
    And these points should reflect the screen resolution right?

    Regards,
    Dan
     
  8. robbieduncan thread starter Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #8
    Basically I don't know: I did not change my drawing code at all it's all Quartz/vectors.
     
  9. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #9
    Dan,

    I changed my code from using a bitmap context to use UIGraphicsBeginImageContextWithOptions/UIGraphicsBeginImageContext but all my drawing is UIString drawing or core graphics calls. I couldn't figure out how to set the scale on the bitmap context and it was simple for me to convert the existing code to use UIGraphicsBeginImageContext, so I went that way.

    In your case you have bitmap data that comes from "somewhere else" that you're drawing into an image context. How you should go depends on how you get that bitmap data. If it's at all possible to use UIGraphicsBeginImageContextWithOptions and draw into that context then you should do that. If not then maybe you should use the code that you've got but then draw the resulting image into a context created with UIGraphicsBeginImageContextWithOptions. You might need to double the size of your bitmap context to get the correct scale. I haven't tried this at all.
     
  10. DanSelig macrumors newbie

    Joined:
    Jun 26, 2010
    #10
    Thank you for your replies.
    My RGBA data is generated by the program. So it's getting the RGBA byte set into a context that is my sticking point.

    Dan
     
  11. robbieduncan thread starter Moderator emeritus

    robbieduncan

    Joined:
    Jul 24, 2002
    Location:
    London
    #11
    From the documentation:


    For bitmaps created in iPhone OS 3.2 and later, the drawing environment uses the premultiplied ARGB format to store the bitmap data. If the opaque parameter is YES, the bitmap’s alpha channel is ignored.
    For bitmaps created in iPhone OS 3.1.x and earlier, the drawing environment uses the premultiplied RGBA format to store the bitmap data.


    Sounds like that may effect you.
     
  12. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #12
    That's for the context created with UIGraphicsBeginImageContextWithOptions, which has its own defaults. I assume that doesn't apply to bitmaps created with CGBitmapCreate, where you specify the format.

    I think that Dan needs to just use the code he has on OS 4. Maybe check the scale value of UIView and make the bitmap twice as big if on a device with a scale factor of 2.0.

    Or affect you, as the case may be.
     
  13. DanSelig macrumors newbie

    Joined:
    Jun 26, 2010
    #13
    Yes. That's what I ended up doing. I found a way to make the program work with my current bitmap code.

    I'm working on my Mandelbrot set (Mbrot7) and I use the screenscale to calculate points (not pixels) and I color the points.
    I found that it works when I initWithFrame the UIScrollViews using the low res frame sizes. I also found that the tapPoint returned from locationInView comes in low res coordinates.

    These may not be the correct approaches. The WWDC session on high res (134) does not address these issues.

    Dan
     

Share This Page