iOS上的PNGvalidation

在iOS上编写地图应用程序,使用OpenStreetMap图块。 地图平铺图像是asynchronous下载并存储在字典中,或者保存在SQLite数据库中。

偶尔,无论出于何种原因,在尝试渲染地图平铺图像时,出现以下错误:
ImageIO: <ERROR> PNGinvalid distance too far back

这会导致令人讨厌的黑色方块出现在我的地图上。

这是发生这种情况的一段代码:

 NSData *imageData = [TileDownloader RetrieveDataAtTileX:(int)tilex Y:(int)tiley Zoom:(int)zoomLevel]; if (imageData != nil) { NSLog(@"Obtained image data\n"); UIImage *img = [[UIImage imageWithData:imageData] retain]; // Perform the image render on the current UI context. // ERROR OCCURS BETWEEN PUSH AND POP UIGraphicsPushContext(context); [img drawInRect:[self rectForMapRect:mapRect] blendMode:kCGBlendModeNormal alpha:1.0f]; UIGraphicsPopContext(); [img release]; } 

现在,我正在寻找一种方法来确保一个PNG是有效的,然后再尝试将其呈现给我的地图。

编辑:系统也偶尔抛出这个错误:
ImageIO: <ERROR> PNGIDAT: CRC error

我在其他问题中发现了这个问题,并把这个问题解决了。 希望你觉得这有帮助。

PNG格式有几个内置的检查。 每个“块”有一个CRC32检查,但检查你需要阅读完整的文件。

一个更基本的检查(当然不是简单的)将是读取文件的开始和结束。

前8个字节应始终为以下(十进制)值{137,80,78,71,13,10,26,10}(ref)。 特别地,第二到第四字节对应于ASCIIstring“PNG”。

hex:

 89 50 4e 47 0d 0a 1a 0a .. PNG ........... 

您也可以检查文件的最后12个字节(IEND块)。 中间的4个字节应该对应于ASCIIstring“IEND”。 更具体地说,最后12个字节应该是(以六进制):

 00 00 00 00 49 45 4e 44 ae 42 60 82 ........... IEND ........... 

(严格来说,PNG文件并不是真的必须以这12个字节结束,IEND块本身表示PNGstream的结束,所以原则上文件可能会有额外的尾随字节,PNG阅读器将忽略这些字节。实际上,这是非常不可能的)。

这是一个实现:

 - (BOOL)dataIsValidPNG:(NSData *)data { if (!data || data.length < 12) { return NO; } NSInteger totalBytes = data.length; const char *bytes = (const char *)[data bytes]; return (bytes[0] == (char)0x89 && // PNG bytes[1] == (char)0x50 && bytes[2] == (char)0x4e && bytes[3] == (char)0x47 && bytes[4] == (char)0x0d && bytes[5] == (char)0x0a && bytes[6] == (char)0x1a && bytes[7] == (char)0x0a && bytes[totalBytes - 12] == (char)0x00 && // IEND bytes[totalBytes - 11] == (char)0x00 && bytes[totalBytes - 10] == (char)0x00 && bytes[totalBytes - 9] == (char)0x00 && bytes[totalBytes - 8] == (char)0x49 && bytes[totalBytes - 7] == (char)0x45 && bytes[totalBytes - 6] == (char)0x4e && bytes[totalBytes - 5] == (char)0x44 && bytes[totalBytes - 4] == (char)0xae && bytes[totalBytes - 3] == (char)0x42 && bytes[totalBytes - 2] == (char)0x60 && bytes[totalBytes - 1] == (char)0x82); } 

从我自己的Asynchronous Download Queue Manager切换到All Seeing I实现。 问题成为一个有争议的问题。