2013-03-28 2 views
2

У меня есть собственный класс загрузчика изображений, он содержит очередь и загружает изображения один (или определенное количество) за раз, записывает их в папку кэша и извлекает их из папки кеша, когда необходимо. У меня также есть подкласс UIImageView, к которому я могу передать URL-адрес, через класс загрузчика изображений он будет выглядеть, если изображение уже существует на устройстве и покажет его, если оно есть, или загрузите и покажите его после его завершения.UIImage to raw NSData/избежать сжатия

После завершения загрузки изображения я делаю следующее. Я создаю UIImage из загруженных NSData, сохраняю загруженные NSData на диск и возвращаю UIImage.

// This is executed in a background thread 
downloadedImage = [UIImage imageWithData:downloadedData]; 
BOOL saved = [fileManager createFileAtPath:filePath contents:downloadedData attributes:attributes]; 
// Send downloadedImage to the main thread and do something with it 

Чтобы получить существующее изображение, я делаю это.

// This is executed in a background thread 
if ([fileManager fileExistsAtPath:filePath]) 
{ 
    NSData* imageData = [fileManager contentsAtPath:filePath]; 
    retrievedImage = [UIImage imageWithData:imageData]; 
    // Send retrievedImage to the main thread and do something with it 
} 

Как вы можете видеть, я всегда создавать UIImage непосредственно из загруженного NSData, я никогда не создавать NSData с помощью UIImagePNGRepresentation так что изображение никогда не будет сжиматься. Когда вы создаете UIImage из сжатых NSData, UIImage распаковывает его прямо перед рендерингом в основном потоке и тем самым блокирует пользовательский интерфейс. Поскольку у меня теперь есть UITableView с тонны маленьких изображений, которые нужно загрузить или извлечь с диска, это было бы неприемлемо, так как это сильно замедляло бы мою прокрутку.

Теперь моя проблема. Пользователь также может выбрать фотографию из рулона камеры, сохранить ее, и она также должна появиться в моем UITableView. Но я не могу найти способ превратить UIImage из рулона камеры в NSData, не используя UIImagePNGRпредставление. Итак, вот мой вопрос.

Как я могу конвертировать UIImage в несжатые NSData, чтобы впоследствии преобразовать его обратно в UIImage с помощью imageWithData, чтобы его не нужно было распаковать перед рендерингом?

или

Есть ли способ, что я могу сделать декомпрессию перед отправкой UIImage в основной поток и кэшировать поэтому он имеет только распаковывать один раз?

Заранее спасибо.

ответ

2

Как я могу преобразовать UIImage в несжатом NSData, так что я могу преобразовать его обратно в UIImage позже, используя imageWithData так, что он не должен быть распакованы перед визуализацией?

Что вы действительно просят здесь, я понимаю, как сохранить UIImage на диске таким образом, что вы можете позже прочитать UIImage с диска как можно быстрее. Вам все равно, хранится ли она как NSData; вы просто хотите быстро прочитать его. Я предлагаю вам использовать среду ImageIO. Сохраните в качестве места назначения изображения и выберите позже с помощью источника изображения.

http://developer.apple.com/library/ios/#documentation/GraphicsImaging/Conceptual/ImageIOGuide/ikpg_dest/ikpg_dest.html

Есть ли способ, что я могу сделать декомпрессию перед отправкой UIImage в основной поток и кэшировать поэтому он имеет только распаковывать один раз?

Да, хороший вопрос. Это будет мое второе предложение: используйте потоки. Это то, что люди постоянно делают со столами. Когда таблица запрашивает изображение, у вас либо есть изображение уже, либо нет.Если вы этого не сделаете, вы получите изображение наполнителя и в фоновом режиме получите реальное изображение. Когда реальное изображение будет готово, вы договорились получить уведомление. Вернемся к основному потоку, и вы скажете в представлении таблицы снова запросить данные для этой строки; на этот раз у вас есть изображение, и вы его поставте. Таким образом, пользователь увидит небольшую задержку до появления изображения. Я уверен, что вы видели множество приложений, которые ведут себя таким образом (New York Times - хороший пример).

