13

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

私は多くの解決策を試し、スタックオーバーフローを何日も検索しましたが、これらの制約のために、見つけたすべての解決策が機能していません:

  • デフォルトのサムネイルは小さすぎるため使用できません。
  • 画面にたくさんの画像があるため、fullScreenまたはfullResolution画像を使用できません。
  • UIImageまたはUIImageViewを使用してサイズを変更することはできません。これらは、fullResolutionイメージをロードするためです。
  • 画像をメモリに読み込めません。20Mpxの画像を使用しています。
  • 画面にロードするには、元のアセットの200x200ピクセルバージョンを作成する必要があります。

これは私が付属したコードの最後の反復です:

#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:アセットのURLは;で使用できません。
  • から抽出しALAssetたりALAssetRepresentationCGDataProviderRefで使用したりすることはできませんCGImageSourceCreateWithDataProvider:
  • CGImageSourceCreateWithData:動作させるには、fullResolutionまたはfullscreenアセットをメモリに保存する必要があります。

私は何かが足りないのですか?

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

前もって感謝します。

4

2 に答える 2

25

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

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

私はブログ投稿を書き、このテクニックを完全に具体化した要点を書きました。

于 2012-12-18T21:00:53.987 に答える
4

JesseRusakが言及したこのアプローチには問題があります。アセットが大きすぎると、アプリは次のスタックでクラッシュします。

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:オフ:2432len: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;
}
于 2014-03-21T11:53:32.863 に答える