在 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 中不起作用