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_)); | |
43 } | 24 } |
44 | 25 |
45 void ReportBack(SkBitmap* image) { | 26 // Lets this class know that the tracker is no longer interested in the |
46 ChromeThread::PostTask( | 27 // results. |
47 callback_thread_id_, FROM_HERE, | 28 void StopTracking() { |
48 NewRunnableMethod( | 29 tracker_ = NULL; |
49 tracker_, &ImageLoadingTracker::OnImageLoaded, image, index_)); | |
50 } | 30 } |
51 | 31 |
52 virtual void Run() { | 32 // Instructs the loader to load a task on the File thread. |
33 void LoadImage(const ExtensionResource& resource, | |
34 const gfx::Size& max_size, | |
35 int index) { | |
36 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); | |
37 ChromeThread::PostTask( | |
38 ChromeThread::FILE, FROM_HERE, | |
39 NewRunnableMethod(this, &ImageLoader::LoadOnFileThread, resource, | |
40 max_size, index)); | |
41 } | |
42 | |
43 void LoadOnFileThread(const ExtensionResource& resource, | |
44 const gfx::Size& max_size, | |
45 int index) { | |
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, index); |
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, index); |
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(), index); |
76 } | |
77 | |
78 void ReportBack(SkBitmap* image, size_t index) { | |
79 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); | |
80 | |
81 ChromeThread::PostTask( | |
82 ChromeThread::UI, FROM_HERE, | |
83 NewRunnableMethod(this, &ImageLoader::ReportOnUIThread, image, index)); | |
84 } | |
85 | |
86 void ReportOnUIThread(SkBitmap* image, int index) { | |
87 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); | |
88 | |
89 if (tracker_) | |
90 tracker_->OnImageLoaded(image, index); | |
91 | |
92 if (image) | |
93 delete image; | |
81 } | 94 } |
82 | 95 |
83 private: | 96 private: |
84 // The thread that we need to call back on to report that we are done. | 97 // The tracker we are loading the image for. If NULL, it means the tracker is |
85 ChromeThread::ID callback_thread_id_; | 98 // no longer interested in the reply. |
86 | |
87 // The object that is waiting for us to respond back. | |
88 ImageLoadingTracker* tracker_; | 99 ImageLoadingTracker* tracker_; |
89 | 100 |
90 // The image resource to load asynchronously. | 101 DISALLOW_COPY_AND_ASSIGN(ImageLoader); |
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 }; | 102 }; |
99 | 103 |
100 //////////////////////////////////////////////////////////////////////////////// | 104 //////////////////////////////////////////////////////////////////////////////// |
101 // ImageLoadingTracker | 105 // ImageLoadingTracker |
102 | 106 |
103 void ImageLoadingTracker::PostLoadImageTask(const ExtensionResource& resource, | 107 ImageLoadingTracker::ImageLoadingTracker(Observer* observer) |
104 const gfx::Size& max_size) { | 108 : observer_(observer) { |
105 ChromeThread::PostTask( | 109 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); |
106 ChromeThread::FILE, FROM_HERE, | |
107 new LoadImageTask(this, resource, max_size, posted_count_++)); | |
108 } | 110 } |
109 | 111 |
110 void ImageLoadingTracker::OnImageLoaded(SkBitmap* image, size_t index) { | 112 ImageLoadingTracker::~ImageLoadingTracker() { |
111 if (observer_) | 113 // The loader is created lazily and is NULL if the tracker is destroyed before |
112 observer_->OnImageLoaded(image, index); | 114 // any valid image load tasks have been posted. |
115 if (loader_) | |
116 loader_->StopTracking(); | |
117 } | |
113 | 118 |
114 if (image) | 119 void ImageLoadingTracker::PostLoadImageTask(const ExtensionResource& resource, |
Aaron Boodman
2010/03/23 19:10:55
Nit: I think you could simply this name to just Lo
| |
115 delete image; | 120 const gfx::Size& max_size, |
121 int index) { | |
122 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); | |
116 | 123 |
117 if (--image_count_ == 0) | 124 // If we don't have a path we don't need to do any further work, just respond |
118 Release(); // We are no longer needed. | 125 // back. |
126 if (resource.relative_path().empty()) | |
127 OnImageLoaded(NULL, index); | |
128 | |
129 if (!loader_) | |
130 loader_ = new ImageLoader(this); | |
131 loader_->LoadImage(resource, max_size, index); | |
119 } | 132 } |
133 | |
134 void ImageLoadingTracker::OnImageLoaded(SkBitmap* image, int index) { | |
135 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); | |
136 | |
137 observer_->OnImageLoaded(image, index); | |
138 } | |
OLD | NEW |