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.h"
9 #include "base/logging.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/stl_util.h"
12 #include "third_party/skia/include/core/SkBitmap.h"
13 #include "ui/gfx/image/image_png_rep.h"
14 #include "ui/gfx/image/image_skia.h"
15 #include "ui/gfx/size.h"
18 #include "ui/gfx/codec/png_codec.h"
22 #include "base/mac/foundation_util.h"
23 #include "ui/gfx/image/image_skia_util_ios.h"
24 #elif defined(OS_MACOSX)
25 #include "base/mac/mac_util.h"
26 #include "ui/gfx/image/image_skia_util_mac.h"
34 scoped_refptr
<base::RefCountedMemory
> Get1xPNGBytesFromUIImage(
36 // Caller takes ownership of the returned UIImage.
37 UIImage
* CreateUIImageFromPNG(
38 const std::vector
<gfx::ImagePNGRep
>& image_png_reps
);
39 gfx::Size
UIImageSize(UIImage
* image
);
40 #elif defined(OS_MACOSX)
41 scoped_refptr
<base::RefCountedMemory
> Get1xPNGBytesFromNSImage(
43 // Caller takes ownership of the returned NSImage.
44 NSImage
* NSImageFromPNG(const std::vector
<gfx::ImagePNGRep
>& image_png_reps
,
45 CGColorSpaceRef color_space
);
46 gfx::Size
NSImageSize(NSImage
* image
);
47 #endif // defined(OS_MACOSX)
50 ImageSkia
* ImageSkiaFromPNG(
51 const std::vector
<gfx::ImagePNGRep
>& image_png_reps
);
52 scoped_refptr
<base::RefCountedMemory
> Get1xPNGBytesFromImageSkia(
53 const ImageSkia
* skia
);
55 // Returns a 16x16 red image to visually show error in decoding PNG.
56 // Caller takes ownership of returned ImageSkia.
57 ImageSkia
* GetErrorImageSkia() {
59 bitmap
.setConfig(SkBitmap::kARGB_8888_Config
, 16, 16);
61 bitmap
.eraseARGB(0xff, 0xff, 0, 0);
62 return new gfx::ImageSkia(gfx::ImageSkiaRep(bitmap
, 1.0f
));
65 ImageSkia
* ImageSkiaFromPNG(
66 const std::vector
<gfx::ImagePNGRep
>& image_png_reps
) {
67 if (image_png_reps
.empty())
68 return GetErrorImageSkia();
70 scoped_ptr
<gfx::ImageSkia
> image_skia(new ImageSkia());
71 for (size_t i
= 0; i
< image_png_reps
.size(); ++i
) {
72 scoped_refptr
<base::RefCountedMemory
> raw_data
=
73 image_png_reps
[i
].raw_data
;
74 CHECK(raw_data
.get());
76 if (!gfx::PNGCodec::Decode(raw_data
->front(), raw_data
->size(),
78 LOG(ERROR
) << "Unable to decode PNG for "
79 << image_png_reps
[i
].scale
81 return GetErrorImageSkia();
83 image_skia
->AddRepresentation(gfx::ImageSkiaRep(
84 bitmap
, image_png_reps
[i
].scale
));
86 return image_skia
.release();
89 scoped_refptr
<base::RefCountedMemory
> Get1xPNGBytesFromImageSkia(
90 const ImageSkia
* image_skia
) {
91 ImageSkiaRep image_skia_rep
= image_skia
->GetRepresentation(1.0f
);
93 scoped_refptr
<base::RefCountedBytes
> png_bytes(new base::RefCountedBytes());
94 if (image_skia_rep
.scale() != 1.0f
||
95 !gfx::PNGCodec::EncodeBGRASkBitmap(image_skia_rep
.sk_bitmap(), false,
96 &png_bytes
->data())) {
106 class ImageRepCocoaTouch
;
108 // An ImageRep is the object that holds the backing memory for an Image. Each
109 // RepresentationType has an ImageRep subclass that is responsible for freeing
110 // the memory that the ImageRep holds. When an ImageRep is created, it expects
111 // to take ownership of the image, without having to retain it or increase its
115 explicit ImageRep(Image::RepresentationType rep
) : type_(rep
) {}
117 // Deletes the associated pixels of an ImageRep.
118 virtual ~ImageRep() {}
120 // Cast helpers ("fake RTTI").
121 ImageRepPNG
* AsImageRepPNG() {
122 CHECK_EQ(type_
, Image::kImageRepPNG
);
123 return reinterpret_cast<ImageRepPNG
*>(this);
126 ImageRepSkia
* AsImageRepSkia() {
127 CHECK_EQ(type_
, Image::kImageRepSkia
);
128 return reinterpret_cast<ImageRepSkia
*>(this);
132 ImageRepCocoaTouch
* AsImageRepCocoaTouch() {
133 CHECK_EQ(type_
, Image::kImageRepCocoaTouch
);
134 return reinterpret_cast<ImageRepCocoaTouch
*>(this);
136 #elif defined(OS_MACOSX)
137 ImageRepCocoa
* AsImageRepCocoa() {
138 CHECK_EQ(type_
, Image::kImageRepCocoa
);
139 return reinterpret_cast<ImageRepCocoa
*>(this);
143 Image::RepresentationType
type() const { return type_
; }
145 virtual int Width() const = 0;
146 virtual int Height() const = 0;
147 virtual gfx::Size
Size() const = 0;
150 Image::RepresentationType type_
;
153 class ImageRepPNG
: public ImageRep
{
155 ImageRepPNG() : ImageRep(Image::kImageRepPNG
) {
158 ImageRepPNG(const std::vector
<ImagePNGRep
>& image_png_reps
)
159 : ImageRep(Image::kImageRepPNG
),
160 image_png_reps_(image_png_reps
) {
163 virtual ~ImageRepPNG() {
166 virtual int Width() const OVERRIDE
{
167 return Size().width();
170 virtual int Height() const OVERRIDE
{
171 return Size().height();
174 virtual gfx::Size
Size() const OVERRIDE
{
175 // Read the PNG data to get the image size, caching it.
177 for (std::vector
<ImagePNGRep
>::const_iterator it
= image_reps().begin();
178 it
!= image_reps().end(); ++it
) {
179 if (it
->scale
== 1.0f
) {
180 size_cache_
.reset(new gfx::Size(it
->Size()));
184 size_cache_
.reset(new gfx::Size
);
190 const std::vector
<ImagePNGRep
>& image_reps() const { return image_png_reps_
; }
193 std::vector
<ImagePNGRep
> image_png_reps_
;
195 // Cached to avoid having to parse the raw data multiple times.
196 mutable scoped_ptr
<gfx::Size
> size_cache_
;
198 DISALLOW_COPY_AND_ASSIGN(ImageRepPNG
);
201 class ImageRepSkia
: public ImageRep
{
203 // Takes ownership of |image|.
204 explicit ImageRepSkia(ImageSkia
* image
)
205 : ImageRep(Image::kImageRepSkia
),
209 virtual ~ImageRepSkia() {
212 virtual int Width() const OVERRIDE
{
213 return image_
->width();
216 virtual int Height() const OVERRIDE
{
217 return image_
->height();
220 virtual gfx::Size
Size() const OVERRIDE
{
221 return image_
->size();
224 ImageSkia
* image() { return image_
.get(); }
227 scoped_ptr
<ImageSkia
> image_
;
229 DISALLOW_COPY_AND_ASSIGN(ImageRepSkia
);
233 class ImageRepCocoaTouch
: public ImageRep
{
235 explicit ImageRepCocoaTouch(UIImage
* image
)
236 : ImageRep(Image::kImageRepCocoaTouch
),
241 virtual ~ImageRepCocoaTouch() {
242 base::mac::NSObjectRelease(image_
);
246 virtual int Width() const OVERRIDE
{
247 return Size().width();
250 virtual int Height() const OVERRIDE
{
251 return Size().height();
254 virtual gfx::Size
Size() const OVERRIDE
{
255 return internal::UIImageSize(image_
);
258 UIImage
* image() const { return image_
; }
263 DISALLOW_COPY_AND_ASSIGN(ImageRepCocoaTouch
);
265 #elif defined(OS_MACOSX)
266 class ImageRepCocoa
: public ImageRep
{
268 explicit ImageRepCocoa(NSImage
* image
)
269 : ImageRep(Image::kImageRepCocoa
),
274 virtual ~ImageRepCocoa() {
275 base::mac::NSObjectRelease(image_
);
279 virtual int Width() const OVERRIDE
{
280 return Size().width();
283 virtual int Height() const OVERRIDE
{
284 return Size().height();
287 virtual gfx::Size
Size() const OVERRIDE
{
288 return internal::NSImageSize(image_
);
291 NSImage
* image() const { return image_
; }
296 DISALLOW_COPY_AND_ASSIGN(ImageRepCocoa
);
298 #endif // defined(OS_MACOSX)
300 // The Storage class acts similarly to the pixels in a SkBitmap: the Image
301 // class holds a refptr instance of Storage, which in turn holds all the
302 // ImageReps. This way, the Image can be cheaply copied.
303 class ImageStorage
: public base::RefCounted
<ImageStorage
> {
305 ImageStorage(gfx::Image::RepresentationType default_type
)
306 : default_representation_type_(default_type
),
307 #if defined(OS_MACOSX) && !defined(OS_IOS)
308 default_representation_color_space_(
309 base::mac::GetGenericRGBColorSpace()),
310 #endif // defined(OS_MACOSX) && !defined(OS_IOS)
311 representations_deleter_(&representations_
) {
314 gfx::Image::RepresentationType
default_representation_type() {
315 return default_representation_type_
;
317 gfx::Image::RepresentationMap
& representations() { return representations_
; }
319 #if defined(OS_MACOSX) && !defined(OS_IOS)
320 void set_default_representation_color_space(CGColorSpaceRef color_space
) {
321 default_representation_color_space_
= color_space
;
323 CGColorSpaceRef
default_representation_color_space() {
324 return default_representation_color_space_
;
326 #endif // defined(OS_MACOSX) && !defined(OS_IOS)
329 friend class base::RefCounted
<ImageStorage
>;
333 // The type of image that was passed to the constructor. This key will always
334 // exist in the |representations_| map.
335 gfx::Image::RepresentationType default_representation_type_
;
337 #if defined(OS_MACOSX) && !defined(OS_IOS)
338 // The default representation's colorspace. This is used for converting to
339 // NSImage. This field exists to compensate for PNGCodec not writing or
340 // reading colorspace ancillary chunks. (sRGB, iCCP).
342 CGColorSpaceRef default_representation_color_space_
;
343 #endif // defined(OS_MACOSX) && !defined(OS_IOS)
345 // All the representations of an Image. Size will always be at least one, with
346 // more for any converted representations.
347 gfx::Image::RepresentationMap representations_
;
349 STLValueDeleter
<Image::RepresentationMap
> representations_deleter_
;
351 DISALLOW_COPY_AND_ASSIGN(ImageStorage
);
354 } // namespace internal
357 // |storage_| is NULL for empty Images.
360 Image::Image(const std::vector
<ImagePNGRep
>& image_reps
) {
361 // Do not store obviously invalid ImagePNGReps.
362 std::vector
<ImagePNGRep
> filtered
;
363 for (size_t i
= 0; i
< image_reps
.size(); ++i
) {
364 if (image_reps
[i
].raw_data
.get() && image_reps
[i
].raw_data
->size())
365 filtered
.push_back(image_reps
[i
]);
368 if (filtered
.empty())
371 storage_
= new internal::ImageStorage(Image::kImageRepPNG
);
372 internal::ImageRepPNG
* rep
= new internal::ImageRepPNG(filtered
);
373 AddRepresentation(rep
);
376 Image::Image(const ImageSkia
& image
) {
377 if (!image
.isNull()) {
378 storage_
= new internal::ImageStorage(Image::kImageRepSkia
);
379 internal::ImageRepSkia
* rep
= new internal::ImageRepSkia(
380 new ImageSkia(image
));
381 AddRepresentation(rep
);
386 Image::Image(UIImage
* image
)
387 : storage_(new internal::ImageStorage(Image::kImageRepCocoaTouch
)) {
389 internal::ImageRepCocoaTouch
* rep
= new internal::ImageRepCocoaTouch(image
);
390 AddRepresentation(rep
);
393 #elif defined(OS_MACOSX)
394 Image::Image(NSImage
* image
) {
396 storage_
= new internal::ImageStorage(Image::kImageRepCocoa
);
397 internal::ImageRepCocoa
* rep
= new internal::ImageRepCocoa(image
);
398 AddRepresentation(rep
);
403 Image::Image(const Image
& other
) : storage_(other
.storage_
) {
406 Image
& Image::operator=(const Image
& other
) {
407 storage_
= other
.storage_
;
415 Image
Image::CreateFrom1xBitmap(const SkBitmap
& bitmap
) {
416 return gfx::Image(ImageSkia::CreateFrom1xBitmap(bitmap
));
420 Image
Image::CreateFrom1xPNGBytes(const unsigned char* input
,
422 if (input_size
== 0u)
425 scoped_refptr
<base::RefCountedBytes
> raw_data(new base::RefCountedBytes());
426 raw_data
->data().assign(input
, input
+ input_size
);
428 return CreateFrom1xPNGBytes(raw_data
);
431 Image
Image::CreateFrom1xPNGBytes(
432 const scoped_refptr
<base::RefCountedMemory
>& input
) {
433 if (!input
.get() || input
->size() == 0u)
436 std::vector
<gfx::ImagePNGRep
> image_reps
;
437 image_reps
.push_back(ImagePNGRep(input
, 1.0f
));
438 return gfx::Image(image_reps
);
441 const SkBitmap
* Image::ToSkBitmap() const {
442 // Possibly create and cache an intermediate ImageRepSkia.
443 return ToImageSkia()->bitmap();
446 const ImageSkia
* Image::ToImageSkia() const {
447 internal::ImageRep
* rep
= GetRepresentation(kImageRepSkia
, false);
449 switch (DefaultRepresentationType()) {
451 internal::ImageRepPNG
* png_rep
=
452 GetRepresentation(kImageRepPNG
, true)->AsImageRepPNG();
453 rep
= new internal::ImageRepSkia(
454 internal::ImageSkiaFromPNG(png_rep
->image_reps()));
458 case kImageRepCocoaTouch
: {
459 internal::ImageRepCocoaTouch
* native_rep
=
460 GetRepresentation(kImageRepCocoaTouch
, true)
461 ->AsImageRepCocoaTouch();
462 rep
= new internal::ImageRepSkia(new ImageSkia(
463 ImageSkiaFromUIImage(native_rep
->image())));
466 #elif defined(OS_MACOSX)
467 case kImageRepCocoa
: {
468 internal::ImageRepCocoa
* native_rep
=
469 GetRepresentation(kImageRepCocoa
, true)->AsImageRepCocoa();
470 rep
= new internal::ImageRepSkia(new ImageSkia(
471 ImageSkiaFromNSImage(native_rep
->image())));
479 AddRepresentation(rep
);
481 return rep
->AsImageRepSkia()->image();
485 UIImage
* Image::ToUIImage() const {
486 internal::ImageRep
* rep
= GetRepresentation(kImageRepCocoaTouch
, false);
488 switch (DefaultRepresentationType()) {
490 internal::ImageRepPNG
* png_rep
=
491 GetRepresentation(kImageRepPNG
, true)->AsImageRepPNG();
492 rep
= new internal::ImageRepCocoaTouch(internal::CreateUIImageFromPNG(
493 png_rep
->image_reps()));
496 case kImageRepSkia
: {
497 internal::ImageRepSkia
* skia_rep
=
498 GetRepresentation(kImageRepSkia
, true)->AsImageRepSkia();
499 UIImage
* image
= UIImageFromImageSkia(*skia_rep
->image());
500 base::mac::NSObjectRetain(image
);
501 rep
= new internal::ImageRepCocoaTouch(image
);
508 AddRepresentation(rep
);
510 return rep
->AsImageRepCocoaTouch()->image();
512 #elif defined(OS_MACOSX)
513 NSImage
* Image::ToNSImage() const {
514 internal::ImageRep
* rep
= GetRepresentation(kImageRepCocoa
, false);
516 CGColorSpaceRef default_representation_color_space
=
517 storage_
->default_representation_color_space();
519 switch (DefaultRepresentationType()) {
521 internal::ImageRepPNG
* png_rep
=
522 GetRepresentation(kImageRepPNG
, true)->AsImageRepPNG();
523 rep
= new internal::ImageRepCocoa(internal::NSImageFromPNG(
524 png_rep
->image_reps(), default_representation_color_space
));
527 case kImageRepSkia
: {
528 internal::ImageRepSkia
* skia_rep
=
529 GetRepresentation(kImageRepSkia
, true)->AsImageRepSkia();
530 NSImage
* image
= NSImageFromImageSkiaWithColorSpace(*skia_rep
->image(),
531 default_representation_color_space
);
532 base::mac::NSObjectRetain(image
);
533 rep
= new internal::ImageRepCocoa(image
);
540 AddRepresentation(rep
);
542 return rep
->AsImageRepCocoa()->image();
546 scoped_refptr
<base::RefCountedMemory
> Image::As1xPNGBytes() const {
548 return new base::RefCountedBytes();
550 internal::ImageRep
* rep
= GetRepresentation(kImageRepPNG
, false);
553 const std::vector
<gfx::ImagePNGRep
>& image_png_reps
=
554 rep
->AsImageRepPNG()->image_reps();
555 for (size_t i
= 0; i
< image_png_reps
.size(); ++i
) {
556 if (image_png_reps
[i
].scale
== 1.0f
)
557 return image_png_reps
[i
].raw_data
;
559 return new base::RefCountedBytes();
562 scoped_refptr
<base::RefCountedMemory
> png_bytes(NULL
);
563 switch (DefaultRepresentationType()) {
565 case kImageRepCocoaTouch
: {
566 internal::ImageRepCocoaTouch
* cocoa_touch_rep
=
567 GetRepresentation(kImageRepCocoaTouch
, true)
568 ->AsImageRepCocoaTouch();
569 png_bytes
= internal::Get1xPNGBytesFromUIImage(
570 cocoa_touch_rep
->image());
573 #elif defined(OS_MACOSX)
574 case kImageRepCocoa
: {
575 internal::ImageRepCocoa
* cocoa_rep
=
576 GetRepresentation(kImageRepCocoa
, true)->AsImageRepCocoa();
577 png_bytes
= internal::Get1xPNGBytesFromNSImage(cocoa_rep
->image());
581 case kImageRepSkia
: {
582 internal::ImageRepSkia
* skia_rep
=
583 GetRepresentation(kImageRepSkia
, true)->AsImageRepSkia();
584 png_bytes
= internal::Get1xPNGBytesFromImageSkia(skia_rep
->image());
590 if (!png_bytes
.get() || !png_bytes
->size()) {
591 // Add an ImageRepPNG with no data such that the conversion is not
592 // attempted each time we want the PNG bytes.
593 AddRepresentation(new internal::ImageRepPNG());
594 return new base::RefCountedBytes();
597 // Do not insert representations for scale factors other than 1x even if
598 // they are available because:
599 // - Only the 1x PNG bytes can be accessed.
600 // - ImageRepPNG is not used as an intermediate type in converting to a
601 // final type eg (converting from ImageRepSkia to ImageRepPNG to get an
603 std::vector
<ImagePNGRep
> image_png_reps
;
604 image_png_reps
.push_back(gfx::ImagePNGRep(png_bytes
, 1.0f
));
605 rep
= new internal::ImageRepPNG(image_png_reps
);
606 AddRepresentation(rep
);
610 SkBitmap
Image::AsBitmap() const {
611 return IsEmpty() ? SkBitmap() : *ToSkBitmap();
614 ImageSkia
Image::AsImageSkia() const {
615 return IsEmpty() ? ImageSkia() : *ToImageSkia();
618 #if defined(OS_MACOSX) && !defined(OS_IOS)
619 NSImage
* Image::AsNSImage() const {
620 return IsEmpty() ? nil
: ToNSImage();
624 scoped_refptr
<base::RefCountedMemory
> Image::Copy1xPNGBytes() const {
625 scoped_refptr
<base::RefCountedMemory
> original
= As1xPNGBytes();
626 scoped_refptr
<base::RefCountedBytes
> copy(new base::RefCountedBytes());
627 copy
->data().assign(original
->front(), original
->front() + original
->size());
631 ImageSkia
* Image::CopyImageSkia() const {
632 return new ImageSkia(*ToImageSkia());
635 SkBitmap
* Image::CopySkBitmap() const {
636 return new SkBitmap(*ToSkBitmap());
640 UIImage
* Image::CopyUIImage() const {
641 UIImage
* image
= ToUIImage();
642 base::mac::NSObjectRetain(image
);
645 #elif defined(OS_MACOSX)
646 NSImage
* Image::CopyNSImage() const {
647 NSImage
* image
= ToNSImage();
648 base::mac::NSObjectRetain(image
);
653 bool Image::HasRepresentation(RepresentationType type
) const {
654 return storage_
.get() && storage_
->representations().count(type
) != 0;
657 size_t Image::RepresentationCount() const {
661 return storage_
->representations().size();
664 bool Image::IsEmpty() const {
665 return RepresentationCount() == 0;
668 int Image::Width() const {
671 return GetRepresentation(DefaultRepresentationType(), true)->Width();
674 int Image::Height() const {
677 return GetRepresentation(DefaultRepresentationType(), true)->Height();
680 gfx::Size
Image::Size() const {
683 return GetRepresentation(DefaultRepresentationType(), true)->Size();
686 void Image::SwapRepresentations(gfx::Image
* other
) {
687 storage_
.swap(other
->storage_
);
690 #if defined(OS_MACOSX) && !defined(OS_IOS)
691 void Image::SetSourceColorSpace(CGColorSpaceRef color_space
) {
693 storage_
->set_default_representation_color_space(color_space
);
695 #endif // defined(OS_MACOSX) && !defined(OS_IOS)
697 Image::RepresentationType
Image::DefaultRepresentationType() const {
698 CHECK(storage_
.get());
699 return storage_
->default_representation_type();
702 internal::ImageRep
* Image::GetRepresentation(
703 RepresentationType rep_type
, bool must_exist
) const {
704 CHECK(storage_
.get());
705 RepresentationMap::iterator it
= storage_
->representations().find(rep_type
);
706 if (it
== storage_
->representations().end()) {
713 void Image::AddRepresentation(internal::ImageRep
* rep
) const {
714 CHECK(storage_
.get());
715 storage_
->representations().insert(std::make_pair(rep
->type(), rep
));