У меня есть еще одно предложение, и это может быть лучше всего. Вы говорите об этом, требуя времени для декомпрессии изображения с диска. Но это займет совсем немного времени, если изображение мало. Но образ должен быть быть маленьким, потому что он собирается зайти в небольшое место - ячейку таблицы. Другими словами, вы должны сжать изображения заранее, когда вы их сначала получите, чтобы вы были готовы с маленькими версиями каждого изображения, когда его спросили. Это огромная трата времени и памяти для обеспечения большого изображения, которое должно входить в небольшое пространство.

ADDED LATER: Конечно, вы понимаете, что много этого беспокойства было бы лишним, если бы вы не сохраняли изображения на диск. Я не совсем понимаю, зачем вам это нужно. Надеюсь, у вас есть все основания для этого; но это намного быстрее, очевидно, если вы просто держите изображения в памяти.

+0

Привет, спасибо за Ваш ответ. Я уже использую фоновый поток для загрузки изображений. Изображения представляют собой размер, который они отображаются в таблицеView, поэтому они могут быть как можно меньше. И когда они загружаются с диска, я сохраняю их в памяти столько, сколько могу. Но этого все еще не хватало, когда прокручивалось через мой стол. Декомпрессия занимала около 1/3 времени. Я использовал ваше предложение для использования ImageIO, в CGImageSourceCreateImageAtIndex я установил kCGImageSourceShouldCache в YES. Теперь он сохраняет несжатую версию в памяти, и она работает быстрее. Благодарю. – David

+0

Очень приятно! Я все еще немного обеспокоен; Я надеюсь, что вы проверите с помощью инструментов и убедитесь, что все хорошо, когда пользователь прокручивает представление таблицы на самом устройстве. – matt

+0

Я использовал профилировщик времени, на самом деле, как я узнал, что именно распаковка вызвала такие огромные задержки. copyImageBlockSetPNG и png_read_now были функциями, занимающими 1/3 времени. Я тестирую iPhone 3GS и iPod Touch 4G, два самых медленных устройства, поддерживаемых нашим приложением. Даже если я отключу кэширование и загружаю изображения с диска каждый раз, когда они становятся видимыми, я могу лишь немного увидеть пустой образ перед тем, как будет отображаться изображение, но пользовательский интерфейс вообще не висит. И при включенном кешировании это совершенно незаметно. Это получилось великолепно, поэтому еще раз спасибо. – David

0

ЕСЛИ вы загружаете данные и сохраняете их на диске, тогда данные сжимаются в формате PNG, JPEG или GIF. Вы не собираетесь загружать несжатые данные изображения. Итак, сначала нужно решить корень вашего вопроса о выполнении декомпрессии, прежде чем сохранять файл на диск. Декомпрессия перед сохранением сделает файл намного больше, но это означает, что декомпрессии не требуется, прежде чем данные будут прочитаны в CGImageRef или UIImage. Это загрузка, а затем распаковка нескольких изображений, которые замедляют работу вашего процессора и замедляют прокрутку. Но это не решение просто держать все в памяти уже распакованным, потому что это будет использовать всю память приложения и вскоре выскочит на ваш телефон. Возможно, вам удастся уйти от него за небольшое количество изображений, но это основной недостаток дизайна, который вам нужно решить при первом написании кода. Если вам нравится, вы можете посмотреть мое сообщение в блоге по этой теме video-and-memory-usage-on-ios-devices, сообщение посвящено видео, но у вас есть такая же проблема, когда речь идет о множестве разных изображений. Я бы предположил, что вы пишете свои маленькие изображения на диск в несжатом формате, таком как TIFF или BMP, так что чтение их обратно легко, пока ImageIO поддерживает этот конкретный формат.

1

Я нашел решение:

CGImageRef downloadedImageRef = downloadedImage.CGImage; 
CGDataProviderRef provider = CGImageGetDataProvider(downloadedImageRef); 
NSData *data = CFBridgingRelease(CGDataProviderCopyData(provider)); 
// Then you can save the data