OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "ui/gfx/image/image_skia.h" | 5 #include "ui/gfx/image/image_skia.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 | 8 |
9 #include <algorithm> | 9 #include <algorithm> |
10 #include <cmath> | 10 #include <cmath> |
(...skipping 27 matching lines...) Expand all Loading... |
38 // larger one. For example, assume 1.20 is requested but only 1.0 and 2.0 are | 38 // larger one. For example, assume 1.20 is requested but only 1.0 and 2.0 are |
39 // supported. In that case, not fall back to 2.0 but 1.0, and then expand | 39 // supported. In that case, not fall back to 2.0 but 1.0, and then expand |
40 // the image to 1.25. | 40 // the image to 1.25. |
41 const float kFallbackToSmallerScaleDiff = 0.20f; | 41 const float kFallbackToSmallerScaleDiff = 0.20f; |
42 | 42 |
43 } // namespace | 43 } // namespace |
44 | 44 |
45 namespace internal { | 45 namespace internal { |
46 namespace { | 46 namespace { |
47 | 47 |
48 class Matcher { | |
49 public: | |
50 explicit Matcher(float scale) : scale_(scale) { | |
51 } | |
52 | |
53 bool operator()(const ImageSkiaRep& rep) const { | |
54 return rep.scale() == scale_; | |
55 } | |
56 | |
57 private: | |
58 float scale_; | |
59 }; | |
60 | |
61 ImageSkiaRep ScaleImageSkiaRep(const ImageSkiaRep& rep, float target_scale) { | 48 ImageSkiaRep ScaleImageSkiaRep(const ImageSkiaRep& rep, float target_scale) { |
62 if (rep.is_null() || rep.scale() == target_scale) | 49 if (rep.is_null() || rep.scale() == target_scale) |
63 return rep; | 50 return rep; |
64 | 51 |
65 gfx::Size scaled_size = | 52 gfx::Size scaled_size = |
66 gfx::ScaleToCeiledSize(rep.pixel_size(), target_scale / rep.scale()); | 53 gfx::ScaleToCeiledSize(rep.pixel_size(), target_scale / rep.scale()); |
67 return ImageSkiaRep(skia::ImageOperations::Resize( | 54 return ImageSkiaRep(skia::ImageOperations::Resize( |
68 rep.sk_bitmap(), | 55 rep.sk_bitmap(), |
69 skia::ImageOperations::RESIZE_LANCZOS3, | 56 skia::ImageOperations::RESIZE_LANCZOS3, |
70 scaled_size.width(), | 57 scaled_size.width(), |
71 scaled_size.height()), target_scale); | 58 scaled_size.height()), target_scale); |
72 } | 59 } |
73 | 60 |
74 } // namespace | 61 } // namespace |
75 | 62 |
76 // A helper class such that ImageSkia can be cheaply copied. ImageSkia holds a | 63 // A helper class such that ImageSkia can be cheaply copied. ImageSkia holds a |
77 // refptr instance of ImageSkiaStorage, which in turn holds all of ImageSkia's | 64 // refptr instance of ImageSkiaStorage, which in turn holds all of ImageSkia's |
78 // information. Having both |base::RefCountedThreadSafe| and | 65 // information. Having both |base::RefCountedThreadSafe| and |
79 // |base::NonThreadSafe| may sounds strange but necessary to turn | 66 // |base::NonThreadSafe| may sound strange but is necessary to turn |
80 // the 'thread-non-safe modifiable ImageSkiaStorage' into | 67 // the 'thread-non-safe modifiable ImageSkiaStorage' into |
81 // the 'thread-safe read-only ImageSkiaStorage'. | 68 // the 'thread-safe read-only ImageSkiaStorage'. |
82 class ImageSkiaStorage : public base::RefCountedThreadSafe<ImageSkiaStorage>, | 69 class ImageSkiaStorage : public base::RefCountedThreadSafe<ImageSkiaStorage>, |
83 public base::NonThreadSafe { | 70 public base::NonThreadSafe { |
84 public: | 71 public: |
85 ImageSkiaStorage(ImageSkiaSource* source, const gfx::Size& size) | 72 ImageSkiaStorage(ImageSkiaSource* source, const gfx::Size& size); |
86 : source_(source), | 73 ImageSkiaStorage(ImageSkiaSource* source, float scale); |
87 size_(size), | |
88 read_only_(false) { | |
89 } | |
90 | 74 |
91 ImageSkiaStorage(ImageSkiaSource* source, float scale) | 75 bool has_source() const { return source_ != nullptr; } |
92 : source_(source), | |
93 read_only_(false) { | |
94 ImageSkia::ImageSkiaReps::iterator it = FindRepresentation(scale, true); | |
95 if (it == image_reps_.end() || it->is_null()) | |
96 source_.reset(); | |
97 else | |
98 size_.SetSize(it->GetWidth(), it->GetHeight()); | |
99 } | |
100 | |
101 bool has_source() const { return source_.get() != NULL; } | |
102 | |
103 std::vector<gfx::ImageSkiaRep>& image_reps() { return image_reps_; } | 76 std::vector<gfx::ImageSkiaRep>& image_reps() { return image_reps_; } |
104 | |
105 const gfx::Size& size() const { return size_; } | 77 const gfx::Size& size() const { return size_; } |
106 | |
107 bool read_only() const { return read_only_; } | 78 bool read_only() const { return read_only_; } |
108 | 79 |
109 void DeleteSource() { | 80 void DeleteSource(); |
110 source_.reset(); | 81 void SetReadOnly(); |
111 } | 82 void DetachFromThread(); |
112 | |
113 void SetReadOnly() { | |
114 read_only_ = true; | |
115 } | |
116 | |
117 void DetachFromThread() { | |
118 base::NonThreadSafe::DetachFromThread(); | |
119 } | |
120 | 83 |
121 // Checks if the current thread can safely modify the storage. | 84 // Checks if the current thread can safely modify the storage. |
122 bool CanModify() const { | 85 bool CanModify() const; |
123 return !read_only_ && CalledOnValidThread(); | |
124 } | |
125 | 86 |
126 // Checks if the current thread can safely read the storage. | 87 // Checks if the current thread can safely read the storage. |
127 bool CanRead() const { | 88 bool CanRead() const; |
128 return (read_only_ && !source_.get()) || CalledOnValidThread(); | |
129 } | |
130 | 89 |
131 // Add a new representation. This checks if the scale of the added image | 90 // Add a new representation. This checks if the scale of the added image |
132 // is not 1.0f, and mark the existing rep as scaled to make | 91 // is not 1.0f, and mark the existing rep as scaled to make |
133 // the image high DPI aware. | 92 // the image high DPI aware. |
134 void AddRepresentation(const ImageSkiaRep& image) { | 93 void AddRepresentation(const ImageSkiaRep& image); |
135 if (image.scale() != 1.0f) { | |
136 for (ImageSkia::ImageSkiaReps::iterator it = image_reps_.begin(); | |
137 it < image_reps_.end(); | |
138 ++it) { | |
139 if (it->unscaled()) { | |
140 DCHECK_EQ(1.0f, it->scale()); | |
141 it->SetScaled(); | |
142 break; | |
143 } | |
144 } | |
145 } | |
146 image_reps_.push_back(image); | |
147 } | |
148 | 94 |
149 // Returns the iterator of the image rep whose density best matches | 95 // Returns the iterator of the image rep whose density best matches |
150 // |scale|. If the image for the |scale| doesn't exist in the storage and | 96 // |scale|. If the image for the |scale| doesn't exist in the storage and |
151 // |storage| is set, it fetches new image by calling | 97 // |storage| is set, it fetches new image by calling |
152 // |ImageSkiaSource::GetImageForScale|. There are two modes to deal with | 98 // |ImageSkiaSource::GetImageForScale|. There are two modes to deal with |
153 // arbitrary scale factors. | 99 // arbitrary scale factors. |
154 // 1: Invoke GetImageForScale with requested scale and if the source | 100 // 1: Invoke GetImageForScale with requested scale and if the source |
155 // returns the image with different scale (if the image doesn't exist in | 101 // returns the image with different scale (if the image doesn't exist in |
156 // resource, for example), it will fallback to closest image rep. | 102 // resource, for example), it will fallback to closest image rep. |
157 // 2: Invoke GetImageForScale with the closest known scale to the requested | 103 // 2: Invoke GetImageForScale with the closest known scale to the requested |
158 // one and rescale the image. | 104 // one and rescale the image. |
159 // Right now only Windows uses 2 and other platforms use 1 by default. | 105 // Right now only Windows uses 2 and other platforms use 1 by default. |
160 // TODO(mukai, oshima): abandon 1 code path and use 2 for every platforms. | 106 // TODO(mukai, oshima): abandon 1 code path and use 2 for every platforms. |
161 std::vector<ImageSkiaRep>::iterator FindRepresentation( | 107 std::vector<ImageSkiaRep>::iterator FindRepresentation( |
162 float scale, bool fetch_new_image) const { | 108 float scale, |
163 ImageSkiaStorage* non_const = const_cast<ImageSkiaStorage*>(this); | 109 bool fetch_new_image) const; |
164 | |
165 ImageSkia::ImageSkiaReps::iterator closest_iter = | |
166 non_const->image_reps().end(); | |
167 ImageSkia::ImageSkiaReps::iterator exact_iter = | |
168 non_const->image_reps().end(); | |
169 float smallest_diff = std::numeric_limits<float>::max(); | |
170 for (ImageSkia::ImageSkiaReps::iterator it = | |
171 non_const->image_reps().begin(); | |
172 it < image_reps_.end(); ++it) { | |
173 if (it->scale() == scale) { | |
174 // found exact match | |
175 fetch_new_image = false; | |
176 if (it->is_null()) | |
177 continue; | |
178 exact_iter = it; | |
179 break; | |
180 } | |
181 float diff = std::abs(it->scale() - scale); | |
182 if (diff < smallest_diff && !it->is_null()) { | |
183 closest_iter = it; | |
184 smallest_diff = diff; | |
185 } | |
186 } | |
187 | |
188 if (fetch_new_image && source_.get()) { | |
189 DCHECK(CalledOnValidThread()) << | |
190 "An ImageSkia with the source must be accessed by the same thread."; | |
191 | |
192 ImageSkiaRep image; | |
193 float resource_scale = scale; | |
194 if (g_supported_scales) { | |
195 if (g_supported_scales->back() <= scale) { | |
196 resource_scale = g_supported_scales->back(); | |
197 } else { | |
198 for (size_t i = 0; i < g_supported_scales->size(); ++i) { | |
199 if ((*g_supported_scales)[i] + kFallbackToSmallerScaleDiff >= | |
200 scale) { | |
201 resource_scale = (*g_supported_scales)[i]; | |
202 break; | |
203 } | |
204 } | |
205 } | |
206 } | |
207 if (scale != resource_scale) { | |
208 std::vector<ImageSkiaRep>::iterator iter = FindRepresentation( | |
209 resource_scale, fetch_new_image); | |
210 DCHECK(iter != image_reps_.end()); | |
211 image = iter->unscaled() ? (*iter) : ScaleImageSkiaRep(*iter, scale); | |
212 } else { | |
213 image = source_->GetImageForScale(scale); | |
214 // Image may be missing for the specified scale in some cases, such like | |
215 // looking up 2x resources but the 2x resource pack is missing. Falls | |
216 // back to 1x and re-scale it. | |
217 if (image.is_null() && scale != 1.0f) | |
218 image = ScaleImageSkiaRep(source_->GetImageForScale(1.0f), scale); | |
219 } | |
220 | |
221 // If the source returned the new image, store it. | |
222 if (!image.is_null() && | |
223 std::find_if(image_reps_.begin(), image_reps_.end(), | |
224 Matcher(image.scale())) == image_reps_.end()) { | |
225 non_const->image_reps().push_back(image); | |
226 } | |
227 | |
228 // If the result image's scale isn't same as the expected scale, create | |
229 // null ImageSkiaRep with the |scale| so that the next lookup will | |
230 // fallback to the closest scale. | |
231 if (image.is_null() || image.scale() != scale) { | |
232 non_const->image_reps().push_back(ImageSkiaRep(SkBitmap(), scale)); | |
233 } | |
234 | |
235 // image_reps_ must have the exact much now, so find again. | |
236 return FindRepresentation(scale, false); | |
237 } | |
238 return exact_iter != image_reps_.end() ? exact_iter : closest_iter; | |
239 } | |
240 | 110 |
241 private: | 111 private: |
242 virtual ~ImageSkiaStorage() { | 112 friend class base::RefCountedThreadSafe<ImageSkiaStorage>; |
243 // We only care if the storage is modified by the same thread. | 113 |
244 // Don't blow up even if someone else deleted the ImageSkia. | 114 virtual ~ImageSkiaStorage(); |
245 DetachFromThread(); | |
246 } | |
247 | 115 |
248 // Vector of bitmaps and their associated scale. | 116 // Vector of bitmaps and their associated scale. |
249 std::vector<gfx::ImageSkiaRep> image_reps_; | 117 std::vector<gfx::ImageSkiaRep> image_reps_; |
250 | 118 |
251 scoped_ptr<ImageSkiaSource> source_; | 119 scoped_ptr<ImageSkiaSource> source_; |
252 | 120 |
253 // Size of the image in DIP. | 121 // Size of the image in DIP. |
254 gfx::Size size_; | 122 gfx::Size size_; |
255 | 123 |
256 bool read_only_; | 124 bool read_only_; |
257 | 125 |
258 friend class base::RefCountedThreadSafe<ImageSkiaStorage>; | 126 DISALLOW_COPY_AND_ASSIGN(ImageSkiaStorage); |
259 }; | 127 }; |
260 | 128 |
| 129 ImageSkiaStorage::ImageSkiaStorage(ImageSkiaSource* source, |
| 130 const gfx::Size& size) |
| 131 : source_(source), size_(size), read_only_(false) {} |
| 132 |
| 133 ImageSkiaStorage::ImageSkiaStorage(ImageSkiaSource* source, float scale) |
| 134 : source_(source), read_only_(false) { |
| 135 ImageSkia::ImageSkiaReps::iterator it = FindRepresentation(scale, true); |
| 136 if (it == image_reps_.end() || it->is_null()) |
| 137 source_.reset(); |
| 138 else |
| 139 size_.SetSize(it->GetWidth(), it->GetHeight()); |
| 140 } |
| 141 |
| 142 void ImageSkiaStorage::DeleteSource() { |
| 143 source_.reset(); |
| 144 } |
| 145 |
| 146 void ImageSkiaStorage::SetReadOnly() { |
| 147 read_only_ = true; |
| 148 } |
| 149 |
| 150 void ImageSkiaStorage::DetachFromThread() { |
| 151 base::NonThreadSafe::DetachFromThread(); |
| 152 } |
| 153 |
| 154 bool ImageSkiaStorage::CanModify() const { |
| 155 return !read_only_ && CalledOnValidThread(); |
| 156 } |
| 157 |
| 158 bool ImageSkiaStorage::CanRead() const { |
| 159 return (read_only_ && !source_) || CalledOnValidThread(); |
| 160 } |
| 161 |
| 162 void ImageSkiaStorage::AddRepresentation(const ImageSkiaRep& image) { |
| 163 if (image.scale() != 1.0f) { |
| 164 for (ImageSkia::ImageSkiaReps::iterator it = image_reps_.begin(); |
| 165 it < image_reps_.end(); ++it) { |
| 166 if (it->unscaled()) { |
| 167 DCHECK_EQ(1.0f, it->scale()); |
| 168 it->SetScaled(); |
| 169 break; |
| 170 } |
| 171 } |
| 172 } |
| 173 image_reps_.push_back(image); |
| 174 } |
| 175 |
| 176 std::vector<ImageSkiaRep>::iterator ImageSkiaStorage::FindRepresentation( |
| 177 float scale, |
| 178 bool fetch_new_image) const { |
| 179 ImageSkiaStorage* non_const = const_cast<ImageSkiaStorage*>(this); |
| 180 |
| 181 ImageSkia::ImageSkiaReps::iterator closest_iter = |
| 182 non_const->image_reps().end(); |
| 183 ImageSkia::ImageSkiaReps::iterator exact_iter = non_const->image_reps().end(); |
| 184 float smallest_diff = std::numeric_limits<float>::max(); |
| 185 for (ImageSkia::ImageSkiaReps::iterator it = non_const->image_reps().begin(); |
| 186 it < image_reps_.end(); ++it) { |
| 187 if (it->scale() == scale) { |
| 188 // found exact match |
| 189 fetch_new_image = false; |
| 190 if (it->is_null()) |
| 191 continue; |
| 192 exact_iter = it; |
| 193 break; |
| 194 } |
| 195 float diff = std::abs(it->scale() - scale); |
| 196 if (diff < smallest_diff && !it->is_null()) { |
| 197 closest_iter = it; |
| 198 smallest_diff = diff; |
| 199 } |
| 200 } |
| 201 |
| 202 if (fetch_new_image && source_.get()) { |
| 203 DCHECK(CalledOnValidThread()) |
| 204 << "An ImageSkia with the source must be accessed by the same thread."; |
| 205 |
| 206 ImageSkiaRep image; |
| 207 float resource_scale = scale; |
| 208 if (g_supported_scales) { |
| 209 if (g_supported_scales->back() <= scale) { |
| 210 resource_scale = g_supported_scales->back(); |
| 211 } else { |
| 212 for (size_t i = 0; i < g_supported_scales->size(); ++i) { |
| 213 if ((*g_supported_scales)[i] + kFallbackToSmallerScaleDiff >= scale) { |
| 214 resource_scale = (*g_supported_scales)[i]; |
| 215 break; |
| 216 } |
| 217 } |
| 218 } |
| 219 } |
| 220 if (scale != resource_scale) { |
| 221 std::vector<ImageSkiaRep>::iterator iter = |
| 222 FindRepresentation(resource_scale, fetch_new_image); |
| 223 DCHECK(iter != image_reps_.end()); |
| 224 image = iter->unscaled() ? (*iter) : ScaleImageSkiaRep(*iter, scale); |
| 225 } else { |
| 226 image = source_->GetImageForScale(scale); |
| 227 // Image may be missing for the specified scale in some cases, such like |
| 228 // looking up 2x resources but the 2x resource pack is missing. Fall back |
| 229 // to 1x and re-scale it. |
| 230 if (image.is_null() && scale != 1.0f) |
| 231 image = ScaleImageSkiaRep(source_->GetImageForScale(1.0f), scale); |
| 232 } |
| 233 |
| 234 // If the source returned the new image, store it. |
| 235 if (!image.is_null() && |
| 236 std::find_if(image_reps_.begin(), image_reps_.end(), |
| 237 [&image](const ImageSkiaRep& rep) { |
| 238 return rep.scale() == image.scale(); |
| 239 }) == image_reps_.end()) { |
| 240 non_const->image_reps().push_back(image); |
| 241 } |
| 242 |
| 243 // If the result image's scale isn't same as the expected scale, create a |
| 244 // null ImageSkiaRep with the |scale| so that the next lookup will fall back |
| 245 // to the closest scale. |
| 246 if (image.is_null() || image.scale() != scale) { |
| 247 non_const->image_reps().push_back(ImageSkiaRep(SkBitmap(), scale)); |
| 248 } |
| 249 |
| 250 // image_reps_ must have the exact much now, so find again. |
| 251 return FindRepresentation(scale, false); |
| 252 } |
| 253 return exact_iter != image_reps_.end() ? exact_iter : closest_iter; |
| 254 } |
| 255 |
| 256 ImageSkiaStorage::~ImageSkiaStorage() { |
| 257 // We only care if the storage is modified by the same thread. Don't blow up |
| 258 // even if someone else deleted the ImageSkia. |
| 259 DetachFromThread(); |
| 260 } |
| 261 |
261 } // internal | 262 } // internal |
262 | 263 |
263 ImageSkia::ImageSkia() : storage_(NULL) { | 264 ImageSkia::ImageSkia() : storage_(NULL) { |
264 } | 265 } |
265 | 266 |
266 ImageSkia::ImageSkia(ImageSkiaSource* source, const gfx::Size& size) | 267 ImageSkia::ImageSkia(ImageSkiaSource* source, const gfx::Size& size) |
267 : storage_(new internal::ImageSkiaStorage(source, size)) { | 268 : storage_(new internal::ImageSkiaStorage(source, size)) { |
268 DCHECK(source); | 269 DCHECK(source); |
269 // No other thread has reference to this, so it's safe to detach the thread. | 270 // No other thread has reference to this, so it's safe to detach the thread. |
270 DetachStorageFromThread(); | 271 DetachStorageFromThread(); |
(...skipping 224 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
495 bool ImageSkia::CanModify() const { | 496 bool ImageSkia::CanModify() const { |
496 return !storage_.get() || storage_->CanModify(); | 497 return !storage_.get() || storage_->CanModify(); |
497 } | 498 } |
498 | 499 |
499 void ImageSkia::DetachStorageFromThread() { | 500 void ImageSkia::DetachStorageFromThread() { |
500 if (storage_.get()) | 501 if (storage_.get()) |
501 storage_->DetachFromThread(); | 502 storage_->DetachFromThread(); |
502 } | 503 } |
503 | 504 |
504 } // namespace gfx | 505 } // namespace gfx |
OLD | NEW |