1. 程式人生 > >iOS 通過圖片檔案頭,獲取圖片型別,以及圖片尺寸

iOS 通過圖片檔案頭,獲取圖片型別,以及圖片尺寸

有時候,我們需要獲取一個圖片的格式和大小,但是卻不想從記憶體中把它的整個檔案從記憶體中讀取出來

因為讀取整個檔案的記憶體耗費還是有些的,所以這麼做通常不可取。

實在需要讀取那也沒辦法。

但是其實是可以從檔案中讀取出來。 原因是圖片檔案的格式標準。

這些標準的存在使得可以只讀取部分欄位,

就能獲知圖片的格式。

先說圖片的格式:

圖片的格式存在 圖片檔案的前8個位元組中。

以PNG 為例

PNG的前八個位元組依次為:0x89  0x50 0x4E   0x47 0x0D 0x0A 0x1A 0x0A

讀取PNG 圖片前8個位元組,判斷一下就可以知道是否是PNG 圖片。

其他圖片的格式類似。

再來說說圖片的大小。

圖片的大小也是存在相關欄位中

還是以PNG 為例,PNG 的圖片大小,儲存在第9個位元組開始的8個位元組中

前四個位元組表示寬度,後四個位元組表示高度。

PNG檔案儲存的長寬資訊儲存較簡單。

但是JPG檔案就比較複雜了。

首先JPG檔案儲存的東西是分塊儲存的。

很多很多的塊。

這些塊的開頭幾個位元組,表示該塊包含哪些資訊。

其中,如果某個塊的開頭0xffc0 或者是 0xffc1或者是 0xffc2

那麼該塊將會儲存 圖片檔案的 尺寸資訊,也就是長寬。

如果不是的話,那麼就是別的塊。

除了塊的開頭兩個位元組是固定的 0xffxx資訊,塊開頭的第三第四個位元組表示該塊的的長度。

我們可以根據該塊的大小資訊快速跳過此塊,查詢下一個塊,這樣能夠過濾不含有

圖片尺寸的資訊。

下面為部分程式碼。

//
//  HBImageTypeSizeUtill.m
//  Pods
//
//  Created by wangdan on 16/9/1.
//
//

#import "HBImageTypeSizeUtil.h"
#import <UIKit/UIKit.h>

@implementation HBImageTypeSizeUtil


+ (HBImageType)imageTypeOfFilePath:(NSString *)filePath
{

    HBImageType type = HBImageTypeUnKnow;
    if (filePath.length == 0) {
        return HBImageTypeUnKnow;
    }
    if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
        return HBImageTypeUnKnow;
    }
    NSFileHandle *fileHandler  = [NSFileHandle fileHandleForReadingAtPath:filePath];
    if (!fileHandler) {
        return HBImageTypeUnKnow;
    }
    
    NSData *headData = [fileHandler readDataOfLength:8];
    if (!headData.length == 8) {
        [fileHandler closeFile];
        return HBImageTypeUnKnow;
    }
    
    Byte *bytesArray = (Byte*)[headData bytes];
    if (bytesArray[0] == 0x89 &&
        bytesArray[1] == 0x50 &&  // P
        bytesArray[2] == 0x4E &&  // N
        bytesArray[3] == 0x47 &&  // G
        bytesArray[4] == 0x0D &&
        bytesArray[5] == 0x0A &&
        bytesArray[6] == 0x1A &&
        bytesArray[7] == 0x0A)
    {
        type = HBImageTypePNG;
    }
    else if (bytesArray[0] == 0xFF && bytesArray[1] == 0xD8)
    {
        type = HBImageTypeJPG;
    }
    else if (bytesArray[0] == 0x47 &&    // G
             bytesArray[1] == 0x49 &&    // I
             bytesArray[2] == 0x46 &&    // F
             bytesArray[3] == 0x38 &&    // 8
             (bytesArray[4] == 0x39 ||   // 9
              bytesArray[4] == 0x37) &&  // 7
             bytesArray[5] == 0x61)      // a
    {
        type = HBImageTypeGIF;
    }
    else if (bytesArray[0] == 0x42 &&   //B
             bytesArray[1] == 0x4D)      //M
    {
        type = HBImageTypeBMP;
    }
    
    [fileHandler closeFile];
    
    return type;

}

