3

I need to retrieve the pixel data for a given (x, y) coordinate of a bitmap image. For years my code has been as follows:

_palettePixelData = CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage));
const UInt8 *rawData = CFDataGetBytePtr(_imagePixelData);

int pixelInfo = ((image.size.width * floorf(pointInImage.y) * image.scale) + floorf(pointInImage.x) * image.scale ) * 4;

CGFloat red   = rawData[pixelInfo + 0] / 255.0;
CGFloat green = rawData[pixelInfo + 1] / 255.0;
CGFloat blue  = rawData[pixelInfo + 2] / 255.0;
CGFloat alpha = rawData[pixelInfo + 3] / 255.0;

This works perfectly on my iPhone 4 and 5 (not the S) running iOS7.

I'm now trying to get it to work on an iPhone 6 running iOS8. To do so I need to make this change:

int pixelInfo = ((image.size.width * floorf(pointInImage.y) * image.scale * 2) + floorf(pointInImage.x) * image.scale ) * 4;

Note the extra * 2 in the code.

I'd like to understand why this change is necessary so that I can apply it correctly. It is linked to iOS8 or to the 64-bit architecture of the iPhone 6? If it's due to the 64-bit architecture then it doesn't make sense to be because:

  1. The pixel data itself is still encoded as 4 bytes.
  2. It seems that it's the width of each line of pixel data that's changed due to extra padding, but the position of the data on that line is the same. i.e. assuming 8 byte pixels does not work:

    int pixelInfo = ((image.size.width * floorf(pointInImage.y) * image.scale) + floorf(pointInImage.x) * image.scale ) * 8;

Can anyone shed some light on what's going on?

tarmes
  • 15,366
  • 10
  • 53
  • 87

1 Answers1

0

This same exact problem has been driving me up the wall all week...

I do my fonts from characters laid out in a grid in a PNG file. When loading, I analyse the pixels of each character to assess the exact width. This code has been working well for years, and has just started breaking on the iPhone 6 (and iPad Mini).

This is NOT due to iOS 8 because the code still works on an iPhone 5 with iOS 8.

It is also NOT due to any change to the compilation process as I can reproduce the problem, on simulators and devices, from the very same project.

This is the code that's worked for years:

void PixelMap::LoadPixelData (const char * imageFile)
{
    CFStringRef filename = CFStringCreateWithCString (NULL, imageFile, kCFStringEncodingASCII),
                fileExtension = CFStringCreateWithCString (NULL, "png", kCFStringEncodingASCII);

    // Open image
    CGImageRef image = SpriteManager::CreateNamedImage (filename, fileExtension);

    CFRelease (filename);
    CFRelease (fileExtension);

    if (image == NULL)
    {
        printf ("ERROR: cannot open image \"%s\"\n", imageFile);
        return;
    }

    // Get image information
    CGImageAlphaInfo info = CGImageGetAlphaInfo (image);

    BOOL hasAlpha =
    (
        (info == kCGImageAlphaPremultipliedLast) ||
        (info == kCGImageAlphaPremultipliedFirst) ||
        (info == kCGImageAlphaLast) ||
        (info == kCGImageAlphaFirst)
    );

    PixelFormat pixelFormat;

    if (CGImageGetColorSpace (image))
    {
        if (hasAlpha)
        {
            pixelFormat = format_RGBA8888;
        }
        else
        {
            pixelFormat = format_RGB565;
        }
    }
    else  // NOTE: No colorspace means a mask image
    {
        printf ("ERROR: invalid colour space for \"%s\"\nMust be RGBA8888...\n", imageFile);
        CGImageRelease (image);

        return;
    }

    // Round dimensions up to nearest power of 2
    CGSize imageSize = CGSizeMake (CGImageGetWidth (image), CGImageGetHeight (image));

    NSUInteger width = SpriteManager::MakePowerOf2 (imageSize.width),
               height = SpriteManager::MakePowerOf2 (imageSize.height);

#ifdef DEBUG
    // Check we're not wasting resources with padding
    if (width != imageSize.width || height != imageSize.height)
    {
        printf ("WARNING: texture \"%s\" has padded width and/or height.\n", imageFile);
    }

    // Check texture size is within maximum texture size
    if (width > TEXTURE_MAX_SIZE || height > TEXTURE_MAX_SIZE)
    {
        PANIC ("Texture is too big.");
    }
#endif

    // Store dimensions
    m_width = (int)width;
    m_height = (int)height;

    // Grab the data
    if (pixelFormat == format_RGBA8888)
    {
        // Make a pixel buffer
        Allocate (width * height * 4);

        if (m_data == NULL)
        {
            printf ("ERROR: out of memory for PixelMap of \"%s\"\n", imageFile);
        }
        else
        {
            // Get the pixels
            CGColorSpaceRef colourSpace = CGImageGetColorSpace (image);

            CGContextRef context = CGBitmapContextCreate (m_data, width, height, 8, (width * 4), colourSpace, (kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big));
            CGContextDrawImage (context, CGRectMake (0, 0, CGImageGetWidth (image), CGImageGetHeight (image)), image);

            CGColorSpaceRelease (colourSpace);
            CGContextRelease (context);

#ifdef DEBUG
            printf ("Loaded pixel map from texture \"%s\"\n", imageFile);
#endif
        }
    }
    else
    {
        printf ("ERROR: invalid pixel format for \"%s\"\nMust be RGBA8888...\n", imageFile);
    }

    // Clean up the image
    CGImageRelease (image);
}

