在 iOS 8 / iPhone 6 上获取像素数据

Posted

技术标签:

【中文标题】在 iOS 8 / iPhone 6 上获取像素数据【英文标题】:Getting pixel data on iOS 8 / iPhone 6 【发布时间】:2014-09-26 11:59:17 【问题描述】:

我需要检索位图图像给定 (x, y) 坐标的像素数据。多年来,我的代码如下:

_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;

这在我运行 ios7 的 iPhone 4 和 5(不是 S)上完美运行。

我现在正试图让它在运行 iOS8 的 iPhone 6 上运行。为此,我需要进行此更改:

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

注意代码中多余的* 2

我想了解为什么此更改是必要的,以便我可以正确应用它。它是与 iOS8 相关联还是与 iPhone 6 的 64 位架构相关联?如果是由于 64 位架构,那么它是没有意义的,因为:

    像素数据本身仍被编码为 4 个字节。

    似乎是由于额外的填充而改变了每行像素数据的宽度,但该行数据的位置是相同的。即假设 8 字节像素 not 工作:

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

谁能解释一下发生了什么?

【问题讨论】:

【参考方案1】:

这个问题一直困扰着我整整一周...

我使用 PNG 文件中网格中的字符来制作字体。加载时,我分析每个字符的像素以评估确切的宽度。这段代码多年来一直运行良好,并且刚刚开始在 iPhone 6(和 iPad Mini)上破解。

这与 iOS 8 无关,因为该代码仍然适用于装有 iOS 8 的 iPhone 5。

这也不是因为编译过程发生了任何变化,因为我可以在模拟器和设备上从同一个项目中重现问题。

这是使用多年的代码:

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);

这是我最近尝试过的一种更简单的替代方法,可以产生完全相同的效果:

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);

这是我传递给此函数的示例字体图像:

调查实现此功能的这两种方式的输出:

在 iPhone 5 上(正确)...

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

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

...还有一部 iPhone 6...

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

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






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

如您所见,得到的像素数据基本上已经缩放了 1/2。有一段时间我一直在指责色彩空间,但这似乎不是罪魁祸首。

我读过的(许多)页面之一让我认为 iPhone 6 和其他出现此问题的设备在幕后某处使用每像素 64 位而不是通常的 32 位(尽管所有尝试强制32 BPP 从我这边)。

跳到“深奥的布局”:http://www.objc.io/issue-3/moving-pixels-onto-the-screen.html

我真的不想做“if (iPhone6 || iPadMini) do hack else work normal”之类的事情,但老实说,我已经厌倦了试图巧妙地解决这个问题。

也可能相关:iPhone 6 Plus resolution confusion: Xcode or Apple's website? for development

【讨论】:

以上是关于在 iOS 8 / iPhone 6 上获取像素数据的主要内容,如果未能解决你的问题,请参考以下文章

iPhone 6 Simulator,iOS 8 上奇怪的 TabBar 背景问题

推送通知在带有 iOS 8.0 的 iPhone 6 中不起作用

iOS 8 / iPhone 6 XPC 崩溃

如何在 iPad 上运行的 iPhone 应用程序中获取 iOS 8 自定义键盘的主机应用程序屏幕几何图形?

iOS @2x @3x图的区别和理解

IOS9 以编程方式限制 iphone 和 ipad