从ALAssetRepresentation生成自定义缩略图
我的主要问题是我需要获得一个ALAsset对象的缩略图。
我尝试了很多解决scheme,并search堆栈溢出了几天,由于这些约束,我发现的所有解决scheme都不适合我:
- 我不能使用默认的缩略图,因为它太less了;
- 我无法使用fullScreen或fullResolution图像,因为我在屏幕上有很多图像;
- 我不能使用UIImage或UIImageView来resize,因为这些加载fullResolution图像
- 我无法加载内存中的图像,我正在处理20Mpx图像;
- 我需要创build一个200×200像素版本的原始资产加载到屏幕上;
这是我附带的代码的最后一个迭代:
#import <AssetsLibrary/ALAsset.h> #import <ImageIO/ImageIO.h> // ... ALAsset *asset; // ... ALAssetRepresentation *assetRepresentation = [asset defaultRepresentation]; NSDictionary *thumbnailOptions = [NSDictionary dictionaryWithObjectsAndKeys: (id)kCFBooleanTrue, kCGImageSourceCreateThumbnailWithTransform, (id)kCFBooleanTrue, kCGImageSourceCreateThumbnailFromImageAlways, (id)[NSNumber numberWithFloat:200], kCGImageSourceThumbnailMaxPixelSize, nil]; CGImageRef generatedThumbnail = [assetRepresentation CGImageWithOptions:thumbnailOptions]; UIImage *thumbnailImage = [UIImage imageWithCGImage:generatedThumbnail];
问题是,所得到的CGImageRef
既不是由方向转换的,也不是由指定的最大像素尺寸转换的;
我也试图find使用CGImageSource
resize的方法,但是:
- 资源url不能在
CGImageSourceCreateWithURL:
; - 我无法从
ALAsset
或ALAssetRepresentation
提取CGDataProviderRef
以与CGImageSourceCreateWithDataProvider:
一起使用CGImageSourceCreateWithDataProvider:
; -
CGImageSourceCreateWithData:
要求我将FullResolution或全屏资源存储在内存中才能正常工作。
我错过了什么?
是否有另一种方法从ALAsset
或ALAssetRepresentation
我缺less自定义缩略图?
提前致谢。
您可以使用CGImageSourceCreateThumbnailAtIndex
从可能较大的图像源创build一个小图像。 您可以使用ALAssetRepresentation
的getBytes:fromOffset:length:error:
方法从磁盘加载图像,并使用它来创buildCGImageSourceRef。
然后,只需将kCGImageSourceThumbnailMaxPixelSize
和kCGImageSourceCreateThumbnailFromImageAlways
选项传递给CGImageSourceCreateThumbnailAtIndex
,并将其创build为较小的版本,而不会将巨大版本加载到内存中。
我已经写了一篇博客文章,并且用这个技巧充实了。
Jesse Rusak提到的这种方法存在问题。 如果资产太大,您的应用程序将与以下堆栈崩溃:
0 CoreGraphics 0x2f602f1c x_malloc + 16 1 libsystem_malloc.dylib 0x39fadd63 malloc + 52 2 CoreGraphics 0x2f62413f CGDataProviderCopyData + 178 3 ImageIO 0x302e27b7 CGImageReadCreateWithProvider + 156 4 ImageIO 0x302e2699 CGImageSourceCreateWithDataProvider + 180 ...
链接寄存器分析:
符号:malloc + 52
说明:我们已经确定链接寄存器(lr)很可能包含了帧#0的调用函数的返回地址,并将其作为帧#1插入到崩溃的线程的回溯中以帮助分析。 这一决定是通过应用启发式来确定崩溃函数是否可能在崩溃时创build了一个新的堆栈帧。
types:1
模拟碰撞非常容易。 我们用小块读取getAssetBytesCallback中ALAssetRepresentation的数据。 块的特定大小并不重要。 唯一重要的是调用callback约20次。
static size_t getAssetBytesCallback(void *info, void *buffer, off_t position, size_t count) { static int i = 0; ++i; ALAssetRepresentation *rep = (__bridge id)info; NSError *error = nil; NSLog(@"%d: off:%lld len:%zu", i, position, count); const size_t countRead = [rep getBytes:(uint8_t *)buffer fromOffset:position length:128 error:&error]; return countRead; }
这里是日志的尾巴
2014-03-21 11:21:14.250 MRCloudApp [3461:1303] 20:关:2432 len:2156064
MRCloudApp(3461,0×701000)malloc:*** mach_vm_map(size = 217636864)失败(错误代码= 3)
***错误:不能分配区域
***在malloc_error_break中设置一个断点来进行debugging
我介绍了一个计数器来防止这个崩溃。 你可以看到我的修复如下:
typedef struct { void *assetRepresentation; int decodingIterationCount; } ThumbnailDecodingContext; static const int kThumbnailDecodingContextMaxIterationCount = 16; static size_t getAssetBytesCallback(void *info, void *buffer, off_t position, size_t count) { ThumbnailDecodingContext *decodingContext = (ThumbnailDecodingContext *)info; ALAssetRepresentation *assetRepresentation = (__bridge ALAssetRepresentation *)decodingContext->assetRepresentation; if (decodingContext->decodingIterationCount == kThumbnailDecodingContextMaxIterationCount) { NSLog(@"WARNING: Image %@ is too large for thumbnail extraction.", [assetRepresentation url]); return 0; } ++decodingContext->decodingIterationCount; NSError *error = nil; size_t countRead = [assetRepresentation getBytes:(uint8_t *)buffer fromOffset:position length:count error:&error]; if (countRead == 0 || error != nil) { NSLog(@"ERROR: Failed to decode image %@: %@", [assetRepresentation url], error); return 0; } return countRead; } - (UIImage *)thumbnailForAsset:(ALAsset *)asset maxPixelSize:(CGFloat)size { NSParameterAssert(asset); NSParameterAssert(size > 0); ALAssetRepresentation *representation = [asset defaultRepresentation]; if (!representation) { return nil; } CGDataProviderDirectCallbacks callbacks = { .version = 0, .getBytePointer = NULL, .releaseBytePointer = NULL, .getBytesAtPosition = getAssetBytesCallback, .releaseInfo = NULL }; ThumbnailDecodingContext decodingContext = { .assetRepresentation = (__bridge void *)representation, .decodingIterationCount = 0 }; CGDataProviderRef provider = CGDataProviderCreateDirect((void *)&decodingContext, [representation size], &callbacks); NSParameterAssert(provider); if (!provider) { return nil; } CGImageSourceRef source = CGImageSourceCreateWithDataProvider(provider, NULL); NSParameterAssert(source); if (!source) { CGDataProviderRelease(provider); return nil; } CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(source, 0, (__bridge CFDictionaryRef) @{(NSString *)kCGImageSourceCreateThumbnailFromImageAlways : @YES, (NSString *)kCGImageSourceThumbnailMaxPixelSize : [NSNumber numberWithFloat:size], (NSString *)kCGImageSourceCreateThumbnailWithTransform : @YES}); UIImage *image = nil; if (imageRef) { image = [UIImage imageWithCGImage:imageRef]; CGImageRelease(imageRef); } CFRelease(source); CGDataProviderRelease(provider); return image; }