Image I/O基礎(chǔ)
Image I/O框架提供了從源圖像讀取數(shù)據(jù)的不透明數(shù)據(jù)類型(CGImageSourceRef)和圖像數(shù)據(jù)寫入到目的地(CGImageDestinationRef),它支持多種圖片格式,包括標(biāo)準(zhǔn)的web格式,高動(dòng)態(tài)范圍圖片和相機(jī)原始數(shù)據(jù),Image I/O還有許多特性,如:
最快的圖片編解碼(MAC平臺(tái))
增量加載圖片的能力
支持圖像元數(shù)據(jù)
有效的緩存
創(chuàng)建圖片源和寫入目的地的方法有:URLS:可以將指定位置的圖片(URL)作為圖片數(shù)據(jù)的提供者或接受者,在Image I/O中,URL在Core Foundation框架中的數(shù)據(jù)類型是CFURLRef,可以通過(guò)(__bridge CFURLRef)NSURL橋接.
Core Foundation對(duì)象 CFDataRef和CFMutableDataRef
Quartz data consumer(消費(fèi)者) (CGDataConsumerRef) 和 data provider(提供者) (CGDataProviderRef)
使用
導(dǎo)入頭文件#import <ImageIO/ImageIO.h>
支持的圖片格式
Image I/O框架支持大多數(shù)常見(jiàn)的圖像文件格式立肘,如JPEG、JPEG2000、RAW应役、TIFF许饿、BMP和PNG支示。并不是每個(gè)平臺(tái)都支持所有格式宽档⊥脊龋可以調(diào)用函數(shù)CGImageSourceCopyTypeIdentifiers(創(chuàng)建圖片源),CGImageDestinationCopyTypeIdentifiers(寫入)來(lái)查看支持的格式列表
// 獲取票渠、打印所支持的統(tǒng)一類型標(biāo)識(shí)符
- (void)getUniformyTypeIdentifiers{
//數(shù)組
CFArrayRef mySourceTypes = CGImageSourceCopyTypeIdentifiers();
//在控制臺(tái)打印數(shù)組
CFShow(mySourceTypes);
CFArrayRef myDestinationTypes = CGImageDestinationCopyTypeIdentifiers();
CFShow(myDestinationTypes);
}
創(chuàng)建和使用圖片源
圖片源提取了數(shù)據(jù)訪問(wèn)任務(wù)并且節(jié)省了通過(guò)原始緩存數(shù)據(jù)中管理數(shù)據(jù)的需要逐哈。數(shù)據(jù)源可以包含多個(gè)圖片,縮略圖问顷,每張圖片的屬性和圖片文件昂秃。當(dāng)你需要用到圖片數(shù)據(jù)時(shí),圖片源是最好的方式杜窄。在創(chuàng)建CGImageSource對(duì)象后肠骆,你可以通過(guò)引用這個(gè)對(duì)象獲取圖片,縮略圖塞耕,圖片屬性和其他圖片信息.
創(chuàng)建一張圖片通過(guò)圖片源
Image I/O最經(jīng)常完成的一個(gè)任務(wù)就是從圖片源中創(chuàng)建圖片蚀腿。如下面的例子所示。該例子展示了怎樣從路徑名中創(chuàng)建一個(gè)圖片源并且抽取圖片。當(dāng)你創(chuàng)建圖片源對(duì)象后莉钙,你可以提供一個(gè)圖片格式的提示
從圖片源中創(chuàng)建圖片時(shí)廓脆,必須指定一個(gè)索引并且提供屬性的字典來(lái)指定是否創(chuàng)建縮略圖、是否允許緩存等磁玉。詳見(jiàn)CGImageSource Reference 和 CGImageProperties Reference
提供索引的原因是因?yàn)橐恍﹫D片文件格式允許同個(gè)圖片源中有多個(gè)圖片(比如gif)停忿。如果只有一個(gè)的話可以傳0∥蒙。可以調(diào)用CGImageSourceGetCount來(lái)獲取數(shù)量
- (void)getImageWithImageSourceRef{
NSString *string = [[NSBundle mainBundle]pathForResource:@"videoPlay.png" ofType:nil];
NSURL *url = [NSURL fileURLWithPath:string];
CFStringRef keys[2],values[2];
//將圖片緩存成解碼格式
keys[0] = kCGImageSourceShouldCache;
values[0] = (CFTypeRef)kCFBooleanTrue;
//在圖片格式支持浮點(diǎn)值時(shí)使用浮點(diǎn)值
keys[1] = kCGImageSourceShouldAllowFloat;
values[1] = (CFTypeRef)kCFBooleanTrue;
/**
創(chuàng)建一個(gè)不可變字典
@param allocator#> 為字典分配內(nèi)存,傳NULL或kCFAllocatorDefault使用當(dāng)前默認(rèn)的分配器 description#>
@param keys#> key的數(shù)組席赂。如果numValues參數(shù)為0,則這個(gè)值可能是NULL时迫。這個(gè)函數(shù)沒(méi)有改變或釋放這個(gè)數(shù)組氧枣。該值必須是有效的C數(shù)組 description#>
@param values#> value的數(shù)組 description#>
@param numValues#> 鍵值對(duì)數(shù)目。>=0 && >=實(shí)際數(shù)目别垮。 description#>
@param keyCallBacks#> 鍵的回調(diào)便监。 description#>
@param valueCallBacks#> 值的回調(diào) description#>
@return CF字典
*/
CFDictionaryRef options = CFDictionaryCreate(NULL, (const void**)keys, (const void **)values, 2, &kCFTypeDictionaryKeyCallBacks,& kCFTypeDictionaryValueCallBacks);
CGImageSourceRef imageSource = CGImageSourceCreateWithURL((__bridge CFURLRef)url, options);
if (imageSource == NULL) {
fprintf(stderr, "Image source is NULL.");
}
CFRelease(options);
size_t count = CGImageSourceGetCount(imageSource);
NSLog(@"圖片數(shù)量:%zu",count);
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);
if (imageRef == NULL) {
fprintf(stderr, "Image not created from image source.");
}
self.imageView.image = [UIImage imageWithCGImage:imageRef];
}
通過(guò)圖片源創(chuàng)建一張縮略圖
一些圖片源文件包含可以檢索的縮略圖。如果縮略圖還沒(méi)有出現(xiàn)碳想,Image I/O將提供創(chuàng)建它們的選項(xiàng)烧董。可以指定最大縮略圖大小以及是否對(duì)縮略圖進(jìn)行轉(zhuǎn)換胧奔。
使用kCGImageSourceCreateThumbnailWithTransform,指定縮略圖是否應(yīng)該旋轉(zhuǎn)和縮放逊移,以匹配整張圖片的方向和像素寬高比。
CGImageRef MyCreateThumbnailImageFromData (NSData * data, int imageSize)
{
CGImageRef myThumbnailImage = NULL;
CGImageSourceRef myImageSource;
CFDictionaryRef myOptions = NULL;
CFStringRef myKeys[3];
CFTypeRef myValues[3];
CFNumberRef thumbnailSize;
//通過(guò)NSData創(chuàng)建圖片源
myImageSource = CGImageSourceCreateWithData((CFDataRef)data,
NULL);
//確認(rèn)圖片源存在
if (myImageSource == NULL){
fprintf(stderr, "Image source is NULL.");
return NULL;
}
// 創(chuàng)建CFNumber對(duì)象. kCFNumerType
thumbnailSize = CFNumberCreate(NULL, kCFNumberIntType, &imageSize);
//配置縮略圖選項(xiàng)
myKeys[0] = kCGImageSourceCreateThumbnailWithTransform;
myValues[0] = (CFTypeRef)kCFBooleanTrue;
myKeys[1] = kCGImageSourceCreateThumbnailFromImageIfAbsent;
myValues[1] = (CFTypeRef)kCFBooleanTrue;
myKeys[2] = kCGImageSourceThumbnailMaxPixelSize;
myValues[2] = (CFTypeRef)thumbnailSize;
myOptions = CFDictionaryCreate(NULL, (const void **) myKeys,
(const void **) myValues, 2,
&kCFTypeDictionaryKeyCallBacks,
& kCFTypeDictionaryValueCallBacks);
//創(chuàng)建縮略圖
myThumbnailImage = CGImageSourceCreateThumbnailAtIndex(myImageSource,
0,
myOptions);
//ref類型的需要釋放
CFRelease(thumbnailSize);
CFRelease(myOptions);
CFRelease(myImageSource);
if (myThumbnailImage == NULL){
fprintf(stderr, "Thumbnail image not created from image source.");
return NULL;
}
return myThumbnailImage;
}
增量顯示一張圖片
如果有非常大的圖片龙填,或者正在Web上加載圖片胳泉,可以創(chuàng)建一個(gè)逐步加載的圖片源,動(dòng)態(tài)的繪制圖片岩遗。
步驟:
- 創(chuàng)建CFData對(duì)象來(lái)累加圖片數(shù)據(jù)
- 通過(guò)調(diào)用函數(shù)CGImageSourceCreateIncremental創(chuàng)建增量圖片源
- 添加圖片數(shù)據(jù)到CFData對(duì)象扇商。
- 調(diào)用函數(shù)CGImageSourceUpdateData,將CFData對(duì)象和指定數(shù)據(jù)參數(shù)是否包含整張圖片亦或者是部分的圖片數(shù)據(jù)的布爾值傳遞過(guò)去。事實(shí)上宿礁,數(shù)據(jù)參數(shù)必須包含完整的圖片文件數(shù)據(jù)案铺,以便能加載到那個(gè)點(diǎn)。
- 如果已經(jīng)積累了足夠的圖像數(shù)據(jù)梆靖,通過(guò)調(diào)用CGImageSourceCreateImageAtIndex創(chuàng)建一個(gè)圖像控汉,繪制部分圖像,然后釋放它返吻。
- 調(diào)用函數(shù)CGImageSourceGetStatusAtIndex檢查所有的圖像數(shù)據(jù)姑子。如果圖像是完整的,這個(gè)函數(shù)返回kCGImageStatusComplete测僵。如果圖像不完整街佑,請(qǐng)重復(fù)步驟3和步驟4,直到它結(jié)束為止。
- 釋放增量圖像源舆乔。
@interface ViewController ()<NSURLSessionDataDelegate>{
CGImageSourceRef _incrementallyImgSource;
NSMutableData *_recieveData;
bool _isLoadFinished;
long long _expectedLeght;
}
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//創(chuàng)建一個(gè)空的CGImageSource
_incrementallyImgSource = CGImageSourceCreateIncremental(NULL);
_recieveData = [[NSMutableData alloc]init];
_isLoadFinished = false;
// 使用代理方法需要設(shè)置代理,但是session的delegate屬性是只讀的,要想設(shè)置代理只能通過(guò)這種方式創(chuàng)建session
NSURLSession *session = [NSURLSession sessionWithConfiguration
:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self
delegateQueue:[NSOperationQueue mainQueue]];
// 創(chuàng)建任務(wù)(因?yàn)橐褂么矸椒?就不需要block方式的初始化了)
NSURLSessionDataTask *task = [session dataTaskWithRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://cdn.eso.org/images/large/eso0934a.jpg"]]];
// 啟動(dòng)任務(wù)
[task resume];
}
//對(duì)應(yīng)的代理方法如下:
// 1.接收到服務(wù)器的響應(yīng)
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
_expectedLeght = response.expectedContentLength;
NSLog(@"expected Length: %lld", _expectedLeght);
NSString *mimeType = response.MIMEType;
NSLog(@"MIME TYPE %@", mimeType);
NSArray *arr = [mimeType componentsSeparatedByString:@"/"];
if (arr.count < 1 || ![[arr objectAtIndex:0] isEqual:@"image"]) {
NSLog(@"not a image url");
}
// 允許處理服務(wù)器的響應(yīng)岳服,才會(huì)繼續(xù)接收服務(wù)器返回的數(shù)據(jù)
completionHandler(NSURLSessionResponseAllow);
}
// 2.接收到服務(wù)器的數(shù)據(jù)(可能調(diào)用多次)
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
// 處理每次接收的數(shù)據(jù)
[_recieveData appendData:data];
_isLoadFinished = false;
if (_expectedLeght == _recieveData.length) {
_isLoadFinished = true;
}
// 每次收到數(shù)據(jù)的時(shí)候調(diào)用CGImageSourceUpdateData更新imageSource的數(shù)據(jù)剂公,接著調(diào)用CGImageSourceCreateImageAtIndex獲取最新的圖片
CGImageSourceUpdateData(_incrementallyImgSource, (CFDataRef)_recieveData, _isLoadFinished);
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(_incrementallyImgSource, 0, NULL);
self.imageView.image = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
}
// 3.請(qǐng)求成功或者失斚A(如果失敗,error有值)
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
// 請(qǐng)求完成,成功或者失敗的處理
if (!error) {
NSLog(@"下載完畢!");
CFRelease(_incrementallyImgSource);
}
}
@end
顯示圖片屬性
數(shù)碼照片包含了豐富的信息:圖像尺寸(dimensions)纲辽,分辨率(resolution)颜武,方向9orientation0,顏色配置(color profile)拖吼,光圈(aperture)鳞上,測(cè)光模式(metering mode),焦距(focal length)吊档,創(chuàng)建日期篙议,關(guān)鍵字,標(biāo)題(caption)怠硼,以及更多的信息鬼贱。通過(guò)調(diào)用CGImageSourceCopyPropertiesAtIndex,我們可以拿到這些信息,這將對(duì)于圖像處理和圖像編輯非常有用.
- (void)getImageSourceProperty{
NSString *path = [[NSBundle mainBundle]pathForResource:@"gif.gif" ofType:nil];
NSURL *imageFileURL = [NSURL fileURLWithPath:path];
CGImageSourceRef imageSource = CGImageSourceCreateWithURL((__bridge CFURLRef)imageFileURL, NULL);
if (imageSource) {
NSDictionary *options = @{(NSString *)kCGImageSourceShouldCache:@NO};
CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, (__bridge CFDictionaryRef)options);
CFShow(imageProperties);
if (imageProperties) {
CFStringRef width = CFDictionaryGetValue(imageProperties, kCGImagePropertyPixelWidth);
NSLog(@"width:%@",width);
CFStringRef height = CFDictionaryGetValue(imageProperties, kCGImagePropertyPixelHeight);
NSLog(@"height:%@",height);
CFDictionaryRef exif = CFDictionaryGetValue(imageProperties, kCGImagePropertyExifDictionary);
if (exif) {
NSString *dateTakenString = (NSString *)CFDictionaryGetValue(exif, kCGImagePropertyExifDateTimeOriginal);
NSLog(@"Date Taken: %@", dateTakenString);
}
CFDictionaryRef tiff = CFDictionaryGetValue(imageProperties, kCGImagePropertyTIFFDictionary);
if (tiff) {
NSString *cameraModel = (NSString *)CFDictionaryGetValue(tiff, kCGImagePropertyTIFFModel);
NSLog(@"Camera Model: %@", cameraModel);
}
CFDictionaryRef gps = CFDictionaryGetValue(imageProperties, kCGImagePropertyGPSDictionary);
if (gps) {
NSString *latitudeString = (NSString *)CFDictionaryGetValue(gps, kCGImagePropertyGPSLatitude);
NSString *latitudeRef = (NSString *)CFDictionaryGetValue(gps, kCGImagePropertyGPSLatitudeRef);
NSString *longitudeString = (NSString *)CFDictionaryGetValue(gps, kCGImagePropertyGPSLongitude);
NSString *longitudeRef = (NSString *)CFDictionaryGetValue(gps, kCGImagePropertyGPSLongitudeRef);
NSLog(@"GPS Coordinates: %@ %@ / %@ %@", longitudeString, longitudeRef, latitudeString, latitudeRef);
}
CFRelease(imageProperties);
}
CFRelease(imageSource);
} else {
NSLog(@"Error loading image");
}
}
CGImageDestination
CGImageDestination抽象化了數(shù)據(jù)寫入任務(wù)并且省去了從緩存器管理數(shù)據(jù)的步驟。CGImageDestination可以呈現(xiàn)一張或多張圖片香璃。它包含縮略圖和每張圖片的屬性这难。創(chuàng)建CGImageDestination對(duì)象可以通過(guò)(URL, CFData或者Quartz數(shù)據(jù))參數(shù),然后添加圖片數(shù)據(jù)葡秒,設(shè)置圖片屬性姻乓,完成操作后,調(diào)用CGImageDestinationFinalize.
設(shè)置屬性
CGImageDestinationSetProperties添加了屬性字典(CFDictionaryRef)眯牧。雖然屬性的設(shè)置是可選的蹋岩,但是通常都會(huì)設(shè)置屬性來(lái)完成自己的需求。舉個(gè)例子学少,如果你的程序允許用戶添加圖片關(guān)鍵字或者改變飽和度星澳,曝光值等,就可將這些值保存在字典中,調(diào)用這個(gè)函數(shù)旱易。
Image I/O定義了一系列可擴(kuò)展的鍵來(lái)指定壓縮質(zhì)量禁偎,背景顏色合成的EXIF字典鍵,顏色模型值阀坏,GIF字典鍵如暖,尼康和佳能相機(jī)鍵,等等忌堂。
在設(shè)置屬性時(shí)盒至,你有兩個(gè)選擇。可以創(chuàng)建CFDictionary對(duì)象或者創(chuàng)建NSDictionary對(duì)象枷遂。然后在傳遞可選字典到CGImageDestinationSetProperties時(shí)將它當(dāng)做CFDictionaryRef.
float compression = 1.0; //設(shè)置壓縮比
int orientation = 4; // 設(shè)置朝向bottom, left.
CFStringRef myKeys[3];
CFTypeRef myValues[3];
CFDictionaryRef myOptions = NULL;
myKeys[0] = kCGImagePropertyOrientation;
myValues[0] = CFNumberCreate(NULL, kCFNumberIntType, &orientation);
myKeys[1] = kCGImagePropertyHasAlpha;
myValues[1] = kCFBooleanTrue;
myKeys[2] = kCGImageDestinationLossyCompressionQuality;
myValues[2] = CFNumberCreate(NULL, kCFNumberFloatType, &compression);
myOptions = CFDictionaryCreate( NULL, (const void **)myKeys, (const void **)myValues, 3,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
// 記得Release不需要的變量
寫入一張圖片通過(guò)CGImageDestinationRef
你可以創(chuàng)建一個(gè)CGImageDestinationRef對(duì)象通過(guò)以下函數(shù):
CGImageDestinationCreateWithURL,
CGImageDestinationCreateWithData, CGImageDestinationCreateWithDataConsumer
你需要提供所生成圖像文件的UTI
創(chuàng)建完成CGImageDestinationRef,你可以調(diào)用CGImageDestinationAddImage 或者 CGImageDestinationAddImageFromSource方法添加一張圖片,如果目標(biāo)文件的格式支持多張圖片,你可以重復(fù)調(diào)用,添加完成調(diào)用CGImageDestinationFinalize方法,來(lái)表明結(jié)束添加.此時(shí),不可再次進(jìn)行添加圖片
- (void) writeCGImage: (CGImageRef) image toURL: (NSURL*) url withType: (CFStringRef) imageType andOptions: (CFDictionaryRef) options
{
CGImageDestinationRef myImageDest = CGImageDestinationCreateWithURL((CFURLRef)url, imageType, 1, nil);
//添加數(shù)據(jù)和圖片
CGImageDestinationAddImage(myImageDest, image, options);
//最后調(diào)用樱衷,完成數(shù)據(jù)寫入
CGImageDestinationFinalize(myImageDest);
//釋放
CFRelease(myImageDest);
}
創(chuàng)建一張動(dòng)圖
Image I/O框架可以用來(lái)創(chuàng)建動(dòng)畫圖像。創(chuàng)建一個(gè)動(dòng)畫形象的時(shí)候酒唉,調(diào)用CGImageDestinationAddImage添加每一幀動(dòng)畫矩桂。還必須指定控制動(dòng)畫執(zhí)行方式的一些屬性。
- (void)gifSynthesize{
//1.獲取數(shù)據(jù)
NSMutableArray *tmpArray = [[NSMutableArray alloc]init];
for (int i = 0; i < 4; i ++) {
UIImage *image = [UIImage imageNamed:[NSString stringWithFormat:@"Documents%d",i]];
[tmpArray addObject:image];
}
//2.創(chuàng)建gif文件
NSArray *document = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentStr = [document objectAtIndex:0];
NSFileManager *manager = [NSFileManager defaultManager];
NSString *textDict = [documentStr stringByAppendingString:@"/gif"];
[manager createDirectoryAtPath:textDict withIntermediateDirectories:YES attributes:nil error:nil];
NSString *path = [textDict stringByAppendingString:@"test1.gif"];
NSLog(@"path:%@",path);
//3.配置gif的屬性
CGImageDestinationRef destination;
CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef)path, kCFURLPOSIXPathStyle, false);
destination = CGImageDestinationCreateWithURL(url, kUTTypeGIF, tmpArray.count, NULL);
NSDictionary *frameDic = [NSDictionary dictionaryWithObject:[NSMutableDictionary dictionaryWithObjectsAndKeys:@(0.3),(NSString *)kCGImagePropertyGIFDelayTime,nil] forKey:(NSString *)kCGImagePropertyGIFDelayTime];
NSMutableDictionary *gifParmdict = [NSMutableDictionary dictionaryWithCapacity:2];
[gifParmdict setObject:@(YES) forKey:(NSString *)kCGImagePropertyGIFHasGlobalColorMap];
[gifParmdict setObject:(NSString *)kCGImagePropertyColorModelRGB forKey:(NSString *)kCGImagePropertyColorModel];
[gifParmdict setObject:@(8) forKey:(NSString *)kCGImagePropertyDepth];
[gifParmdict setObject:@(0) forKey:(NSString *)kCGImagePropertyGIFLoopCount];
NSDictionary *gifProperty = [NSDictionary dictionaryWithObject:gifParmdict forKey:(NSString *)kCGImagePropertyGIFDictionary];
//4.單幀添加到圖片
for (UIImage *image in tmpArray) {
CGImageDestinationAddImage(destination, image.CGImage, (__bridge CFDictionaryRef)frameDic);
}
CGImageDestinationSetProperties(destination, (__bridge CFDictionaryRef)gifProperty);
//釋放
CGImageDestinationFinalize(destination);
CFRelease(destination);
}