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.
4
5 #include "ui/gfx/image/image.h"
6
7 #include <algorithm>
8
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"
16
17 #if !defined(OS_IOS)
18 #include "ui/gfx/codec/png_codec.h"
19 #endif
20
21 #if defined(TOOLKIT_GTK)
22 #include <gdk-pixbuf/gdk-pixbuf.h>
23 #include <gdk/gdk.h>
24 #include <glib-object.h>
25 #include "ui/gfx/canvas.h"
26 #include "ui/gfx/gtk_util.h"
27 #include "ui/gfx/image/cairo_cached_surface.h"
28 #include "ui/gfx/scoped_gobject.h"
29 #elif defined(OS_IOS)
30 #include "base/mac/foundation_util.h"
31 #include "ui/gfx/image/image_skia_util_ios.h"
32 #elif defined(OS_MACOSX)
33 #include "base/mac/mac_util.h"
34 #include "ui/gfx/image/image_skia_util_mac.h"
35 #endif
36
37 namespace gfx {
38
39 namespace internal {
40
41 #if defined(TOOLKIT_GTK)
ImageSkiaFromGdkPixbuf(GdkPixbuf * pixbuf)42 const ImageSkia ImageSkiaFromGdkPixbuf(GdkPixbuf* pixbuf) {
43 CHECK(pixbuf);
44 gfx::Canvas canvas(gfx::Size(gdk_pixbuf_get_width(pixbuf),
45 gdk_pixbuf_get_height(pixbuf)),
46 1.0f,
47 false);
48 skia::ScopedPlatformPaint scoped_platform_paint(canvas.sk_canvas());
49 cairo_t* cr = scoped_platform_paint.GetPlatformSurface();
50 gdk_cairo_set_source_pixbuf(cr, pixbuf, 0, 0);
51 cairo_paint(cr);
52 return ImageSkia(canvas.ExtractImageRep());
53 }
54
55 // Returns a 16x16 red pixbuf to visually show error in decoding PNG.
56 // Also logs error to console.
GetErrorPixbuf()57 GdkPixbuf* GetErrorPixbuf() {
58 LOG(ERROR) << "Unable to decode PNG.";
59 GdkPixbuf* pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, 16, 16);
60 gdk_pixbuf_fill(pixbuf, 0xff0000ff);
61 return pixbuf;
62 }
63
GdkPixbufFromPNG(const std::vector<gfx::ImagePNGRep> & image_png_reps)64 GdkPixbuf* GdkPixbufFromPNG(
65 const std::vector<gfx::ImagePNGRep>& image_png_reps) {
66 scoped_refptr<base::RefCountedMemory> png_bytes(NULL);
67 for (size_t i = 0; i < image_png_reps.size(); ++i) {
68 if (image_png_reps[i].scale == 1.0f)
69 png_bytes = image_png_reps[i].raw_data;
70 }
71
72 if (!png_bytes.get())
73 return GetErrorPixbuf();
74
75 GdkPixbuf* pixbuf = NULL;
76 ui::ScopedGObject<GdkPixbufLoader>::Type loader(gdk_pixbuf_loader_new());
77
78 bool ok = gdk_pixbuf_loader_write(loader.get(),
79 reinterpret_cast<const guint8*>(png_bytes->front()), png_bytes->size(),
80 NULL);
81
82 // Calling gdk_pixbuf_loader_close forces the data to be parsed by the
83 // loader. This must be done before calling gdk_pixbuf_loader_get_pixbuf.
84 if (ok)
85 ok = gdk_pixbuf_loader_close(loader.get(), NULL);
86 if (ok)
87 pixbuf = gdk_pixbuf_loader_get_pixbuf(loader.get());
88
89 if (pixbuf) {
90 // The pixbuf is owned by the scoped loader which will delete its ref when
91 // it goes out of scope. Add a ref so that the pixbuf still exists.
92 g_object_ref(pixbuf);
93 } else {
94 return GetErrorPixbuf();
95 }
96
97 return pixbuf;
98 }
99
Get1xPNGBytesFromPixbuf(GdkPixbuf * pixbuf)100 scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromPixbuf(
101 GdkPixbuf* pixbuf) {
102 gchar* image = NULL;
103 gsize image_size;
104 GError* error = NULL;
105 CHECK(gdk_pixbuf_save_to_buffer(
106 pixbuf, &image, &image_size, "png", &error, NULL));
107 scoped_refptr<base::RefCountedBytes> png_bytes(
108 new base::RefCountedBytes());
109 png_bytes->data().assign(image, image + image_size);
110 g_free(image);
111 return png_bytes;
112 }
113
114 #endif // defined(TOOLKIT_GTK)
115
116 #if defined(OS_IOS)
117 scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromUIImage(
118 UIImage* uiimage);
119 // Caller takes ownership of the returned UIImage.
120 UIImage* CreateUIImageFromPNG(
121 const std::vector<gfx::ImagePNGRep>& image_png_reps);
122 gfx::Size UIImageSize(UIImage* image);
123 #elif defined(OS_MACOSX)
124 scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromNSImage(
125 NSImage* nsimage);
126 // Caller takes ownership of the returned NSImage.
127 NSImage* NSImageFromPNG(const std::vector<gfx::ImagePNGRep>& image_png_reps,
128 CGColorSpaceRef color_space);
129 gfx::Size NSImageSize(NSImage* image);
130 #endif // defined(OS_MACOSX)
131
132 #if defined(OS_IOS)
133 ImageSkia* ImageSkiaFromPNG(
134 const std::vector<gfx::ImagePNGRep>& image_png_reps);
135 scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromImageSkia(
136 const ImageSkia* skia);
137 #else
138 // Returns a 16x16 red image to visually show error in decoding PNG.
139 // Caller takes ownership of returned ImageSkia.
GetErrorImageSkia()140 ImageSkia* GetErrorImageSkia() {
141 SkBitmap bitmap;
142 bitmap.setConfig(SkBitmap::kARGB_8888_Config, 16, 16);
143 bitmap.allocPixels();
144 bitmap.eraseRGB(0xff, 0, 0);
145 return new gfx::ImageSkia(gfx::ImageSkiaRep(bitmap, 1.0f));
146 }
147
ImageSkiaFromPNG(const std::vector<gfx::ImagePNGRep> & image_png_reps)148 ImageSkia* ImageSkiaFromPNG(
149 const std::vector<gfx::ImagePNGRep>& image_png_reps) {
150 if (image_png_reps.empty())
151 return GetErrorImageSkia();
152
153 scoped_ptr<gfx::ImageSkia> image_skia(new ImageSkia());
154 for (size_t i = 0; i < image_png_reps.size(); ++i) {
155 scoped_refptr<base::RefCountedMemory> raw_data =
156 image_png_reps[i].raw_data;
157 CHECK(raw_data.get());
158 SkBitmap bitmap;
159 if (!gfx::PNGCodec::Decode(raw_data->front(), raw_data->size(),
160 &bitmap)) {
161 LOG(ERROR) << "Unable to decode PNG for "
162 << image_png_reps[i].scale
163 << ".";
164 return GetErrorImageSkia();
165 }
166 image_skia->AddRepresentation(gfx::ImageSkiaRep(
167 bitmap, image_png_reps[i].scale));
168 }
169 return image_skia.release();
170 }
171
Get1xPNGBytesFromImageSkia(const ImageSkia * image_skia)172 scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromImageSkia(
173 const ImageSkia* image_skia) {
174 ImageSkiaRep image_skia_rep = image_skia->GetRepresentation(1.0f);
175
176 scoped_refptr<base::RefCountedBytes> png_bytes(new base::RefCountedBytes());
177 if (image_skia_rep.scale() != 1.0f ||
178 !gfx::PNGCodec::EncodeBGRASkBitmap(image_skia_rep.sk_bitmap(), false,
179 &png_bytes->data())) {
180 return NULL;
181 }
182 return png_bytes;
183 }
184 #endif
185
186 class ImageRepPNG;
187 class ImageRepSkia;
188 class ImageRepGdk;
189 class ImageRepCairo;
190 class ImageRepCocoa;
191 class ImageRepCocoaTouch;
192
193 // An ImageRep is the object that holds the backing memory for an Image. Each
194 // RepresentationType has an ImageRep subclass that is responsible for freeing
195 // the memory that the ImageRep holds. When an ImageRep is created, it expects
196 // to take ownership of the image, without having to retain it or increase its
197 // reference count.
198 class ImageRep {
199 public:
ImageRep(Image::RepresentationType rep)200 explicit ImageRep(Image::RepresentationType rep) : type_(rep) {}
201
202 // Deletes the associated pixels of an ImageRep.
~ImageRep()203 virtual ~ImageRep() {}
204
205 // Cast helpers ("fake RTTI").
AsImageRepPNG()206 ImageRepPNG* AsImageRepPNG() {
207 CHECK_EQ(type_, Image::kImageRepPNG);
208 return reinterpret_cast<ImageRepPNG*>(this);
209 }
210
AsImageRepSkia()211 ImageRepSkia* AsImageRepSkia() {
212 CHECK_EQ(type_, Image::kImageRepSkia);
213 return reinterpret_cast<ImageRepSkia*>(this);
214 }
215
216 #if defined(TOOLKIT_GTK)
AsImageRepGdk()217 ImageRepGdk* AsImageRepGdk() {
218 CHECK_EQ(type_, Image::kImageRepGdk);
219 return reinterpret_cast<ImageRepGdk*>(this);
220 }
221
AsImageRepCairo()222 ImageRepCairo* AsImageRepCairo() {
223 CHECK_EQ(type_, Image::kImageRepCairo);
224 return reinterpret_cast<ImageRepCairo*>(this);
225 }
226 #endif
227
228 #if defined(OS_IOS)
AsImageRepCocoaTouch()229 ImageRepCocoaTouch* AsImageRepCocoaTouch() {
230 CHECK_EQ(type_, Image::kImageRepCocoaTouch);
231 return reinterpret_cast<ImageRepCocoaTouch*>(this);
232 }
233 #elif defined(OS_MACOSX)
AsImageRepCocoa()234 ImageRepCocoa* AsImageRepCocoa() {
235 CHECK_EQ(type_, Image::kImageRepCocoa);
236 return reinterpret_cast<ImageRepCocoa*>(this);
237 }
238 #endif
239
type() const240 Image::RepresentationType type() const { return type_; }
241
242 virtual int Width() const = 0;
243 virtual int Height() const = 0;
244 virtual gfx::Size Size() const = 0;
245
246 private:
247 Image::RepresentationType type_;
248 };
249
250 class ImageRepPNG : public ImageRep {
251 public:
ImageRepPNG()252 ImageRepPNG() : ImageRep(Image::kImageRepPNG) {
253 }
254
ImageRepPNG(const std::vector<ImagePNGRep> & image_png_reps)255 ImageRepPNG(const std::vector<ImagePNGRep>& image_png_reps)
256 : ImageRep(Image::kImageRepPNG),
257 image_png_reps_(image_png_reps) {
258 }
259
~ImageRepPNG()260 virtual ~ImageRepPNG() {
261 }
262
Width() const263 virtual int Width() const OVERRIDE {
264 return Size().width();
265 }
266
Height() const267 virtual int Height() const OVERRIDE {
268 return Size().height();
269 }
270
Size() const271 virtual gfx::Size Size() const OVERRIDE {
272 // Read the PNG data to get the image size, caching it.
273 if (!size_cache_) {
274 for (std::vector<ImagePNGRep>::const_iterator it = image_reps().begin();
275 it != image_reps().end(); ++it) {
276 if (it->scale == 1.0f) {
277 size_cache_.reset(new gfx::Size(it->Size()));
278 return *size_cache_;
279 }
280 }
281 size_cache_.reset(new gfx::Size);
282 }
283
284 return *size_cache_;
285 }
286
image_reps() const287 const std::vector<ImagePNGRep>& image_reps() const { return image_png_reps_; }
288
289 private:
290 std::vector<ImagePNGRep> image_png_reps_;
291
292 // Cached to avoid having to parse the raw data multiple times.
293 mutable scoped_ptr<gfx::Size> size_cache_;
294
295 DISALLOW_COPY_AND_ASSIGN(ImageRepPNG);
296 };
297
298 class ImageRepSkia : public ImageRep {
299 public:
300 // Takes ownership of |image|.
ImageRepSkia(ImageSkia * image)301 explicit ImageRepSkia(ImageSkia* image)
302 : ImageRep(Image::kImageRepSkia),
303 image_(image) {
304 }
305
~ImageRepSkia()306 virtual ~ImageRepSkia() {
307 }
308
Width() const309 virtual int Width() const OVERRIDE {
310 return image_->width();
311 }
312
Height() const313 virtual int Height() const OVERRIDE {
314 return image_->height();
315 }
316
Size() const317 virtual gfx::Size Size() const OVERRIDE {
318 return image_->size();
319 }
320
image()321 ImageSkia* image() { return image_.get(); }
322
323 private:
324 scoped_ptr<ImageSkia> image_;
325
326 DISALLOW_COPY_AND_ASSIGN(ImageRepSkia);
327 };
328
329 #if defined(TOOLKIT_GTK)
330 class ImageRepGdk : public ImageRep {
331 public:
ImageRepGdk(GdkPixbuf * pixbuf)332 explicit ImageRepGdk(GdkPixbuf* pixbuf)
333 : ImageRep(Image::kImageRepGdk),
334 pixbuf_(pixbuf) {
335 CHECK(pixbuf);
336 }
337
~ImageRepGdk()338 virtual ~ImageRepGdk() {
339 if (pixbuf_) {
340 g_object_unref(pixbuf_);
341 pixbuf_ = NULL;
342 }
343 }
344
Width() const345 virtual int Width() const OVERRIDE {
346 return gdk_pixbuf_get_width(pixbuf_);
347 }
348
Height() const349 virtual int Height() const OVERRIDE {
350 return gdk_pixbuf_get_height(pixbuf_);
351 }
352
Size() const353 virtual gfx::Size Size() const OVERRIDE {
354 return gfx::Size(Width(), Height());
355 }
356
pixbuf() const357 GdkPixbuf* pixbuf() const { return pixbuf_; }
358
359 private:
360 GdkPixbuf* pixbuf_;
361
362 DISALLOW_COPY_AND_ASSIGN(ImageRepGdk);
363 };
364
365 // Represents data that lives on the display server instead of in the client.
366 class ImageRepCairo : public ImageRep {
367 public:
ImageRepCairo(GdkPixbuf * pixbuf)368 explicit ImageRepCairo(GdkPixbuf* pixbuf)
369 : ImageRep(Image::kImageRepCairo),
370 cairo_cache_(new CairoCachedSurface) {
371 CHECK(pixbuf);
372 cairo_cache_->UsePixbuf(pixbuf);
373 }
374
~ImageRepCairo()375 virtual ~ImageRepCairo() {
376 delete cairo_cache_;
377 }
378
Width() const379 virtual int Width() const OVERRIDE {
380 return cairo_cache_->Width();
381 }
382
Height() const383 virtual int Height() const OVERRIDE {
384 return cairo_cache_->Height();
385 }
386
Size() const387 virtual gfx::Size Size() const OVERRIDE {
388 return gfx::Size(Width(), Height());
389 }
390
surface() const391 CairoCachedSurface* surface() const { return cairo_cache_; }
392
393 private:
394 CairoCachedSurface* cairo_cache_;
395
396 DISALLOW_COPY_AND_ASSIGN(ImageRepCairo);
397 };
398 #endif // defined(TOOLKIT_GTK)
399
400 #if defined(OS_IOS)
401 class ImageRepCocoaTouch : public ImageRep {
402 public:
ImageRepCocoaTouch(UIImage * image)403 explicit ImageRepCocoaTouch(UIImage* image)
404 : ImageRep(Image::kImageRepCocoaTouch),
405 image_(image) {
406 CHECK(image);
407 }
408
~ImageRepCocoaTouch()409 virtual ~ImageRepCocoaTouch() {
410 base::mac::NSObjectRelease(image_);
411 image_ = nil;
412 }
413
Width() const414 virtual int Width() const OVERRIDE {
415 return Size().width();
416 }
417
Height() const418 virtual int Height() const OVERRIDE {
419 return Size().height();
420 }
421
Size() const422 virtual gfx::Size Size() const OVERRIDE {
423 return internal::UIImageSize(image_);
424 }
425
image() const426 UIImage* image() const { return image_; }
427
428 private:
429 UIImage* image_;
430
431 DISALLOW_COPY_AND_ASSIGN(ImageRepCocoaTouch);
432 };
433 #elif defined(OS_MACOSX)
434 class ImageRepCocoa : public ImageRep {
435 public:
ImageRepCocoa(NSImage * image)436 explicit ImageRepCocoa(NSImage* image)
437 : ImageRep(Image::kImageRepCocoa),
438 image_(image) {
439 CHECK(image);
440 }
441
~ImageRepCocoa()442 virtual ~ImageRepCocoa() {
443 base::mac::NSObjectRelease(image_);
444 image_ = nil;
445 }
446
Width() const447 virtual int Width() const OVERRIDE {
448 return Size().width();
449 }
450
Height() const451 virtual int Height() const OVERRIDE {
452 return Size().height();
453 }
454
Size() const455 virtual gfx::Size Size() const OVERRIDE {
456 return internal::NSImageSize(image_);
457 }
458
image() const459 NSImage* image() const { return image_; }
460
461 private:
462 NSImage* image_;
463
464 DISALLOW_COPY_AND_ASSIGN(ImageRepCocoa);
465 };
466 #endif // defined(OS_MACOSX)
467
468 // The Storage class acts similarly to the pixels in a SkBitmap: the Image
469 // class holds a refptr instance of Storage, which in turn holds all the
470 // ImageReps. This way, the Image can be cheaply copied.
471 class ImageStorage : public base::RefCounted<ImageStorage> {
472 public:
ImageStorage(gfx::Image::RepresentationType default_type)473 ImageStorage(gfx::Image::RepresentationType default_type)
474 : default_representation_type_(default_type),
475 #if defined(OS_MACOSX) && !defined(OS_IOS)
476 default_representation_color_space_(
477 base::mac::GetGenericRGBColorSpace()),
478 #endif // defined(OS_MACOSX) && !defined(OS_IOS)
479 representations_deleter_(&representations_) {
480 }
481
default_representation_type()482 gfx::Image::RepresentationType default_representation_type() {
483 return default_representation_type_;
484 }
representations()485 gfx::Image::RepresentationMap& representations() { return representations_; }
486
487 #if defined(OS_MACOSX) && !defined(OS_IOS)
set_default_representation_color_space(CGColorSpaceRef color_space)488 void set_default_representation_color_space(CGColorSpaceRef color_space) {
489 default_representation_color_space_ = color_space;
490 }
default_representation_color_space()491 CGColorSpaceRef default_representation_color_space() {
492 return default_representation_color_space_;
493 }
494 #endif // defined(OS_MACOSX) && !defined(OS_IOS)
495
496 private:
497 friend class base::RefCounted<ImageStorage>;
498
~ImageStorage()499 ~ImageStorage() {}
500
501 // The type of image that was passed to the constructor. This key will always
502 // exist in the |representations_| map.
503 gfx::Image::RepresentationType default_representation_type_;
504
505 #if defined(OS_MACOSX) && !defined(OS_IOS)
506 // The default representation's colorspace. This is used for converting to
507 // NSImage. This field exists to compensate for PNGCodec not writing or
508 // reading colorspace ancillary chunks. (sRGB, iCCP).
509 // Not owned.
510 CGColorSpaceRef default_representation_color_space_;
511 #endif // defined(OS_MACOSX) && !defined(OS_IOS)
512
513 // All the representations of an Image. Size will always be at least one, with
514 // more for any converted representations.
515 gfx::Image::RepresentationMap representations_;
516
517 STLValueDeleter<Image::RepresentationMap> representations_deleter_;
518
519 DISALLOW_COPY_AND_ASSIGN(ImageStorage);
520 };
521
522 } // namespace internal
523
Image()524 Image::Image() {
525 // |storage_| is NULL for empty Images.
526 }
527
Image(const std::vector<ImagePNGRep> & image_reps)528 Image::Image(const std::vector<ImagePNGRep>& image_reps) {
529 // Do not store obviously invalid ImagePNGReps.
530 std::vector<ImagePNGRep> filtered;
531 for (size_t i = 0; i < image_reps.size(); ++i) {
532 if (image_reps[i].raw_data.get() && image_reps[i].raw_data->size())
533 filtered.push_back(image_reps[i]);
534 }
535
536 if (filtered.empty())
537 return;
538
539 storage_ = new internal::ImageStorage(Image::kImageRepPNG);
540 internal::ImageRepPNG* rep = new internal::ImageRepPNG(filtered);
541 AddRepresentation(rep);
542 }
543
Image(const ImageSkia & image)544 Image::Image(const ImageSkia& image) {
545 if (!image.isNull()) {
546 storage_ = new internal::ImageStorage(Image::kImageRepSkia);
547 internal::ImageRepSkia* rep = new internal::ImageRepSkia(
548 new ImageSkia(image));
549 AddRepresentation(rep);
550 }
551 }
552
553 #if defined(TOOLKIT_GTK)
Image(GdkPixbuf * pixbuf)554 Image::Image(GdkPixbuf* pixbuf) {
555 if (pixbuf) {
556 storage_ = new internal::ImageStorage(Image::kImageRepGdk);
557 internal::ImageRepGdk* rep = new internal::ImageRepGdk(pixbuf);
558 AddRepresentation(rep);
559 }
560 }
561 #endif
562
563 #if defined(OS_IOS)
Image(UIImage * image)564 Image::Image(UIImage* image)
565 : storage_(new internal::ImageStorage(Image::kImageRepCocoaTouch)) {
566 if (image) {
567 internal::ImageRepCocoaTouch* rep = new internal::ImageRepCocoaTouch(image);
568 AddRepresentation(rep);
569 }
570 }
571 #elif defined(OS_MACOSX)
Image(NSImage * image)572 Image::Image(NSImage* image) {
573 if (image) {
574 storage_ = new internal::ImageStorage(Image::kImageRepCocoa);
575 internal::ImageRepCocoa* rep = new internal::ImageRepCocoa(image);
576 AddRepresentation(rep);
577 }
578 }
579 #endif
580
Image(const Image & other)581 Image::Image(const Image& other) : storage_(other.storage_) {
582 }
583
operator =(const Image & other)584 Image& Image::operator=(const Image& other) {
585 storage_ = other.storage_;
586 return *this;
587 }
588
~Image()589 Image::~Image() {
590 }
591
592 // static
CreateFrom1xBitmap(const SkBitmap & bitmap)593 Image Image::CreateFrom1xBitmap(const SkBitmap& bitmap) {
594 return gfx::Image(ImageSkia::CreateFrom1xBitmap(bitmap));
595 }
596
597 // static
CreateFrom1xPNGBytes(const unsigned char * input,size_t input_size)598 Image Image::CreateFrom1xPNGBytes(const unsigned char* input,
599 size_t input_size) {
600 if (input_size == 0u)
601 return gfx::Image();
602
603 scoped_refptr<base::RefCountedBytes> raw_data(new base::RefCountedBytes());
604 raw_data->data().assign(input, input + input_size);
605 std::vector<gfx::ImagePNGRep> image_reps;
606 image_reps.push_back(ImagePNGRep(raw_data, 1.0f));
607 return gfx::Image(image_reps);
608 }
609
ToSkBitmap() const610 const SkBitmap* Image::ToSkBitmap() const {
611 // Possibly create and cache an intermediate ImageRepSkia.
612 return ToImageSkia()->bitmap();
613 }
614
ToImageSkia() const615 const ImageSkia* Image::ToImageSkia() const {
616 internal::ImageRep* rep = GetRepresentation(kImageRepSkia, false);
617 if (!rep) {
618 switch (DefaultRepresentationType()) {
619 case kImageRepPNG: {
620 internal::ImageRepPNG* png_rep =
621 GetRepresentation(kImageRepPNG, true)->AsImageRepPNG();
622 rep = new internal::ImageRepSkia(
623 internal::ImageSkiaFromPNG(png_rep->image_reps()));
624 break;
625 }
626 #if defined(TOOLKIT_GTK)
627 case kImageRepGdk: {
628 internal::ImageRepGdk* native_rep =
629 GetRepresentation(kImageRepGdk, true)->AsImageRepGdk();
630 rep = new internal::ImageRepSkia(new ImageSkia(
631 internal::ImageSkiaFromGdkPixbuf(native_rep->pixbuf())));
632 break;
633 }
634 #elif defined(OS_IOS)
635 case kImageRepCocoaTouch: {
636 internal::ImageRepCocoaTouch* native_rep =
637 GetRepresentation(kImageRepCocoaTouch, true)
638 ->AsImageRepCocoaTouch();
639 rep = new internal::ImageRepSkia(new ImageSkia(
640 ImageSkiaFromUIImage(native_rep->image())));
641 break;
642 }
643 #elif defined(OS_MACOSX)
644 case kImageRepCocoa: {
645 internal::ImageRepCocoa* native_rep =
646 GetRepresentation(kImageRepCocoa, true)->AsImageRepCocoa();
647 rep = new internal::ImageRepSkia(new ImageSkia(
648 ImageSkiaFromNSImage(native_rep->image())));
649 break;
650 }
651 #endif
652 default:
653 NOTREACHED();
654 }
655 CHECK(rep);
656 AddRepresentation(rep);
657 }
658 return rep->AsImageRepSkia()->image();
659 }
660
661 #if defined(TOOLKIT_GTK)
ToGdkPixbuf() const662 GdkPixbuf* Image::ToGdkPixbuf() const {
663 internal::ImageRep* rep = GetRepresentation(kImageRepGdk, false);
664 if (!rep) {
665 switch (DefaultRepresentationType()) {
666 case kImageRepPNG: {
667 internal::ImageRepPNG* png_rep =
668 GetRepresentation(kImageRepPNG, true)->AsImageRepPNG();
669 rep = new internal::ImageRepGdk(internal::GdkPixbufFromPNG(
670 png_rep->image_reps()));
671 break;
672 }
673 case kImageRepSkia: {
674 internal::ImageRepSkia* skia_rep =
675 GetRepresentation(kImageRepSkia, true)->AsImageRepSkia();
676 rep = new internal::ImageRepGdk(gfx::GdkPixbufFromSkBitmap(
677 *skia_rep->image()->bitmap()));
678 break;
679 }
680 default:
681 NOTREACHED();
682 }
683 CHECK(rep);
684 AddRepresentation(rep);
685 }
686 return rep->AsImageRepGdk()->pixbuf();
687 }
688
ToCairo() const689 CairoCachedSurface* const Image::ToCairo() const {
690 internal::ImageRep* rep = GetRepresentation(kImageRepCairo, false);
691 if (!rep) {
692 // Handle any-to-Cairo conversion. This may create and cache an intermediate
693 // pixbuf before sending the data to the display server.
694 rep = new internal::ImageRepCairo(ToGdkPixbuf());
695 CHECK(rep);
696 AddRepresentation(rep);
697 }
698 return rep->AsImageRepCairo()->surface();
699 }
700 #endif
701
702 #if defined(OS_IOS)
ToUIImage() const703 UIImage* Image::ToUIImage() const {
704 internal::ImageRep* rep = GetRepresentation(kImageRepCocoaTouch, false);
705 if (!rep) {
706 switch (DefaultRepresentationType()) {
707 case kImageRepPNG: {
708 internal::ImageRepPNG* png_rep =
709 GetRepresentation(kImageRepPNG, true)->AsImageRepPNG();
710 rep = new internal::ImageRepCocoaTouch(internal::CreateUIImageFromPNG(
711 png_rep->image_reps()));
712 break;
713 }
714 case kImageRepSkia: {
715 internal::ImageRepSkia* skia_rep =
716 GetRepresentation(kImageRepSkia, true)->AsImageRepSkia();
717 UIImage* image = UIImageFromImageSkia(*skia_rep->image());
718 base::mac::NSObjectRetain(image);
719 rep = new internal::ImageRepCocoaTouch(image);
720 break;
721 }
722 default:
723 NOTREACHED();
724 }
725 CHECK(rep);
726 AddRepresentation(rep);
727 }
728 return rep->AsImageRepCocoaTouch()->image();
729 }
730 #elif defined(OS_MACOSX)
ToNSImage() const731 NSImage* Image::ToNSImage() const {
732 internal::ImageRep* rep = GetRepresentation(kImageRepCocoa, false);
733 if (!rep) {
734 CGColorSpaceRef default_representation_color_space =
735 storage_->default_representation_color_space();
736
737 switch (DefaultRepresentationType()) {
738 case kImageRepPNG: {
739 internal::ImageRepPNG* png_rep =
740 GetRepresentation(kImageRepPNG, true)->AsImageRepPNG();
741 rep = new internal::ImageRepCocoa(internal::NSImageFromPNG(
742 png_rep->image_reps(), default_representation_color_space));
743 break;
744 }
745 case kImageRepSkia: {
746 internal::ImageRepSkia* skia_rep =
747 GetRepresentation(kImageRepSkia, true)->AsImageRepSkia();
748 NSImage* image = NSImageFromImageSkiaWithColorSpace(*skia_rep->image(),
749 default_representation_color_space);
750 base::mac::NSObjectRetain(image);
751 rep = new internal::ImageRepCocoa(image);
752 break;
753 }
754 default:
755 NOTREACHED();
756 }
757 CHECK(rep);
758 AddRepresentation(rep);
759 }
760 return rep->AsImageRepCocoa()->image();
761 }
762 #endif
763
As1xPNGBytes() const764 scoped_refptr<base::RefCountedMemory> Image::As1xPNGBytes() const {
765 if (IsEmpty())
766 return new base::RefCountedBytes();
767
768 internal::ImageRep* rep = GetRepresentation(kImageRepPNG, false);
769
770 if (rep) {
771 const std::vector<gfx::ImagePNGRep>& image_png_reps =
772 rep->AsImageRepPNG()->image_reps();
773 for (size_t i = 0; i < image_png_reps.size(); ++i) {
774 if (image_png_reps[i].scale == 1.0f)
775 return image_png_reps[i].raw_data;
776 }
777 return new base::RefCountedBytes();
778 }
779
780 scoped_refptr<base::RefCountedMemory> png_bytes(NULL);
781 switch (DefaultRepresentationType()) {
782 #if defined(TOOLKIT_GTK)
783 case kImageRepGdk: {
784 internal::ImageRepGdk* gdk_rep =
785 GetRepresentation(kImageRepGdk, true)->AsImageRepGdk();
786 png_bytes = internal::Get1xPNGBytesFromPixbuf(gdk_rep->pixbuf());
787 break;
788 }
789 #elif defined(OS_IOS)
790 case kImageRepCocoaTouch: {
791 internal::ImageRepCocoaTouch* cocoa_touch_rep =
792 GetRepresentation(kImageRepCocoaTouch, true)
793 ->AsImageRepCocoaTouch();
794 png_bytes = internal::Get1xPNGBytesFromUIImage(
795 cocoa_touch_rep->image());
796 break;
797 }
798 #elif defined(OS_MACOSX)
799 case kImageRepCocoa: {
800 internal::ImageRepCocoa* cocoa_rep =
801 GetRepresentation(kImageRepCocoa, true)->AsImageRepCocoa();
802 png_bytes = internal::Get1xPNGBytesFromNSImage(cocoa_rep->image());
803 break;
804 }
805 #endif
806 case kImageRepSkia: {
807 internal::ImageRepSkia* skia_rep =
808 GetRepresentation(kImageRepSkia, true)->AsImageRepSkia();
809 png_bytes = internal::Get1xPNGBytesFromImageSkia(skia_rep->image());
810 break;
811 }
812 default:
813 NOTREACHED();
814 }
815 if (!png_bytes.get() || !png_bytes->size()) {
816 // Add an ImageRepPNG with no data such that the conversion is not
817 // attempted each time we want the PNG bytes.
818 AddRepresentation(new internal::ImageRepPNG());
819 return new base::RefCountedBytes();
820 }
821
822 // Do not insert representations for scale factors other than 1x even if
823 // they are available because:
824 // - Only the 1x PNG bytes can be accessed.
825 // - ImageRepPNG is not used as an intermediate type in converting to a
826 // final type eg (converting from ImageRepSkia to ImageRepPNG to get an
827 // ImageRepCocoa).
828 std::vector<ImagePNGRep> image_png_reps;
829 image_png_reps.push_back(gfx::ImagePNGRep(png_bytes, 1.0f));
830 rep = new internal::ImageRepPNG(image_png_reps);
831 AddRepresentation(rep);
832 return png_bytes;
833 }
834
AsBitmap() const835 SkBitmap Image::AsBitmap() const {
836 return IsEmpty() ? SkBitmap() : *ToSkBitmap();
837 }
838
AsImageSkia() const839 ImageSkia Image::AsImageSkia() const {
840 return IsEmpty() ? ImageSkia() : *ToImageSkia();
841 }
842
843 #if defined(OS_MACOSX) && !defined(OS_IOS)
AsNSImage() const844 NSImage* Image::AsNSImage() const {
845 return IsEmpty() ? nil : ToNSImage();
846 }
847 #endif
848
Copy1xPNGBytes() const849 scoped_refptr<base::RefCountedMemory> Image::Copy1xPNGBytes() const {
850 scoped_refptr<base::RefCountedMemory> original = As1xPNGBytes();
851 scoped_refptr<base::RefCountedBytes> copy(new base::RefCountedBytes());
852 copy->data().assign(original->front(), original->front() + original->size());
853 return copy;
854 }
855
CopyImageSkia() const856 ImageSkia* Image::CopyImageSkia() const {
857 return new ImageSkia(*ToImageSkia());
858 }
859
CopySkBitmap() const860 SkBitmap* Image::CopySkBitmap() const {
861 return new SkBitmap(*ToSkBitmap());
862 }
863
864 #if defined(TOOLKIT_GTK)
CopyGdkPixbuf() const865 GdkPixbuf* Image::CopyGdkPixbuf() const {
866 GdkPixbuf* pixbuf = ToGdkPixbuf();
867 g_object_ref(pixbuf);
868 return pixbuf;
869 }
870 #endif
871
872 #if defined(OS_IOS)
CopyUIImage() const873 UIImage* Image::CopyUIImage() const {
874 UIImage* image = ToUIImage();
875 base::mac::NSObjectRetain(image);
876 return image;
877 }
878 #elif defined(OS_MACOSX)
CopyNSImage() const879 NSImage* Image::CopyNSImage() const {
880 NSImage* image = ToNSImage();
881 base::mac::NSObjectRetain(image);
882 return image;
883 }
884 #endif
885
HasRepresentation(RepresentationType type) const886 bool Image::HasRepresentation(RepresentationType type) const {
887 return storage_.get() && storage_->representations().count(type) != 0;
888 }
889
RepresentationCount() const890 size_t Image::RepresentationCount() const {
891 if (!storage_.get())
892 return 0;
893
894 return storage_->representations().size();
895 }
896
IsEmpty() const897 bool Image::IsEmpty() const {
898 return RepresentationCount() == 0;
899 }
900
Width() const901 int Image::Width() const {
902 if (IsEmpty())
903 return 0;
904 return GetRepresentation(DefaultRepresentationType(), true)->Width();
905 }
906
Height() const907 int Image::Height() const {
908 if (IsEmpty())
909 return 0;
910 return GetRepresentation(DefaultRepresentationType(), true)->Height();
911 }
912
Size() const913 gfx::Size Image::Size() const {
914 if (IsEmpty())
915 return gfx::Size();
916 return GetRepresentation(DefaultRepresentationType(), true)->Size();
917 }
918
SwapRepresentations(gfx::Image * other)919 void Image::SwapRepresentations(gfx::Image* other) {
920 storage_.swap(other->storage_);
921 }
922
923 #if defined(OS_MACOSX) && !defined(OS_IOS)
SetSourceColorSpace(CGColorSpaceRef color_space)924 void Image::SetSourceColorSpace(CGColorSpaceRef color_space) {
925 if (storage_.get())
926 storage_->set_default_representation_color_space(color_space);
927 }
928 #endif // defined(OS_MACOSX) && !defined(OS_IOS)
929
DefaultRepresentationType() const930 Image::RepresentationType Image::DefaultRepresentationType() const {
931 CHECK(storage_.get());
932 RepresentationType default_type = storage_->default_representation_type();
933 // The conversions above assume that the default representation type is never
934 // kImageRepCairo.
935 DCHECK_NE(default_type, kImageRepCairo);
936 return default_type;
937 }
938
GetRepresentation(RepresentationType rep_type,bool must_exist) const939 internal::ImageRep* Image::GetRepresentation(
940 RepresentationType rep_type, bool must_exist) const {
941 CHECK(storage_.get());
942 RepresentationMap::iterator it = storage_->representations().find(rep_type);
943 if (it == storage_->representations().end()) {
944 CHECK(!must_exist);
945 return NULL;
946 }
947 return it->second;
948 }
949
AddRepresentation(internal::ImageRep * rep) const950 void Image::AddRepresentation(internal::ImageRep* rep) const {
951 CHECK(storage_.get());
952 storage_->representations().insert(std::make_pair(rep->type(), rep));
953 }
954
955 } // namespace gfx
956