我的主要问题是我需要获取ALAsset对象的缩略图。
我尝试了很多解决方案并搜索了堆栈溢出数天,由于这些限制,我找到的所有解决方案都不适用于我:
这是我附带的代码的最后一次迭代:
#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
既不会被方向转换,也不会转换为指定的最大像素大小;
我还试图找到一种使用CGImageSource
调整大小的方法,但是:
CGImageSourceCreateWithURL:
; ALAsset
或ALAssetRepresentation
CGDataProviderRef
中提取出CGImageSourceCreateWithDataProvider:
; CGImageSourceCreateWithData:
要求我将fullResolution或fullscreen资源存储在内存中才能正常工作。我错过了什么吗?
是否有另一种方法可以从ALAsset
或ALAssetRepresentation
获取我缺少的自定义缩略图?
提前致谢。
答案 0 :(得分:25)
您可以使用CGImageSourceCreateThumbnailAtIndex
从可能较大的图像源创建小图像。您可以使用ALAssetRepresentation
的{{1}}方法从磁盘加载图像,并使用它创建CGImageSourceRef。
然后,您只需将getBytes:fromOffset:length:error:
和kCGImageSourceThumbnailMaxPixelSize
选项与您创建的图像源一起传递给kCGImageSourceCreateThumbnailFromImageAlways
,它将为您创建一个较小的版本,而无需加载巨大的版本到内存中。
答案 1 :(得分:4)
this提到的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插入到崩溃线程的回溯中以帮助分析。通过应用启发式来确定崩溃功能是否可能在崩溃时创建了新的堆栈帧来进行此确定。
类型:1
模拟崩溃非常容易。让我们使用一个小块从getAssetBytesCallback中读取ALAssetRepresentation中的数据。块的特定大小并不重要。唯一重要的是召回回调约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,0x701000)malloc:*** mach_vm_map(size = 217636864)失败(错误代码= 3)
***错误:无法分配区域
***在malloc_error_break中设置断点以进行调试
我介绍了一个防止这次崩溃的计数器。你可以在下面看到我的修复:
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;
}