【问题标题】:How do I extract the width and height of a PNG from looking at the header in objective c?如何通过查看目标 c 中的标题来提取 PNG 的宽度和高度?
【发布时间】:2013-05-19 10:58:58
【问题描述】:

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

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

}

这(下载前 100 个字节)非常有效,我通过这种方式获得了正确的 PNG 尺寸。

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

显然,在 PNG 文件中,标题中有一个称为 IHDR 的东西,我必须找到它,紧随其后的是宽度和高度。这给我的印象是我应该遍历数据并找到这个 IHDR 并获取尺寸。问题是,当我 NSLog 数据时,我得到如下内容:

... 49484452 000003b7 000001a7 08060000 006c2da0 b100000a 41694343 50494343 2050726f 66696c65 ...

我不知道如何处理循环遍历我的 NSData 对象并检测 IHDR 令牌,然后将其后的内容转换为数字。我也不知道只为 PNG 请求 100 个字节是为了获取尺寸而请求太多,还是请求不够

【问题讨论】:

  • NSString *convertedData = [[NSString alloc] initWithData:someData encoding:NSUTF8StringEncoding];

标签: objective-c image-processing header binary nsdata


【解决方案1】:

基本原理

根据PNG specification

PNG 文件的前 8 个字节总是包含以下内容 (十进制)值:137 80 78 71 13 10 26 10

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

那么,

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

宽度:4 个字节

高度:4 个字节

等等……

所以根据chunk的结构,你首先要读取代表chunk数据域长度的四个字节,然后读取代表chunk名称的四个字节,然后是表示宽度和高度的两个 32 位整数,总共 8 个字节。

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

Objective-C 代码

一旦你有了你的 NSData 对象,你只需要访问它包含的字节:

unsigned char buffer[24];
[data getBytes:buffer length:24];

可选,但我强烈推荐它,检查您是否确实有一个 PNG 文件:

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

确保您拥有 IHDR:

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

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

unsigned int width = OSReadBigInt32(buffer + 24 - 8);
unsigned int height = OSReadBigInt32(buffer + 24 - 4);

编辑:修正缓冲区读取代码:PNG 实际上以大端序存储整数。

【讨论】:

  • 好答案。您可以为 buffer 创建一个 struct 类型以避免需要偏移量和大小常量。
  • @robmayoff 你觉得你可以举个例子来做这样的事吗?
  • @user309641 IGFastImage - Obj-C 工具用于在给定 URI 的情况下查找图像的大小,提供这样的东西:请参考 IGFastImageHelper parseSizeForPngWithData
  • 只有一件事,为什么要用星号强制转换为“unsigned int *)?我以为你只能用对象类型来做到这一点,而不是原始类型。
  • 对不起所有的cmets!我还必须将宽度和高度更改为此“ ​​int32_t width = OSReadBigInt32(buffer, 16); int32_t height = OSReadBigInt32(buffer, 20);”否则我得到的数字是巨大的负数。
猜你喜欢
  • 2012-12-14
  • 2016-06-12
  • 1970-01-01
  • 1970-01-01
  • 2016-10-15
  • 2014-04-20
  • 2016-10-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多