Ускорьте создание UIImage из SpriteSheet

Я не совсем уверен, что правильно понял название, но я не уверен, в чем именно заключается моя проблема.

Мне нужно загрузить массив UIImages из таблицы спрайтов, которые я затем буду использовать в качестве анимации в UIImageView.

Таблица спрайтов создается с помощью TexturePacker, который генерирует огромный атлас (2048x2048) и json с описаниями спрайтов.

До сих пор он работал без проблем, даже загружая 100 кадров всего за 0,5-0,8 секунды, что меня очень устраивало.

Проблема в том, что теперь мне нужно загрузить спрайт-листы из папки «Документы» (они загружены, поэтому их нельзя интегрировать в приложение), поэтому я должен использовать UIImage imageWithContentsOfFile вместо UIImage imagenamed. Это увеличивает время загрузки на 5-15 секунд в зависимости от количества кадров в анимации. Я предполагаю, что проблема в том, что, поскольку изображение не кэшируется, оно загружается для каждого кадра, но я не понимаю, почему

Это код:

// With this line, perfect
//UIImage *atlas = [UIImage imageNamed:@"media/spritesheets/[email protected]"];
// But with this one, incredibly looooong loading times
UIImage *atlas = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"media/spritesheets/default-pro7@2x" ofType:@"png"]];

CGImageRef atlasCGI = [atlas CGImage];

for( NSDictionary* dict in frames) {

    NSDictionary *frame = [dict objectForKey:@"frame"];
    int x = [[frame objectForKey:@"x"] intValue];
    int y = [[frame objectForKey:@"y"] intValue];
    int width = [[frame objectForKey:@"w"] intValue];
    int height = [[frame objectForKey:@"h"] intValue];

    NSDictionary *spriteSize = [dict objectForKey:@"spriteSourceSize"];
    int realx = [[spriteSize objectForKey:@"x"] intValue];

    NSDictionary *size = [dict objectForKey:@"sourceSize"];
    int realWidth = [[size objectForKey:@"w"] intValue];
    int realHeight = [[size objectForKey:@"h"] intValue];

    //Initialize the canvas size!
    canvasSize = CGSizeMake(realWidth, realHeight);

    bitmapBytesPerRow   = (canvasSize.width * 4);
    bitmapByteCount     = (bitmapBytesPerRow * canvasSize.height);

    bitmapData = malloc( bitmapByteCount );

    //Create the context
    context = CGBitmapContextCreate(bitmapData, canvasSize.width, canvasSize.height, 8, bitmapBytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast);

    // Clear background
    CGContextClearRect(context,CGRectMake(0.0f,0.0f,canvasSize.width,canvasSize.height));

    // Get spriteRect
    CGRect cropRect = CGRectMake(x, y, width, height);
    CGImageRef imageRef = CGImageCreateWithImageInRect(atlasCGI, cropRect);

    //Draw the image into the bitmap context
    CGContextDrawImage(context, CGRectMake(realx, 0, width, height), imageRef);

    //Get the result image
    resultImage = CGBitmapContextCreateImage(context);

    UIImage *result = result = [UIImage imageWithCGImage:resultImage];

    [images addObject:result];

    //Cleanup
    free(bitmapData);
    CGImageRelease(resultImage);
    CGImageRelease(imageRef);
}

Я даже могу попробовать загружать сеансы системного профилирования, которые показывают, где загрузка занимает так много времени.

введите здесь описание изображения


person asendra    schedule 08.03.2013    source источник
comment
Почему кеширование может быть проблемой, если вы загрузили изображение атласа только один раз?   -  person Till    schedule 09.03.2013
comment
Я действительно не знаю, но это единственная известная мне разница между UIImage imagenamed и UIImage imageWithContentsOfFile. (Ну, это, а также то, что imageWithContentsOfFile может загружать изображения из-за пределов набора приложений, поэтому мне это нужно в первую очередь.)   -  person asendra    schedule 09.03.2013


Ответы (1)


Ну наконец-то я понял, в чем проблема. После более внимательного изучения профилировщика Instruments Time я заметил, что в copyImageBlockSetPNG тратится много времени, что сначала заставило меня поверить, что я загружаю изображение с диска для каждого кадра. Оказывается, у кого-то уже была эта проблема fly?, и оказалось, что я загружал память каждый кадр, но не с диска, вместо этого он распаковывался из памяти в формате png для каждого кадра.

В этом посте есть решение, заключающееся в записи изображения в контекст и получении из него несжатой копии изображения.

Это работает, но я нашел другой способ, который, как мне кажется, немного эффективнее.

NSDictionary *dict = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES]
                                             forKey:(id)kCGImageSourceShouldCache];
NSData *imageData = [NSData dataWithContentsOfFile:@"path/to/image.png"]];
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)(imageData), NULL);
CGImageRef atlasCGI = CGImageSourceCreateImageAtIndex(source, 0, (__bridge CFDictionaryRef)dict);
CFRelease(source);

Надеюсь, поможет!

person asendra    schedule 10.03.2013