+ (CGSize)imagSizeOfFilePath:(NSString *)filePath
{
    CGSize finalSize = CGSizeZero;
    HBImageType type = [self imageTypeOfFilePath:filePath];
    switch (type) {
        case HBImageTypeJPG:
        {
            return  [self jpgImageSizeWithFilePath:filePath];
        }
            break;
        case HBImageTypePNG:
        {
            NSData *data = [self fileHeaderData:8 seek:16 filePath:filePath];
            finalSize =  [self pngImageSizeWithHeaderData:data];
        }
            break;
        case HBImageTypeBMP:
        {
            NSData *data = [self fileHeaderData:8 seek:18 filePath:filePath];
            finalSize = [self bmpImageSizeWithHeaderData:data];
        }
            break;
        case HBImageTypeGIF:
        {
            NSData *data = [self fileHeaderData:4 seek:6 filePath:filePath];
            finalSize = [self gifImageSizeWithHeaderData:data];
        }
            break;
        case HBImageTypeUnKnow:
        {
            UIImage *image = [UIImage imageWithContentsOfFile:filePath];
            if (image) {
                finalSize =  image.size;
            }
            else {
                finalSize = CGSizeZero;
            }
        }
            break;
        default:
            finalSize = CGSizeZero;
            break;
    }
    
    return finalSize;
}

+ (NSData*)fileHeaderData:(NSInteger)length seek:(NSInteger)seek filePath:(NSString *)filePath {
    if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
        return nil;
    }
    NSDictionary *fileAttribute = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];
    NSUInteger fileSize = [fileAttribute fileSize];
    if (length + seek > fileSize) {
        return nil;
    }
    
    NSFileHandle *fileHandler = [NSFileHandle fileHandleForReadingAtPath:filePath];
    [fileHandler seekToFileOffset:seek];
    NSData *data = [fileHandler readDataOfLength:length];
    [fileHandler closeFile];
    return data;
}


+ (CGSize)gifImageSizeWithHeaderData:(NSData *)data
{
    if (data.length != 4) {
        return CGSizeZero;
    }
    unsigned char w1 = 0, w2 = 0;
    [data getBytes:&w1 range:NSMakeRange(0, 1)];
    [data getBytes:&w2 range:NSMakeRange(1, 1)];
    short w = w1 + (w2 << 8);
    unsigned char h1 = 0, h2 = 0;
    [data getBytes:&h1 range:NSMakeRange(2, 1)];
    [data getBytes:&h2 range:NSMakeRange(3, 1)];
    short h = h1 + (h2 << 8);
    return CGSizeMake(w, h);
}


