1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/chromeos/login/user_image_loader.h" 6 7 #include "base/bind.h" 8 #include "base/file_util.h" 9 #include "base/files/file_path.h" 10 #include "base/memory/scoped_ptr.h" 11 #include "base/message_loop/message_loop.h" 12 #include "base/threading/worker_pool.h" 13 #include "chrome/browser/chromeos/login/helper.h" 14 #include "chrome/browser/chromeos/login/user_image.h" 15 #include "content/public/browser/browser_thread.h" 16 #include "skia/ext/image_operations.h" 17 #include "third_party/skia/include/core/SkBitmap.h" 18 #include "ui/gfx/codec/png_codec.h" 19 #include "ui/gfx/skbitmap_operations.h" 20 21 using content::BrowserThread; 22 23 namespace chromeos { 24 25 UserImageLoader::ImageInfo::ImageInfo(int size, 26 const LoadedCallback& loaded_cb) 27 : size(size), 28 loaded_cb(loaded_cb) { 29 } 30 31 UserImageLoader::ImageInfo::~ImageInfo() { 32 } 33 34 UserImageLoader::UserImageLoader(ImageDecoder::ImageCodec image_codec) 35 : target_message_loop_(NULL), 36 image_codec_(image_codec) { 37 } 38 39 UserImageLoader::~UserImageLoader() { 40 } 41 42 void UserImageLoader::Start(const std::string& filepath, 43 int size, 44 const LoadedCallback& loaded_cb) { 45 base::SequencedWorkerPool* pool = BrowserThread::GetBlockingPool(); 46 SequenceToken sequence_token = pool->GetSequenceToken(); 47 Start(filepath, size, sequence_token, loaded_cb); 48 } 49 50 void UserImageLoader::Start(const std::string& filepath, 51 int size, 52 const SequenceToken& token, 53 const LoadedCallback& loaded_cb) { 54 target_message_loop_ = base::MessageLoop::current(); 55 56 ImageInfo image_info(size, loaded_cb); 57 base::SequencedWorkerPool* pool = BrowserThread::GetBlockingPool(); 58 scoped_refptr<base::SequencedTaskRunner> task_runner = pool-> 59 GetSequencedTaskRunnerWithShutdownBehavior(token, 60 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); 61 task_runner->PostTask( 62 FROM_HERE, 63 base::Bind(&UserImageLoader::LoadImage, this, filepath, image_info, 64 task_runner)); 65 } 66 67 void UserImageLoader::LoadImage( 68 const std::string& filepath, 69 const ImageInfo& image_info, 70 scoped_refptr<base::SequencedTaskRunner> task_runner) { 71 DCHECK(task_runner->RunsTasksOnCurrentThread()); 72 73 std::string image_data; 74 file_util::ReadFileToString(base::FilePath(filepath), &image_data); 75 76 scoped_refptr<ImageDecoder> image_decoder = 77 new ImageDecoder(this, image_data, image_codec_); 78 { 79 base::AutoLock lock(lock_); 80 image_info_map_.insert(std::make_pair(image_decoder.get(), image_info)); 81 } 82 image_decoder->Start(task_runner); 83 } 84 85 void UserImageLoader::OnImageDecoded(const ImageDecoder* decoder, 86 const SkBitmap& decoded_image) { 87 ImageInfoMap::iterator info_it; 88 scoped_ptr<ImageInfo> image_info; 89 { 90 base::AutoLock lock(lock_); 91 info_it = image_info_map_.find(decoder); 92 if (info_it == image_info_map_.end()) { 93 NOTREACHED(); 94 return; 95 } 96 image_info.reset( 97 new ImageInfo(info_it->second.size, info_it->second.loaded_cb)); 98 image_info_map_.erase(info_it); 99 } 100 101 SkBitmap final_image = decoded_image; 102 103 if (image_info->size > 0) { 104 // Auto crop the image, taking the largest square in the center. 105 int size = std::min(decoded_image.width(), decoded_image.height()); 106 int x = (decoded_image.width() - size) / 2; 107 int y = (decoded_image.height() - size) / 2; 108 SkBitmap cropped_image = 109 SkBitmapOperations::CreateTiledBitmap(decoded_image, x, y, size, size); 110 if (size > image_info->size) { 111 // Also downsize the image to save space and memory. 112 final_image = 113 skia::ImageOperations::Resize(cropped_image, 114 skia::ImageOperations::RESIZE_LANCZOS3, 115 image_info->size, 116 image_info->size); 117 } else { 118 final_image = cropped_image; 119 } 120 } 121 // Make the SkBitmap immutable as we won't modify it. This is important 122 // because otherwise it gets duplicated during painting, wasting memory. 123 final_image.setImmutable(); 124 gfx::ImageSkia final_image_skia = 125 gfx::ImageSkia::CreateFrom1xBitmap(final_image); 126 final_image_skia.MakeThreadSafe(); 127 UserImage user_image(final_image_skia, decoder->get_image_data()); 128 if (image_codec_ == ImageDecoder::ROBUST_JPEG_CODEC) 129 user_image.MarkAsSafe(); 130 target_message_loop_->PostTask( 131 FROM_HERE, 132 base::Bind(image_info->loaded_cb, user_image)); 133 } 134 135 void UserImageLoader::OnDecodeImageFailed(const ImageDecoder* decoder) { 136 ImageInfoMap::iterator info_it; 137 scoped_ptr<ImageInfo> image_info; 138 { 139 base::AutoLock lock(lock_); 140 info_it = image_info_map_.find(decoder); 141 if (info_it == image_info_map_.end()) { 142 NOTREACHED(); 143 return; 144 } 145 image_info.reset( 146 new ImageInfo(info_it->second.size, info_it->second.loaded_cb)); 147 image_info_map_.erase(decoder); 148 } 149 150 target_message_loop_->PostTask( 151 FROM_HERE, 152 base::Bind(image_info->loaded_cb, UserImage())); 153 } 154 155 } // namespace chromeos 156