Ios 从ALAssetRepresentation生成自定义缩略图

Ios 从ALAssetRepresentation生成自定义缩略图,ios,thumbnails,cgimage,alassetslibrary,alasset,Ios,Thumbnails,Cgimage,Alassetslibrary,Alasset,我的主要问题是我需要为一个对象获取一个缩略图 我尝试了很多解决方案并搜索了数天的堆栈溢出,由于以下限制,我找到的所有解决方案都不适用于我: 我不能使用默认的缩略图,因为它太小了 我不能使用全屏或全分辨率图像,因为屏幕上有很多图像 我无法使用UIImage或UIImageView调整大小,因为这些加载 全分辨率图像 我无法在内存中加载图像,我正在处理20Mpx图像 我需要创建一个200x200px版本的原始资产加载到屏幕上 这是我附带的代码的最后一次迭代: #import <Assets

我的主要问题是我需要为一个对象获取一个缩略图

我尝试了很多解决方案并搜索了数天的堆栈溢出,由于以下限制,我找到的所有解决方案都不适用于我:

  • 我不能使用默认的缩略图,因为它太小了
  • 我不能使用全屏或全分辨率图像,因为屏幕上有很多图像
  • 我无法使用UIImage或UIImageView调整大小,因为这些加载 全分辨率图像
  • 我无法在内存中加载图像,我正在处理20Mpx图像
  • 我需要创建一个200x200px版本的原始资产加载到屏幕上
这是我附带的代码的最后一次迭代:

#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];
#导入
#进口
// ...
阿拉塞特*资产;
// ...
ALAssetRepresentation*assetRepresentation=[AssetDefaultRepresentation];
NSDictionary*thumbnailOptions=[NSDictionary Dictionary WithObjectsAndKeys:
(id)kCFBooleanTrue,KCGimageSourceCreateTumbNailWithTransform,
(id)kCFBooleanTrue,KCGimageSourceCreateTumbnailfromImageAlways,
(id)[NSNumber numberWithFloat:200],kCGImageSourceThumbnailMaxPixelSize,
零];
CGImageRef generatedThumbnail=[assetRepresentation CGImageWithOptions:thumbnailOptions];
UIImage*thumbnailImage=[UIImage imageWithCGImage:generatedThumbnail];
问题是,生成的
CGImageRef
既不按方向变换,也不按指定的最大像素大小变换

我还试图找到一种使用
CGImageSource
调整大小的方法,但是:

  • 资产url不能在
    CGImageSourceCreateWithURL:
    中使用
  • 我无法从
    ALAsset
    ALAssetRepresentation
    a
    CGDataProviderRef
    中提取要与
    CGImageSourceCreateWithDataProvider:
    一起使用的内容
  • CGImageSourceCreateWithData:
    要求我将全分辨率或全屏资源存储在内存中才能工作
我错过什么了吗

是否有其他方法可以从
ALAsset
ALAssetRepresentation
获取我缺少的自定义缩略图


提前感谢。

您可以使用
CGImageSourceCreateThumbnailAtIndex
从可能较大的图像源创建小图像。您可以使用
ALAssetRepresentation
getBytes:fromOffset:length:error:
方法从磁盘加载图像,并使用该方法创建CGImageSourceRef

然后,只需将
kCGImageSourceThumbnailMaxPixelSize
kCGImageSourceCreateThumbnailFromImageAlways
选项传递给所创建的图像源的
CGImageSourceCreateThumbnailAtIndex
,它将为您创建一个较小的版本,而无需将庞大的版本加载到内存中

我已经写了一篇文章,用这项技术充实了全文。

中提到的方法有一个问题。如果资产太大,您的应用程序将使用以下堆栈崩溃:

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)很可能包含frame#0调用函数的返回地址,并已将其作为frame#1插入崩溃线程的回溯以帮助分析。通过应用启发式来确定崩溃函数是否可能在崩溃时创建了一个新的堆栈帧,从而进行此确定

类型:1

模拟碰撞非常容易。让我们用一小块从GetAssetByteScalBack中的ALAssetRepresentation读取数据。块的特定大小并不重要。唯一重要的是打20次电话给callback

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:off:2432 len:215064

MRCloudApp(3461,0x701000)malloc:**mach_vm_映射(大小=217636864)失败(错误代码=3)

***错误:无法分配区域

***在malloc\u error\u 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;
}

+1.文档不正确地指出kCGImageSourceThumbnailMaxPixelSize适用于此。没有。请看我的答案。将imageview内容模式设置为UIViewContentModeScaleSpectFit,例如:imageview.contentMode=UIViewContentModeScaleSpectFit;并使用dispatch\u sync(dispatch\u get\u main\u queue()对于与UI相关的工作。AlassetLibrary块将在单独的线程中执行。因此,我建议在主线程中执行与UI相关的工作。是否可以创建非方形缩略图?例如,200*150 px?@alex,我认为这不会生成方形缩略图。
kCGImageSourceThumbnailMaxPixelSize
指的是较大一侧的大小(宽度或高度);缩略图将与原始图像具有相同的外观。@JesseRusak感谢您的帖子,但如何处理已编辑的资源(裁剪、红眼调整或过滤的资源)?@NR4TR据我所知,经过编辑的资产与未经编辑的资产的工作方式相同。你看到了不同的东西吗?@JesseRusak,完成了-谢谢!我正试图在Swift中复制这一点。我对它需要的CGDataProvider上下文感到特别困难。有什么想法吗?看起来你应该将该实现打包到一些Obj中选择C类,然后在Swift中使用它。这是显而易见的,但我认为没有其他方法。