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
3 // found in the LICENSE file.
5 #include "ui/gfx/image/image_skia.h"
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/threading/non_thread_safe.h"
14 #include "ui/gfx/image/image_skia_operations.h"
15 #include "ui/gfx/image/image_skia_source.h"
16 #include "ui/gfx/rect.h"
17 #include "ui/gfx/size.h"
18 #include "ui/gfx/skia_util.h"
24 gfx::ImageSkiaRep
& NullImageRep() {
25 CR_DEFINE_STATIC_LOCAL(ImageSkiaRep
, null_image_rep
, ());
26 return null_image_rep
;
36 explicit Matcher(ui::ScaleFactor scale_factor
) : scale_factor_(scale_factor
) {
39 bool operator()(const ImageSkiaRep
& rep
) const {
40 return rep
.scale_factor() == scale_factor_
;
44 ui::ScaleFactor scale_factor_
;
49 // A helper class such that ImageSkia can be cheaply copied. ImageSkia holds a
50 // refptr instance of ImageSkiaStorage, which in turn holds all of ImageSkia's
51 // information. Having both |base::RefCountedThreadSafe| and
52 // |base::NonThreadSafe| may sounds strange but necessary to turn
53 // the 'thread-non-safe modifiable ImageSkiaStorage' into
54 // the 'thread-safe read-only ImageSkiaStorage'.
55 class ImageSkiaStorage
: public base::RefCountedThreadSafe
<ImageSkiaStorage
>,
56 public base::NonThreadSafe
{
58 ImageSkiaStorage(ImageSkiaSource
* source
, const gfx::Size
& size
)
64 ImageSkiaStorage(ImageSkiaSource
* source
, ui::ScaleFactor scale_factor
)
67 ImageSkia::ImageSkiaReps::iterator it
=
68 FindRepresentation(scale_factor
, true);
69 if (it
== image_reps_
.end() || it
->is_null())
72 size_
.SetSize(it
->GetWidth(), it
->GetHeight());
75 bool has_source() const { return source_
.get() != NULL
; }
77 std::vector
<gfx::ImageSkiaRep
>& image_reps() { return image_reps_
; }
79 const gfx::Size
& size() const { return size_
; }
81 bool read_only() const { return read_only_
; }
91 void DetachFromThread() {
92 base::NonThreadSafe::DetachFromThread();
95 // Checks if the current thread can safely modify the storage.
96 bool CanModify() const {
97 return !read_only_
&& CalledOnValidThread();
100 // Checks if the current thread can safely read the storage.
101 bool CanRead() const {
102 return (read_only_
&& !source_
.get()) || CalledOnValidThread();
105 // Returns the iterator of the image rep whose density best matches
106 // |scale_factor|. If the image for the |scale_factor| doesn't exist
107 // in the storage and |storage| is set, it fetches new image by calling
108 // |ImageSkiaSource::GetImageForScale|. If the source returns the
109 // image with different scale factor (if the image doesn't exist in
110 // resource, for example), it will fallback to closest image rep.
111 std::vector
<ImageSkiaRep
>::iterator
FindRepresentation(
112 ui::ScaleFactor scale_factor
, bool fetch_new_image
) const {
113 ImageSkiaStorage
* non_const
= const_cast<ImageSkiaStorage
*>(this);
115 float scale
= ui::GetScaleFactorScale(scale_factor
);
116 ImageSkia::ImageSkiaReps::iterator closest_iter
=
117 non_const
->image_reps().end();
118 ImageSkia::ImageSkiaReps::iterator exact_iter
=
119 non_const
->image_reps().end();
120 float smallest_diff
= std::numeric_limits
<float>::max();
121 for (ImageSkia::ImageSkiaReps::iterator it
=
122 non_const
->image_reps().begin();
123 it
< image_reps_
.end(); ++it
) {
124 if (it
->GetScale() == scale
) {
126 fetch_new_image
= false;
132 float diff
= std::abs(it
->GetScale() - scale
);
133 if (diff
< smallest_diff
&& !it
->is_null()) {
135 smallest_diff
= diff
;
139 if (fetch_new_image
&& source_
.get()) {
140 DCHECK(CalledOnValidThread()) <<
141 "An ImageSkia with the source must be accessed by the same thread.";
143 ImageSkiaRep image
= source_
->GetImageForScale(scale_factor
);
145 // If the source returned the new image, store it.
146 if (!image
.is_null() &&
147 std::find_if(image_reps_
.begin(), image_reps_
.end(),
148 Matcher(image
.scale_factor())) == image_reps_
.end()) {
149 non_const
->image_reps().push_back(image
);
152 // If the result image's scale factor isn't same as the expected
153 // scale factor, create null ImageSkiaRep with the |scale_factor|
154 // so that the next lookup will fallback to the closest scale.
155 if (image
.is_null() || image
.scale_factor() != scale_factor
) {
156 non_const
->image_reps().push_back(
157 ImageSkiaRep(SkBitmap(), scale_factor
));
160 // image_reps_ must have the exact much now, so find again.
161 return FindRepresentation(scale_factor
, false);
163 return exact_iter
!= image_reps_
.end() ? exact_iter
: closest_iter
;
167 virtual ~ImageSkiaStorage() {
168 // We only care if the storage is modified by the same thread.
169 // Don't blow up even if someone else deleted the ImageSkia.
173 // Vector of bitmaps and their associated scale factor.
174 std::vector
<gfx::ImageSkiaRep
> image_reps_
;
176 scoped_ptr
<ImageSkiaSource
> source_
;
178 // Size of the image in DIP.
183 friend class base::RefCountedThreadSafe
<ImageSkiaStorage
>;
188 ImageSkia::ImageSkia() : storage_(NULL
) {
191 ImageSkia::ImageSkia(ImageSkiaSource
* source
, const gfx::Size
& size
)
192 : storage_(new internal::ImageSkiaStorage(source
, size
)) {
194 // No other thread has reference to this, so it's safe to detach the thread.
195 DetachStorageFromThread();
198 ImageSkia::ImageSkia(ImageSkiaSource
* source
, ui::ScaleFactor scale_factor
)
199 : storage_(new internal::ImageSkiaStorage(source
, scale_factor
)) {
201 if (!storage_
->has_source())
203 // No other thread has reference to this, so it's safe to detach the thread.
204 DetachStorageFromThread();
207 ImageSkia::ImageSkia(const ImageSkiaRep
& image_rep
) {
209 // No other thread has reference to this, so it's safe to detach the thread.
210 DetachStorageFromThread();
213 ImageSkia::ImageSkia(const ImageSkia
& other
) : storage_(other
.storage_
) {
216 ImageSkia
& ImageSkia::operator=(const ImageSkia
& other
) {
217 storage_
= other
.storage_
;
221 ImageSkia::~ImageSkia() {
225 ImageSkia
ImageSkia::CreateFrom1xBitmap(const SkBitmap
& bitmap
) {
226 return ImageSkia(ImageSkiaRep(bitmap
, ui::SCALE_FACTOR_100P
));
229 scoped_ptr
<ImageSkia
> ImageSkia::DeepCopy() const {
230 ImageSkia
* copy
= new ImageSkia
;
232 return scoped_ptr
<ImageSkia
>(copy
);
236 std::vector
<gfx::ImageSkiaRep
>& reps
= storage_
->image_reps();
237 for (std::vector
<gfx::ImageSkiaRep
>::iterator iter
= reps
.begin();
238 iter
!= reps
.end(); ++iter
) {
239 copy
->AddRepresentation(*iter
);
241 // The copy has its own storage. Detach the copy from the current
242 // thread so that other thread can use this.
244 copy
->storage_
->DetachFromThread();
245 return scoped_ptr
<ImageSkia
>(copy
);
248 bool ImageSkia::BackedBySameObjectAs(const gfx::ImageSkia
& other
) const {
249 return storage_
.get() == other
.storage_
.get();
252 void ImageSkia::AddRepresentation(const ImageSkiaRep
& image_rep
) {
253 DCHECK(!image_rep
.is_null());
255 // TODO(oshima): This method should be called |SetRepresentation|
256 // and replace the existing rep if there is already one with the
257 // same scale factor so that we can guarantee that a ImageSkia
258 // instance contians only one image rep per scale factor. This is
259 // not possible now as ImageLoader currently stores need
260 // this feature, but this needs to be fixed.
265 storage_
->image_reps().push_back(image_rep
);
269 void ImageSkia::RemoveRepresentation(ui::ScaleFactor scale_factor
) {
274 ImageSkiaReps
& image_reps
= storage_
->image_reps();
275 ImageSkiaReps::iterator it
=
276 storage_
->FindRepresentation(scale_factor
, false);
277 if (it
!= image_reps
.end() && it
->scale_factor() == scale_factor
)
278 image_reps
.erase(it
);
281 bool ImageSkia::HasRepresentation(ui::ScaleFactor scale_factor
) const {
286 ImageSkiaReps::iterator it
=
287 storage_
->FindRepresentation(scale_factor
, false);
288 return (it
!= storage_
->image_reps().end() &&
289 it
->scale_factor() == scale_factor
);
292 const ImageSkiaRep
& ImageSkia::GetRepresentation(
293 ui::ScaleFactor scale_factor
) const {
295 return NullImageRep();
299 ImageSkiaReps::iterator it
= storage_
->FindRepresentation(scale_factor
, true);
300 if (it
== storage_
->image_reps().end())
301 return NullImageRep();
306 void ImageSkia::SetReadOnly() {
308 storage_
->SetReadOnly();
309 DetachStorageFromThread();
312 void ImageSkia::MakeThreadSafe() {
314 EnsureRepsForSupportedScaleFactors();
315 // Delete source as we no longer needs it.
317 storage_
->DeleteSource();
318 storage_
->SetReadOnly();
319 CHECK(IsThreadSafe());
322 bool ImageSkia::IsThreadSafe() const {
323 return !storage_
|| (storage_
->read_only() && !storage_
->has_source());
326 int ImageSkia::width() const {
327 return isNull() ? 0 : storage_
->size().width();
330 gfx::Size
ImageSkia::size() const {
331 return gfx::Size(width(), height());
334 int ImageSkia::height() const {
335 return isNull() ? 0 : storage_
->size().height();
338 std::vector
<ImageSkiaRep
> ImageSkia::image_reps() const {
340 return std::vector
<ImageSkiaRep
>();
344 ImageSkiaReps internal_image_reps
= storage_
->image_reps();
345 // Create list of image reps to return, skipping null image reps which were
346 // added for caching purposes only.
347 ImageSkiaReps image_reps
;
348 for (ImageSkiaReps::iterator it
= internal_image_reps
.begin();
349 it
!= internal_image_reps
.end(); ++it
) {
351 image_reps
.push_back(*it
);
357 void ImageSkia::EnsureRepsForSupportedScaleFactors() const {
358 // Don't check ReadOnly because the source may generate images
359 // even for read only ImageSkia. Concurrent access will be protected
360 // by |DCHECK(CalledOnValidThread())| in FindRepresentation.
361 if (storage_
&& storage_
->has_source()) {
362 std::vector
<ui::ScaleFactor
> supported_scale_factors
=
363 ui::GetSupportedScaleFactors();
364 for (size_t i
= 0; i
< supported_scale_factors
.size(); ++i
)
365 storage_
->FindRepresentation(supported_scale_factors
[i
], true);
369 void ImageSkia::Init(const ImageSkiaRep
& image_rep
) {
370 // TODO(pkotwicz): The image should be null whenever image rep is null.
371 if (image_rep
.sk_bitmap().empty()) {
375 storage_
= new internal::ImageSkiaStorage(
376 NULL
, gfx::Size(image_rep
.GetWidth(), image_rep
.GetHeight()));
377 storage_
->image_reps().push_back(image_rep
);
380 SkBitmap
& ImageSkia::GetBitmap() const {
382 // Callers expect a ImageSkiaRep even if it is |isNull()|.
383 // TODO(pkotwicz): Fix this.
384 return NullImageRep().mutable_sk_bitmap();
387 // TODO(oshima): This made a few tests flaky on Windows.
388 // Fix the root cause and re-enable this. crbug.com/145623.
393 ImageSkiaReps::iterator it
=
394 storage_
->FindRepresentation(ui::SCALE_FACTOR_100P
, true);
395 if (it
!= storage_
->image_reps().end())
396 return it
->mutable_sk_bitmap();
397 return NullImageRep().mutable_sk_bitmap();
400 bool ImageSkia::CanRead() const {
401 return !storage_
|| storage_
->CanRead();
404 bool ImageSkia::CanModify() const {
405 return !storage_
|| storage_
->CanModify();
408 void ImageSkia::DetachStorageFromThread() {
410 storage_
->DetachFromThread();