2012-08-01 11 views
13

私の主な問題は、私はALAssetオブジェクトのサムネイルを取得する必要があることです。ALAssetRepresentationからカスタムサムネイルを生成

私はソリューションの多くを試してみましたが、日のため、スタックオーバーフローを検索、私が見つけたすべてのソリューションが原因これらの制約に私のために働いていません。

  • それはあまりにも少ないですので、私はデフォルトのサムネイルを使用することはできません;
  • 私は画面にたくさんの画像があるので、fullScreenまたはfullResolution画像を使用することはできません。
  • 私はそれらの負荷 フル解像度の画像
  • 私はメモリ内の画像をロードすることはできませんので、サイズ変更のためにUIImageまたはUIImageViewを使用することはできません、私は20Mpx画像で働いています。
  • 画面に読み込む元のアセットの200x200 pxバージョンを作成する必要があります。

これは私がしてきたコードの最後の繰り返しである:

#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を使用してリサイズする方法を見つけることを試みたが、:

  • 資産URLがCGImageSourceCreateWithURL:で使用することはできません。
  • ALAssetまたはALAssetRepresentationからCGDataProviderRefを抽出することはできません。CGImageSourceCreateWithDataProvider:で使用します。
  • CGImageSourceCreateWithData:を使用するには、fullResolutionまたはフルスクリーンのアセットをメモリに保存して作業する必要があります。

私に何か不足していますか?

ALAssetまたはALAssetRepresentationからカスタムサムネイルを取得する別の方法がありますか?

ありがとうございます。

+2

+1:あなたは以下の私の修正を見ることができます。ドキュメントは、kCGImageSourceThumbnailMaxPixelSizeがこれに対して機能することを述べている間違っています。それはしません。 – akaru

+0

ここに私の答えを参照してくださいhttp://stackoverflow.com/questions/8116524/the-best-way-to-get-thumbnails-with-alassetslibrary/13598533#13598533 UIViewContentModeScaleAspectFitなどとしてあなたImageViewのコンテンツモードを設定します。imageView.contentMode = UIViewContentModeScaleAspectFit ;そして、それは正方形のサムネイルを作成しないことが可能であるので、私はメインスレッド –

答えて

25

CGImageSourceCreateThumbnailAtIndexを使用すると、潜在的に大きな画像ソースから小さな画像を作成できます。 ALAssetRepresentationgetBytes:fromOffset:length:error:メソッドを使用してディスクからイメージをロードし、それを使ってCGImageSourceRefを作成することができます。

kCGImageSourceThumbnailMaxPixelSizekCGImageSourceCreateThumbnailFromImageAlwaysオプションを作成した画像ソースでCGImageSourceCreateThumbnailAtIndexに渡すだけで、巨大なバージョンをメモリにロードせずに小さなバージョンが作成されます。

私はblog postgistと書かれています。このテクニックは完全に融合しています。

+0

にUI関連の低いものを行うことをお勧めします。別のスレッドで実行されます(UI関連works.ALAssetsLibraryブロックのdispatch_get_main_queueを()dispatch_syncでしょう?つまり200 * 150ピクセルの? – alex

+1

@アレックスは、私は、これは正方形のサムネイルを生成しないと思う 'kCGImageSourceThumbnailMaxPixelSize'の方が大きい側(幅または高さ)のサイズを指し;。。サムネイルは、元の画像と同じ縦横になります –

+0

THK、私はそれを試してみましょう(これらのトリミング、赤目調整、またはフィルタリング)あなたは私にあなたのポストのための – alex

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 
... 

Link Register Analysis:

Symbol: malloc + 52

Description: We have determined that the link register (lr) is very likely to contain the return address of frame #0's calling function, and have inserted it into the crashing thread's backtrace as frame #1 to aid in analysis. This determination was made by applying a heuristic to determine whether the crashing function was likely to have created a new stack frame at the time of the crash.

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

MRCloudApp(3461,0x701000) malloc: *** mach_vm_map(size=217636864) failed (error code=3)

*** error: can't allocate region

*** set a breakpoint in malloc_error_break to debug

です。

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; 
} 
+0

私はこれをSwiftで再現しようとしています。必要なCGDataProviderコンテキストでは特に苦労しています。何か案は? – Unome

+0

Objective Cクラスにその実装をラップし、それをSwiftで使用する必要があるようです。それは明らかですが、他の方法はないと思います。 –

関連する問題