//JPG檔案資料,分很多很多的資料段, 並且每個資料段都會以 0xFF開頭
//找到一個數據斷後,如果資料段的開頭是0xffc0,那麼該資料段將會儲存 圖片的尺寸資訊
//否則0xffc0 後面緊跟的兩個欄位,儲存的是當前這個資料段的長度,可跳過當前的資料段
//然後尋找下一個資料段,然後檢視是否有圖片尺寸資訊
+ (CGSize)jpgImageSizeWithFilePath:(NSString *)filePath
{
    if (!filePath.length) {
        return CGSizeZero;
    }
    if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
        return  CGSizeZero;
    }
    
    
    NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
    NSUInteger offset = 2;
    NSUInteger length = 0;
    while (1) {
        [fileHandle seekToFileOffset:offset];
        length = 4;
        NSData *data = [fileHandle readDataOfLength:length];
        if (data.length != length) {
            break;
        }
        offset += length;
        int marker,code;
        NSUInteger newLength;
        unsigned char value1,value2,value3,value4;
        [data getBytes:&value1 range:NSMakeRange(0, 1)];
        [data getBytes:&value2 range:NSMakeRange(1, 1)];
        [data getBytes:&value3 range:NSMakeRange(2, 1)];
        [data getBytes:&value4 range:NSMakeRange(3, 1)];
        marker = value1;
        code = value2;
        newLength = (value3 << 8) + value4;
        if (marker != 0xff) {
            [fileHandle closeFile];
            return CGSizeZero;
        }
        
        if (code >= 0xc0 && code <= 0xc3) {
            length = 5;
            [fileHandle seekToFileOffset:offset];
            NSData *data =[fileHandle readDataOfLength:length];
            if (data.length != length) {
                break;
            }
            Byte *bytesArray = (Byte*)[data bytes];
            NSUInteger height = ((unsigned char)bytesArray[1] << 8) + (unsigned char)bytesArray[2];
            NSUInteger width =  ((unsigned char)bytesArray[3] << 8) + (unsigned char)bytesArray[4];
            [fileHandle closeFile];
            return CGSizeMake(width, height);
        }
        else {
            offset += newLength;
            offset -=2;
        }
    }
    [fileHandle closeFile];
    UIImage *image = [UIImage imageWithContentsOfFile:filePath];
    if (image) {
        CGSizeMake((NSInteger)image.size.width, (NSInteger)image.size.height);
    }
    return CGSizeZero;

}


+ (CGSize)pngImageSizeWithHeaderData:(NSData *)data
{
    if (data.length != 8) {
        return CGSizeZero;
    }
    unsigned char w1 = 0, w2 = 0, w3 = 0, w4 = 0;
    [data getBytes:&w1 range:NSMakeRange(0, 1)];
    [data getBytes:&w2 range:NSMakeRange(1, 1)];
    [data getBytes:&w3 range:NSMakeRange(2, 1)];
    [data getBytes:&w4 range:NSMakeRange(3, 1)];
    int w = (w1 << 24) + (w2 << 16) + (w3 << 8) + w4;
    
    unsigned char h1 = 0, h2 = 0, h3 = 0, h4 = 0;
    [data getBytes:&h1 range:NSMakeRange(4, 1)];
    [data getBytes:&h2 range:NSMakeRange(5, 1)];
    [data getBytes:&h3 range:NSMakeRange(6, 1)];
    [data getBytes:&h4 range:NSMakeRange(7, 1)];
    int h = (h1 << 24) + (h2 << 16) + (h3 << 8) + h4;
    return CGSizeMake(w, h);
}


+ (CGSize)bmpImageSizeWithHeaderData:(NSData *)data {
    if (data.length != 8) {
        return CGSizeZero;
    }
    unsigned char w1 = 0, w2 = 0, w3 = 0, w4 = 0;
    [data getBytes:&w1 range:NSMakeRange(0, 1)];
    [data getBytes:&w2 range:NSMakeRange(1, 1)];
    [data getBytes:&w3 range:NSMakeRange(2, 1)];
    [data getBytes:&w4 range:NSMakeRange(3, 1)];
    int w = w1 + (w2 << 8) + (w3 << 16) + (w4 << 24);
    unsigned char h1 = 0, h2 = 0, h3 = 0, h4 = 0;
    [data getBytes:&h1 range:NSMakeRange(4, 1)];
    [data getBytes:&h2 range:NSMakeRange(5, 1)];
    [data getBytes:&h3 range:NSMakeRange(6, 1)];
    [data getBytes:&h4 range:NSMakeRange(7, 1)];
    int h = h1 + (h2 << 8) + (h3 << 16) + (h4 << 24);
    return CGSizeMake(w, h);
}

@end


完整的程式碼見下面的demo連結

demo

也可以參考我的github

地址