如何通过查看目标c中的标题来提取PNG的宽度和高度?

Dav*_*hta 10 binary header image-processing objective-c nsdata

我需要在不下载图像的情况下在线查找图像的尺寸.为此,我这样做:

+ (CGSize) getImageDimensions:(NSString *)url {

   // Send a synchronous request
    NSMutableURLRequest * urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString: url]];
    NSString *rangeString = [url hasSuffix: @"png"] ? @"bytes=0-100" : @"bytes=0-1300";
    [urlRequest setValue:rangeString forHTTPHeaderField:@"Range"];
    NSURLResponse * response = nil;
    NSError * error = nil;
    NSData * data = [NSURLConnection sendSynchronousRequest:urlRequest
                                          returningResponse:&response
                                                      error:&error];
    if (error == nil)
        return [UIImage imageWithData: data].size;
    else
        return CGSizeMake(0, 0);

}
Run Code Online (Sandbox Code Playgroud)

这(下载前100个字节)令人惊讶地工作,我通过这种方式获得了正确的PNG尺寸.

但是我不认为这是一种非常优雅的方法.首先,我选择通过猜测和检查来下载前100个字节,使其尽可能小,同时仍然可以正常工作.

显然在PNG文件中,这个东西在标题中称为IHDR,我必须在它的宽度和高度之后直接找到它.这让我觉得我应该遍历数据并找到这个IHDR并获得维度.问题是,当我NSLog数据时,我得到这样的东西:

... 49484452 000003b7 000001a7 08060000 006c2da0 b100000a 41694343 50494343 2050726f 66696c65 ...
Run Code Online (Sandbox Code Playgroud)

我不知道如何处理我的NSData对象的循环并检测IHDR令牌,然后将之后的内容转换为数字.我也不知道请求只有100个字节的PNG请求太多只是为了得到尺寸,或者它是否要求不够

Sir*_*ius 21

合理

根据PNG规范:

在第一个8个字节的PNG文件科幻总是包含以下(十进制)值:137 80 78 71 13 10 26 10

因此,您必须阅读这些内容以确保您确实拥有PNG文件.

然后,

IHDR块必须首先出现.它包含:

宽度:4个字节

高度:4个字节

等等...

所以根据组块的结构中,首先必须读取4个表示块的数据字段的长度的字节,则4个表示块的名称字节,则表示宽度和高度中的两个32位整数,8个字节总.

因此,为了确定宽度和高度,您必须读取的确切最小字节数是8 + 4 + 4 + 8 = 24字节.

Objective-C代码

获得NSData对象后,只需访问它包含的字节:

unsigned char buffer[24];
[data getBytes:buffer length:24];
Run Code Online (Sandbox Code Playgroud)

或者,但我强烈推荐它,检查你确实有一个PNG文件:

unsigned char png_header[] = {137, 80, 78, 71, 13, 10, 26, 10};
if (memcmp(buffer, png_header, 8)) {
     // this is not a PNG !
}
Run Code Online (Sandbox Code Playgroud)

确保你有IHDR:

unsigned char ihdr_name[] = "IHDR";
if (memcmp(buffer+8+4, ihdr_name, 4)) {
    // not an IHDR chunk, invalid PNG file
}
Run Code Online (Sandbox Code Playgroud)

宽度和高度可以作为big-endian编码的无符号整数访问,分别为偏移24减8和减4字节:

unsigned int width = OSReadBigInt32(buffer + 24 - 8);
unsigned int height = OSReadBigInt32(buffer + 24 - 4);
Run Code Online (Sandbox Code Playgroud)

编辑:固定缓冲区读取代码:PNG实际上以big-endian存储整数.