And this is a much simpler, alternative method that I have recently tried that yields the exact same effect:

void PixelMap::LoadImageData (const char * imageFile)
{
    NSString * filename = [NSString stringWithUTF8String:imageFile];

    UIImage * image = [UIImage imageNamed:filename];

    m_width = (int)image.size.width;
    m_height = (int)image.size.height;

    Allocate (m_width * m_height * 4);

    CFDataRef imageData = CGDataProviderCopyData (CGImageGetDataProvider (image.CGImage));
    const UInt32 * pixels = (const UInt32 *)CFDataGetBytePtr (imageData);

    memcpy (m_data, pixels, m_dataSize);

    CFRelease (imageData);
}

This is an example font image I pass into this function: Font

Investigating the outputs from either of these ways of implementing this function yields:

On an iPhone 5 (correctly)...

Pixel Map Dump [64, 128 -> 128, 192]

                           ************                         
                         ****************                       
                       ********************                     
                      **********************                    
                     ***********************                    
                     ************************                   
                     ************************                   
                    *************************                   
                    *************************                   
                    *************************                   
                    *************************                   
                    *************************                   
                     ************************                   
                     ************************                   
                     ***********************                    
                     ***********************                    
                      *********************                     
                      *********************                     
                       *******************                      
                       *******************                      
                        *****************                       
                        *****************                       
                        *****************                       
                        *****************                       
                        *****************                       
                        ****************                        
                        ****************                        
                        ****************                        
                        ****************                        
                        ****************                        
                         **************                         
                          ***********                           
                            **********                          
                          *************                         
                         ***************                        
                        *****************                       
                        ******************                      
                       *******************                      
                       ********************                     
                       ********************                     
                      *********************                     
                      *********************                     
                      *********************                     
                      *********************                     
                      *********************                     
                      *********************                     
                      *********************                     
                      *********************                     
                       ********************                     
                       *******************                      
                        ******************                      
                         ***************                        
                          ************                          

...and an iPhone 6...

Pixel Map Dump [64, 128 -> 128, 192]

        **************                       *********          
       *****************                    **************      
       ******************                 ****************      
       ******************                 ****************      
       ******************                *****************      
        *****************               ******************      
         ****************               ******************      
         ****************              *******************      
         ***************               ***********  ******      
         ****************              **********    ****       
         *****************             **********               
         ******************           ***********     ***       
        *******************           ***********   ******      
        ********************          ************ *******      
        ********************          ********************      
        ********************          ********************      
        ********************           *******************      
       *********************           *******************      
       *********************           *******************      
      **********************           *******************      
     ***********************            ******************      
     **********************              *****************      
     **********************              *****************      
     *********************                ***************       
      ******************                    ************        
         ************                         *******           






      ***************                      *************        
     ******************                   **************        
     *******************                 ****************       
     *******************                *****************       
     *******************                *****************       
     *******************                *****************       
      ******************                *****************       
      ******************                ****************        
       ****************                 **************          
      *****************                 ****************        
      ****************                   ****************       
      ******************                  ***************       
      *******************                  ***************      
      ********************                  **************      
      ********************             ***** *************      
      ********************            ********************      
     **********************           ********************      
     **********************           ********************      
     **********************           ********************      
    ************************          ********************      
   *************************          ********************      
   **************************          ******************       
   **************************          *****************        
   **************************           ****************        
    *************************            *************          
      **********    *******                 ********            

As you can see, the resultant pixel data has basically been scaled by 1/2. For awhile I was blaming the colour space, but that doesn't seem to be the culprit.

One of the (many) pages I've read around this makes me think that the iPhone 6 and other devices exhibiting this problem are using 64 bits per pixel behind the scenes somewhere instead of the usual 32 (despite all attempts to force 32 BPP from my end).

Skip to 'Esoteric Layouts': http://www.objc.io/issue-3/moving-pixels-onto-the-screen.html

I really don't want to do an 'if (iPhone6 || iPadMini) do hack else work properly' type thing, but to be honest, I'm so fed up with trying to fix this problem neatly.

Also possibly relevant: iPhone 6 Plus resolution confusion: Xcode or Apple's website? for development

Community
  • 1
  • 1