OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/extensions/image_loading_tracker.h" | 5 #include "chrome/browser/extensions/image_loading_tracker.h" |
6 | 6 |
7 #include "base/file_util.h" | 7 #include "base/file_util.h" |
8 #include "base/logging.h" | |
9 #include "base/message_loop.h" | |
10 #include "base/scoped_ptr.h" | |
11 #include "base/task.h" | |
12 #include "base/thread.h" | |
13 #include "chrome/browser/chrome_thread.h" | 8 #include "chrome/browser/chrome_thread.h" |
14 #include "chrome/common/extensions/extension_resource.h" | 9 #include "chrome/common/extensions/extension_resource.h" |
15 #include "gfx/size.h" | |
16 #include "skia/ext/image_operations.h" | 10 #include "skia/ext/image_operations.h" |
17 #include "third_party/skia/include/core/SkBitmap.h" | 11 #include "third_party/skia/include/core/SkBitmap.h" |
18 #include "webkit/glue/image_decoder.h" | 12 #include "webkit/glue/image_decoder.h" |
19 | 13 |
20 //////////////////////////////////////////////////////////////////////////////// | 14 //////////////////////////////////////////////////////////////////////////////// |
21 // ImageLoadingTracker::LoadImageTask | 15 // ImageLoadingTracker::ImageLoader |
22 | 16 |
23 // The LoadImageTask is for asynchronously loading the image on the file thread. | 17 // A RefCounted class for loading images on the File thread and reporting back |
24 // If the image is successfully loaded and decoded it will report back on the | 18 // on the UI thread. |
25 // calling thread to let the caller know the image is done loading. | 19 class ImageLoadingTracker::ImageLoader |
26 class ImageLoadingTracker::LoadImageTask : public Task { | 20 : public base::RefCountedThreadSafe<ImageLoader> { |
27 public: | 21 public: |
28 // Constructor for the LoadImageTask class. |tracker| is the object that | 22 explicit ImageLoader(ImageLoadingTracker* tracker) |
29 // we use to communicate back to the entity that wants the image after we | 23 : tracker_(tracker) { |
30 // decode it. |path| is the path to load the image from. |max_size| is the | |
31 // maximum size for the loaded image. It will be resized to fit this if | |
32 // larger. |index| is an identifier for the image that we pass back to the | |
33 // caller. | |
34 LoadImageTask(ImageLoadingTracker* tracker, | |
35 const ExtensionResource& resource, | |
36 const gfx::Size& max_size, | |
37 size_t index) | |
38 : tracker_(tracker), | |
39 resource_(resource), | |
40 max_size_(max_size), | |
41 index_(index) { | |
42 CHECK(ChromeThread::GetCurrentThreadIdentifier(&callback_thread_id_)); | 24 CHECK(ChromeThread::GetCurrentThreadIdentifier(&callback_thread_id_)); |
| 25 DCHECK(!ChromeThread::CurrentlyOn(ChromeThread::FILE)); |
43 } | 26 } |
44 | 27 |
45 void ReportBack(SkBitmap* image) { | 28 // Lets this class know that the tracker is no longer interested in the |
46 ChromeThread::PostTask( | 29 // results. |
47 callback_thread_id_, FROM_HERE, | 30 void StopTracking() { |
48 NewRunnableMethod( | 31 tracker_ = NULL; |
49 tracker_, &ImageLoadingTracker::OnImageLoaded, image, index_)); | |
50 } | 32 } |
51 | 33 |
52 virtual void Run() { | 34 // Instructs the loader to load a task on the File thread. |
| 35 void LoadImage(const ExtensionResource& resource, |
| 36 const gfx::Size& max_size) { |
| 37 DCHECK(!ChromeThread::CurrentlyOn(ChromeThread::FILE)); |
| 38 ChromeThread::PostTask( |
| 39 ChromeThread::FILE, FROM_HERE, |
| 40 NewRunnableMethod(this, &ImageLoader::LoadOnFileThread, resource, |
| 41 max_size)); |
| 42 } |
| 43 |
| 44 void LoadOnFileThread(ExtensionResource resource, |
| 45 const gfx::Size& max_size) { |
| 46 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); |
| 47 |
53 // Read the file from disk. | 48 // Read the file from disk. |
54 std::string file_contents; | 49 std::string file_contents; |
55 FilePath path = resource_.GetFilePath(); | 50 FilePath path = resource.GetFilePath(); |
56 if (path.empty() || !file_util::ReadFileToString(path, &file_contents)) { | 51 if (path.empty() || !file_util::ReadFileToString(path, &file_contents)) { |
57 ReportBack(NULL); | 52 ReportBack(NULL, resource); |
58 return; | 53 return; |
59 } | 54 } |
60 | 55 |
61 // Decode the image using WebKit's image decoder. | 56 // Decode the image using WebKit's image decoder. |
62 const unsigned char* data = | 57 const unsigned char* data = |
63 reinterpret_cast<const unsigned char*>(file_contents.data()); | 58 reinterpret_cast<const unsigned char*>(file_contents.data()); |
64 webkit_glue::ImageDecoder decoder; | 59 webkit_glue::ImageDecoder decoder; |
65 scoped_ptr<SkBitmap> decoded(new SkBitmap()); | 60 scoped_ptr<SkBitmap> decoded(new SkBitmap()); |
66 *decoded = decoder.Decode(data, file_contents.length()); | 61 *decoded = decoder.Decode(data, file_contents.length()); |
67 if (decoded->empty()) { | 62 if (decoded->empty()) { |
68 ReportBack(NULL); | 63 ReportBack(NULL, resource); |
69 return; // Unable to decode. | 64 return; // Unable to decode. |
70 } | 65 } |
71 | 66 |
72 if (decoded->width() > max_size_.width() || | 67 if (decoded->width() > max_size.width() || |
73 decoded->height() > max_size_.height()) { | 68 decoded->height() > max_size.height()) { |
74 // The bitmap is too big, re-sample. | 69 // The bitmap is too big, re-sample. |
75 *decoded = skia::ImageOperations::Resize( | 70 *decoded = skia::ImageOperations::Resize( |
76 *decoded, skia::ImageOperations::RESIZE_LANCZOS3, | 71 *decoded, skia::ImageOperations::RESIZE_LANCZOS3, |
77 max_size_.width(), max_size_.height()); | 72 max_size.width(), max_size.height()); |
78 } | 73 } |
79 | 74 |
80 ReportBack(decoded.release()); | 75 ReportBack(decoded.release(), resource); |
| 76 } |
| 77 |
| 78 void ReportBack(SkBitmap* image, const ExtensionResource& resource) { |
| 79 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); |
| 80 |
| 81 ChromeThread::PostTask( |
| 82 callback_thread_id_, FROM_HERE, |
| 83 NewRunnableMethod(this, &ImageLoader::ReportOnUIThread, |
| 84 image, resource)); |
| 85 } |
| 86 |
| 87 void ReportOnUIThread(SkBitmap* image, ExtensionResource resource) { |
| 88 DCHECK(!ChromeThread::CurrentlyOn(ChromeThread::FILE)); |
| 89 |
| 90 if (tracker_) |
| 91 tracker_->OnImageLoaded(image, resource); |
| 92 |
| 93 if (image) |
| 94 delete image; |
81 } | 95 } |
82 | 96 |
83 private: | 97 private: |
| 98 // The tracker we are loading the image for. If NULL, it means the tracker is |
| 99 // no longer interested in the reply. |
| 100 ImageLoadingTracker* tracker_; |
| 101 |
84 // The thread that we need to call back on to report that we are done. | 102 // The thread that we need to call back on to report that we are done. |
85 ChromeThread::ID callback_thread_id_; | 103 ChromeThread::ID callback_thread_id_; |
86 | 104 |
87 // The object that is waiting for us to respond back. | 105 DISALLOW_COPY_AND_ASSIGN(ImageLoader); |
88 ImageLoadingTracker* tracker_; | |
89 | |
90 // The image resource to load asynchronously. | |
91 ExtensionResource resource_; | |
92 | |
93 // The max size for the loaded image. | |
94 gfx::Size max_size_; | |
95 | |
96 // The index of the icon being loaded. | |
97 size_t index_; | |
98 }; | 106 }; |
99 | 107 |
100 //////////////////////////////////////////////////////////////////////////////// | 108 //////////////////////////////////////////////////////////////////////////////// |
101 // ImageLoadingTracker | 109 // ImageLoadingTracker |
102 | 110 |
103 void ImageLoadingTracker::PostLoadImageTask(const ExtensionResource& resource, | 111 ImageLoadingTracker::ImageLoadingTracker(Observer* observer) |
104 const gfx::Size& max_size) { | 112 : observer_(observer), |
105 ChromeThread::PostTask( | 113 responses_(0) { |
106 ChromeThread::FILE, FROM_HERE, | |
107 new LoadImageTask(this, resource, max_size, posted_count_++)); | |
108 } | 114 } |
109 | 115 |
110 void ImageLoadingTracker::OnImageLoaded(SkBitmap* image, size_t index) { | 116 ImageLoadingTracker::~ImageLoadingTracker() { |
111 if (observer_) | 117 // The loader is created lazily and is NULL if the tracker is destroyed before |
112 observer_->OnImageLoaded(image, index); | 118 // any valid image load tasks have been posted. |
| 119 if (loader_) |
| 120 loader_->StopTracking(); |
| 121 } |
113 | 122 |
114 if (image) | 123 void ImageLoadingTracker::LoadImage(const ExtensionResource& resource, |
115 delete image; | 124 gfx::Size max_size) { |
| 125 // If we don't have a path we don't need to do any further work, just respond |
| 126 // back. |
| 127 if (resource.relative_path().empty()) { |
| 128 OnImageLoaded(NULL, resource); |
| 129 return; |
| 130 } |
116 | 131 |
117 if (--image_count_ == 0) | 132 // Instruct the ImageLoader to load this on the File thread. LoadImage does |
118 Release(); // We are no longer needed. | 133 // not block. |
| 134 if (!loader_) |
| 135 loader_ = new ImageLoader(this); |
| 136 loader_->LoadImage(resource, max_size); |
119 } | 137 } |
| 138 |
| 139 void ImageLoadingTracker::OnImageLoaded( |
| 140 SkBitmap* image, const ExtensionResource& resource) { |
| 141 observer_->OnImageLoaded(image, resource, responses_++); |
| 142 } |
OLD | NEW |