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 <algorithm> | |
8 #include <cmath> | |
7 #include <limits> | 9 #include <limits> |
8 #include <cmath> | |
9 | 10 |
10 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/memory/scoped_ptr.h" | |
13 #include "ui/gfx/image/image_skia_source.h" | |
11 #include "ui/gfx/size.h" | 14 #include "ui/gfx/size.h" |
12 #include "ui/gfx/skia_util.h" | 15 #include "ui/gfx/skia_util.h" |
13 | 16 |
14 namespace gfx { | 17 namespace gfx { |
18 namespace { | |
19 | |
20 // static | |
21 gfx::ImageSkiaRep& NullImageRep() { | |
22 CR_DEFINE_STATIC_LOCAL(ImageSkiaRep, null_image_rep, ()); | |
23 return null_image_rep; | |
24 } | |
25 | |
26 } // namespace | |
15 | 27 |
16 namespace internal { | 28 namespace internal { |
29 namespace { | |
30 | |
31 class Matcher { | |
32 public: | |
33 explicit Matcher(ui::ScaleFactor scale_factor) : scale_factor_(scale_factor) { | |
34 } | |
35 | |
36 bool operator()(const ImageSkiaRep& rep) const { | |
37 return rep.scale_factor() == scale_factor_; | |
38 } | |
39 | |
40 private: | |
41 ui::ScaleFactor scale_factor_; | |
42 }; | |
43 | |
44 } // namespace | |
17 | 45 |
18 // A helper class such that ImageSkia can be cheaply copied. ImageSkia holds a | 46 // A helper class such that ImageSkia can be cheaply copied. ImageSkia holds a |
19 // refptr instance of ImageSkiaStorage, which in turn holds all of ImageSkia's | 47 // refptr instance of ImageSkiaStorage, which in turn holds all of ImageSkia's |
20 // information. | 48 // information. |
21 class ImageSkiaStorage : public base::RefCounted<ImageSkiaStorage> { | 49 class ImageSkiaStorage : public base::RefCounted<ImageSkiaStorage> { |
22 public: | 50 public: |
23 ImageSkiaStorage() { | 51 ImageSkiaStorage(ImageSkiaSource* source, const gfx::Size& size) |
52 : source_(source), | |
53 size_(size) { | |
24 } | 54 } |
25 | 55 |
26 std::vector<gfx::ImageSkiaRep>& image_reps() { return image_reps_; } | 56 std::vector<gfx::ImageSkiaRep>& image_reps() { return image_reps_; } |
27 | 57 |
28 void set_size(const gfx::Size& size) { size_ = size; } | |
29 const gfx::Size& size() const { return size_; } | 58 const gfx::Size& size() const { return size_; } |
30 | 59 |
60 // Returns the iterator of the image rep whose density best matches | |
61 // |scale_factor|. If the image for the |scale_factor| doesn't exist | |
62 // in the storage and |storage| is set, it fetches new image by calling | |
63 // |ImageSkiaSource::GetImageForScale|. If the source returns the | |
64 // image with different scale factor (if the image doesn't exist in | |
65 // resource, for example), it will fallback to closest image rep. | |
66 std::vector<ImageSkiaRep>::iterator FindRepresentation( | |
67 ui::ScaleFactor scale_factor, bool fetch_new_image) const { | |
68 ImageSkiaStorage* non_const = const_cast<ImageSkiaStorage*>(this); | |
69 | |
70 float scale = ui::GetScaleFactorScale(scale_factor); | |
71 ImageSkia::ImageSkiaReps::iterator closest_iter = | |
72 non_const->image_reps().end(); | |
73 ImageSkia::ImageSkiaReps::iterator exact_iter = | |
74 non_const->image_reps().end(); | |
75 float smallest_diff = std::numeric_limits<float>::max(); | |
76 for (ImageSkia::ImageSkiaReps::iterator it = | |
77 non_const->image_reps().begin(); | |
78 it < image_reps_.end(); ++it) { | |
79 if (it->GetScale() == scale) { | |
80 // found exact match | |
81 fetch_new_image = false; | |
82 if (it->is_null()) | |
83 continue; | |
84 exact_iter = it; | |
85 break; | |
86 } | |
87 float diff = std::abs(it->GetScale() - scale); | |
88 if (diff < smallest_diff && !it->is_null()) { | |
89 closest_iter = it; | |
90 smallest_diff = diff; | |
91 } | |
92 } | |
93 | |
94 if (fetch_new_image && source_.get()) { | |
95 ImageSkiaRep image = source_->GetImageForScale(scale_factor); | |
96 | |
97 // If the source returned the new image, store it. | |
98 if (!image.is_null() && | |
99 std::find_if(image_reps_.begin(), image_reps_.end(), | |
100 Matcher(image.scale_factor())) == image_reps_.end()) { | |
101 non_const->image_reps().push_back(image); | |
102 } | |
103 | |
104 // If the result image's scale factor isn't same as the expected | |
105 // scale factor, create null ImageSkiaRep with the |scale_factor| | |
106 // so that the next lookup will fallback to the closest scale. | |
107 if (image.is_null() || image.scale_factor() != scale_factor) { | |
108 non_const->image_reps().push_back( | |
109 ImageSkiaRep(SkBitmap(), scale_factor)); | |
110 } | |
111 | |
112 // image_reps_ must have the exact much now, so find again. | |
113 return FindRepresentation(scale_factor, false); | |
114 } | |
115 return exact_iter != image_reps_.end() ? exact_iter : closest_iter; | |
116 } | |
pkotwicz
2012/07/01 22:22:23
This code is ok. However, I would rather that if t
oshima
2012/07/02 16:43:34
ImageSkia::GetRepresentation doesn't guarantee tha
pkotwicz
2012/07/02 17:13:53
I think there are two aspects.
1) We currently som
pkotwicz
2012/07/02 17:26:21
Btw, it might be sufficient to scale the assets wh
oshima
2012/07/02 17:26:47
grit will scale if necessary. Even if it doesn't 1
| |
117 | |
31 private: | 118 private: |
32 ~ImageSkiaStorage() { | 119 ~ImageSkiaStorage() { |
33 } | 120 } |
34 | 121 |
35 // Vector of bitmaps and their associated scale factor. | 122 // Vector of bitmaps and their associated scale factor. |
36 std::vector<gfx::ImageSkiaRep> image_reps_; | 123 std::vector<gfx::ImageSkiaRep> image_reps_; |
37 | 124 |
125 scoped_ptr<ImageSkiaSource> source_; | |
126 | |
38 // Size of the image in DIP. | 127 // Size of the image in DIP. |
39 gfx::Size size_; | 128 const gfx::Size size_; |
40 | 129 |
41 friend class base::RefCounted<ImageSkiaStorage>; | 130 friend class base::RefCounted<ImageSkiaStorage>; |
42 }; | 131 }; |
43 | 132 |
44 } // internal | 133 } // internal |
45 | 134 |
46 ImageSkia::ImageSkia() : storage_(NULL) { | 135 ImageSkia::ImageSkia() : storage_(NULL) { |
47 } | 136 } |
48 | 137 |
138 ImageSkia::ImageSkia(ImageSkiaSource* source, const gfx::Size& size) | |
139 : storage_(new internal::ImageSkiaStorage(source, size)) { | |
140 } | |
141 | |
49 ImageSkia::ImageSkia(const SkBitmap& bitmap) { | 142 ImageSkia::ImageSkia(const SkBitmap& bitmap) { |
50 Init(ImageSkiaRep(bitmap)); | 143 Init(ImageSkiaRep(bitmap)); |
51 } | 144 } |
52 | 145 |
53 ImageSkia::ImageSkia(const ImageSkiaRep& image_rep) { | 146 ImageSkia::ImageSkia(const ImageSkiaRep& image_rep) { |
54 Init(image_rep); | 147 Init(image_rep); |
55 } | 148 } |
56 | 149 |
57 ImageSkia::ImageSkia(const ImageSkia& other) : storage_(other.storage_) { | 150 ImageSkia::ImageSkia(const ImageSkia& other) : storage_(other.storage_) { |
58 } | 151 } |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
96 Init(image_rep); | 189 Init(image_rep); |
97 else | 190 else |
98 storage_->image_reps().push_back(image_rep); | 191 storage_->image_reps().push_back(image_rep); |
99 } | 192 } |
100 | 193 |
101 void ImageSkia::RemoveRepresentation(ui::ScaleFactor scale_factor) { | 194 void ImageSkia::RemoveRepresentation(ui::ScaleFactor scale_factor) { |
102 if (isNull()) | 195 if (isNull()) |
103 return; | 196 return; |
104 | 197 |
105 ImageSkiaReps& image_reps = storage_->image_reps(); | 198 ImageSkiaReps& image_reps = storage_->image_reps(); |
106 ImageSkiaReps::iterator it = FindRepresentation(scale_factor); | 199 ImageSkiaReps::iterator it = |
200 storage_->FindRepresentation(scale_factor, false); | |
107 if (it != image_reps.end() && it->scale_factor() == scale_factor) | 201 if (it != image_reps.end() && it->scale_factor() == scale_factor) |
108 image_reps.erase(it); | 202 image_reps.erase(it); |
109 } | 203 } |
110 | 204 |
111 bool ImageSkia::HasRepresentation(ui::ScaleFactor scale_factor) { | 205 bool ImageSkia::HasRepresentation(ui::ScaleFactor scale_factor) { |
112 if (isNull()) | 206 if (isNull()) |
113 return false; | 207 return false; |
114 | 208 |
115 ImageSkiaReps::iterator it = FindRepresentation(scale_factor); | 209 ImageSkiaReps::iterator it = |
210 storage_->FindRepresentation(scale_factor, false); | |
116 return (it != storage_->image_reps().end() && | 211 return (it != storage_->image_reps().end() && |
117 it->scale_factor() == scale_factor); | 212 it->scale_factor() == scale_factor); |
118 } | 213 } |
119 | 214 |
120 const ImageSkiaRep& ImageSkia::GetRepresentation( | 215 const ImageSkiaRep& ImageSkia::GetRepresentation( |
121 ui::ScaleFactor scale_factor) const { | 216 ui::ScaleFactor scale_factor) const { |
122 if (isNull()) | 217 if (isNull()) |
123 return NullImageRep(); | 218 return NullImageRep(); |
124 | 219 |
125 ImageSkiaReps::iterator it = FindRepresentation(scale_factor); | 220 ImageSkiaReps::iterator it = storage_->FindRepresentation(scale_factor, true); |
126 if (it == storage_->image_reps().end()) | 221 if (it == storage_->image_reps().end()) |
127 return NullImageRep(); | 222 return NullImageRep(); |
128 | 223 |
129 return *it; | 224 return *it; |
130 } | 225 } |
131 | 226 |
132 bool ImageSkia::empty() const { | 227 bool ImageSkia::empty() const { |
133 return isNull() || storage_->size().IsEmpty(); | 228 return isNull() || storage_->size().IsEmpty(); |
134 } | 229 } |
135 | 230 |
136 int ImageSkia::width() const { | 231 int ImageSkia::width() const { |
137 return isNull() ? 0 : storage_->size().width(); | 232 return isNull() ? 0 : storage_->size().width(); |
138 } | 233 } |
139 | 234 |
235 gfx::Size ImageSkia::size() const { | |
236 return gfx::Size(width(), height()); | |
237 } | |
238 | |
140 int ImageSkia::height() const { | 239 int ImageSkia::height() const { |
141 return isNull() ? 0 : storage_->size().height(); | 240 return isNull() ? 0 : storage_->size().height(); |
142 } | 241 } |
143 | 242 |
144 bool ImageSkia::extractSubset(ImageSkia* dst, const SkIRect& subset) const { | 243 bool ImageSkia::extractSubset(ImageSkia* dst, const SkIRect& subset) const { |
145 if (isNull()) | 244 if (isNull()) |
146 return false; | 245 return false; |
147 ImageSkia image; | 246 ImageSkia image; |
148 ImageSkiaReps& image_reps = storage_->image_reps(); | 247 ImageSkiaReps& image_reps = storage_->image_reps(); |
149 for (ImageSkiaReps::iterator it = image_reps.begin(); | 248 for (ImageSkiaReps::iterator it = image_reps.begin(); |
(...skipping 15 matching lines...) Expand all Loading... | |
165 return false; | 264 return false; |
166 | 265 |
167 *dst = image; | 266 *dst = image; |
168 return true; | 267 return true; |
169 } | 268 } |
170 | 269 |
171 std::vector<ImageSkiaRep> ImageSkia::image_reps() const { | 270 std::vector<ImageSkiaRep> ImageSkia::image_reps() const { |
172 if (isNull()) | 271 if (isNull()) |
173 return std::vector<ImageSkiaRep>(); | 272 return std::vector<ImageSkiaRep>(); |
174 | 273 |
175 return storage_->image_reps(); | 274 return storage_->image_reps(); |
pkotwicz
2012/07/01 22:22:23
What happens here when you have an ImageSkiaSource
oshima
2012/07/02 16:43:34
The semantics of this api is and has been "return
pkotwicz
2012/07/02 17:13:53
A couple places where we might want to use 2
- Con
oshima
2012/07/02 17:26:47
Are both cases for mac? We have no plan to use Ima
pkotwicz
2012/07/02 18:05:12
We will most likely be using ImageSkiaSource for m
oshima
2012/07/02 18:19:30
Let's revisit if we find such case then.
| |
176 } | 275 } |
177 | 276 |
178 const SkBitmap* ImageSkia::bitmap() const { | 277 const SkBitmap* ImageSkia::bitmap() const { |
179 if (isNull()) { | 278 if (isNull()) { |
180 // Callers expect a ImageSkiaRep even if it is |isNull()|. | 279 // Callers expect a ImageSkiaRep even if it is |isNull()|. |
181 // TODO(pkotwicz): Fix this. | 280 // TODO(pkotwicz): Fix this. |
182 return &NullImageRep().sk_bitmap(); | 281 return &NullImageRep().sk_bitmap(); |
183 } | 282 } |
184 | 283 |
185 return &storage_->image_reps()[0].sk_bitmap(); | 284 return &storage_->image_reps()[0].sk_bitmap(); |
186 } | 285 } |
187 | 286 |
188 void ImageSkia::Init(const ImageSkiaRep& image_rep) { | 287 void ImageSkia::Init(const ImageSkiaRep& image_rep) { |
189 // TODO(pkotwicz): The image should be null whenever image rep is null. | 288 // TODO(pkotwicz): The image should be null whenever image rep is null. |
190 if (image_rep.sk_bitmap().empty()) { | 289 if (image_rep.sk_bitmap().empty()) { |
191 storage_ = NULL; | 290 storage_ = NULL; |
192 return; | 291 return; |
193 } | 292 } |
194 storage_ = new internal::ImageSkiaStorage(); | 293 storage_ = new internal::ImageSkiaStorage( |
195 storage_->set_size(gfx::Size(image_rep.GetWidth(), image_rep.GetHeight())); | 294 NULL, gfx::Size(image_rep.GetWidth(), image_rep.GetHeight())); |
196 storage_->image_reps().push_back(image_rep); | 295 storage_->image_reps().push_back(image_rep); |
197 } | 296 } |
198 | 297 |
199 // static | |
200 ImageSkiaRep& ImageSkia::NullImageRep() { | |
201 CR_DEFINE_STATIC_LOCAL(ImageSkiaRep, null_image_rep, ()); | |
202 return null_image_rep; | |
203 } | |
204 | |
205 std::vector<ImageSkiaRep>::iterator ImageSkia::FindRepresentation( | |
206 ui::ScaleFactor scale_factor) const { | |
207 DCHECK(!isNull()); | |
208 | |
209 float scale = ui::GetScaleFactorScale(scale_factor); | |
210 ImageSkiaReps& image_reps = storage_->image_reps(); | |
211 ImageSkiaReps::iterator closest_iter = image_reps.end(); | |
212 float smallest_diff = std::numeric_limits<float>::max(); | |
213 for (ImageSkiaReps::iterator it = image_reps.begin(); | |
214 it < image_reps.end(); | |
215 ++it) { | |
216 float diff = std::abs(it->GetScale() - scale); | |
217 if (diff < smallest_diff) { | |
218 closest_iter = it; | |
219 smallest_diff = diff; | |
220 } | |
221 } | |
222 | |
223 return closest_iter; | |
224 } | |
225 | |
226 } // namespace gfx | 298 } // namespace gfx |
OLD | NEW |