| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/gfx/image.h" |
| |
| #include <algorithm> |
| |
| #include "base/logging.h" |
| #include "third_party/skia/include/core/SkBitmap.h" |
| |
| #if defined(OS_LINUX) |
| #include <gdk-pixbuf/gdk-pixbuf.h> |
| #include <glib-object.h> |
| #include "ui/gfx/canvas_skia.h" |
| #include "ui/gfx/gtk_util.h" |
| #elif defined(OS_MACOSX) |
| #include "base/mac/mac_util.h" |
| #include "skia/ext/skia_utils_mac.h" |
| #endif |
| |
| namespace gfx { |
| |
| namespace internal { |
| |
| #if defined(OS_MACOSX) |
| // This is a wrapper around gfx::NSImageToSkBitmap() because this cross-platform |
| // file cannot include the [square brackets] of ObjC. |
| const SkBitmap* NSImageToSkBitmap(NSImage* image); |
| #endif |
| |
| #if defined(OS_LINUX) |
| const SkBitmap* GdkPixbufToSkBitmap(GdkPixbuf* pixbuf) { |
| gfx::CanvasSkia canvas(gdk_pixbuf_get_width(pixbuf), |
| gdk_pixbuf_get_height(pixbuf), |
| /*is_opaque=*/false); |
| canvas.DrawGdkPixbuf(pixbuf, 0, 0); |
| return new SkBitmap(canvas.ExtractBitmap()); |
| } |
| #endif |
| |
| class SkBitmapRep; |
| class GdkPixbufRep; |
| class NSImageRep; |
| |
| // An ImageRep is the object that holds the backing memory for an Image. Each |
| // RepresentationType has an ImageRep subclass that is responsible for freeing |
| // the memory that the ImageRep holds. When an ImageRep is created, it expects |
| // to take ownership of the image, without having to retain it or increase its |
| // reference count. |
| class ImageRep { |
| public: |
| explicit ImageRep(Image::RepresentationType rep) : type_(rep) {} |
| |
| // Deletes the associated pixels of an ImageRep. |
| virtual ~ImageRep() {} |
| |
| // Cast helpers ("fake RTTI"). |
| SkBitmapRep* AsSkBitmapRep() { |
| CHECK_EQ(type_, Image::kSkBitmapRep); |
| return reinterpret_cast<SkBitmapRep*>(this); |
| } |
| |
| #if defined(OS_LINUX) |
| GdkPixbufRep* AsGdkPixbufRep() { |
| CHECK_EQ(type_, Image::kGdkPixbufRep); |
| return reinterpret_cast<GdkPixbufRep*>(this); |
| } |
| #endif |
| |
| #if defined(OS_MACOSX) |
| NSImageRep* AsNSImageRep() { |
| CHECK_EQ(type_, Image::kNSImageRep); |
| return reinterpret_cast<NSImageRep*>(this); |
| } |
| #endif |
| |
| Image::RepresentationType type() const { return type_; } |
| |
| private: |
| Image::RepresentationType type_; |
| }; |
| |
| class SkBitmapRep : public ImageRep { |
| public: |
| explicit SkBitmapRep(const SkBitmap* bitmap) |
| : ImageRep(Image::kSkBitmapRep), |
| bitmap_(bitmap) { |
| CHECK(bitmap); |
| } |
| |
| virtual ~SkBitmapRep() { |
| delete bitmap_; |
| bitmap_ = NULL; |
| } |
| |
| const SkBitmap* bitmap() const { return bitmap_; } |
| |
| private: |
| const SkBitmap* bitmap_; |
| |
| DISALLOW_COPY_AND_ASSIGN(SkBitmapRep); |
| }; |
| |
| #if defined(OS_LINUX) |
| class GdkPixbufRep : public ImageRep { |
| public: |
| explicit GdkPixbufRep(GdkPixbuf* pixbuf) |
| : ImageRep(Image::kGdkPixbufRep), |
| pixbuf_(pixbuf) { |
| CHECK(pixbuf); |
| } |
| |
| virtual ~GdkPixbufRep() { |
| if (pixbuf_) { |
| g_object_unref(pixbuf_); |
| pixbuf_ = NULL; |
| } |
| } |
| |
| GdkPixbuf* pixbuf() const { return pixbuf_; } |
| |
| private: |
| GdkPixbuf* pixbuf_; |
| |
| DISALLOW_COPY_AND_ASSIGN(GdkPixbufRep); |
| }; |
| #endif |
| |
| #if defined(OS_MACOSX) |
| class NSImageRep : public ImageRep { |
| public: |
| explicit NSImageRep(NSImage* image) |
| : ImageRep(Image::kNSImageRep), |
| image_(image) { |
| CHECK(image); |
| } |
| |
| virtual ~NSImageRep() { |
| base::mac::NSObjectRelease(image_); |
| image_ = nil; |
| } |
| |
| NSImage* image() const { return image_; } |
| |
| private: |
| NSImage* image_; |
| |
| DISALLOW_COPY_AND_ASSIGN(NSImageRep); |
| }; |
| #endif |
| |
| // The Storage class acts similarly to the pixels in a SkBitmap: the Image |
| // class holds a refptr instance of Storage, which in turn holds all the |
| // ImageReps. This way, the Image can be cheaply copied. |
| class ImageStorage : public base::RefCounted<ImageStorage> { |
| public: |
| ImageStorage(gfx::Image::RepresentationType default_type) |
| : default_representation_type_(default_type) { |
| } |
| |
| gfx::Image::RepresentationType default_representation_type() { |
| return default_representation_type_; |
| } |
| gfx::Image::RepresentationMap& representations() { return representations_; } |
| |
| private: |
| ~ImageStorage() { |
| for (gfx::Image::RepresentationMap::iterator it = representations_.begin(); |
| it != representations_.end(); |
| ++it) { |
| delete it->second; |
| } |
| representations_.clear(); |
| } |
| |
| // The type of image that was passed to the constructor. This key will always |
| // exist in the |representations_| map. |
| gfx::Image::RepresentationType default_representation_type_; |
| |
| // All the representations of an Image. Size will always be at least one, with |
| // more for any converted representations. |
| gfx::Image::RepresentationMap representations_; |
| |
| friend class base::RefCounted<ImageStorage>; |
| }; |
| |
| } // namespace internal |
| |
| Image::Image(const SkBitmap* bitmap) |
| : storage_(new internal::ImageStorage(Image::kSkBitmapRep)) { |
| internal::SkBitmapRep* rep = new internal::SkBitmapRep(bitmap); |
| AddRepresentation(rep); |
| } |
| |
| #if defined(OS_LINUX) |
| Image::Image(GdkPixbuf* pixbuf) |
| : storage_(new internal::ImageStorage(Image::kGdkPixbufRep)) { |
| internal::GdkPixbufRep* rep = new internal::GdkPixbufRep(pixbuf); |
| AddRepresentation(rep); |
| } |
| #endif |
| |
| #if defined(OS_MACOSX) |
| Image::Image(NSImage* image) |
| : storage_(new internal::ImageStorage(Image::kNSImageRep)) { |
| internal::NSImageRep* rep = new internal::NSImageRep(image); |
| AddRepresentation(rep); |
| } |
| #endif |
| |
| Image::Image(const Image& other) : storage_(other.storage_) { |
| } |
| |
| Image& Image::operator=(const Image& other) { |
| storage_ = other.storage_; |
| return *this; |
| } |
| |
| Image::~Image() { |
| } |
| |
| Image::operator const SkBitmap*() { |
| internal::ImageRep* rep = GetRepresentation(Image::kSkBitmapRep); |
| return rep->AsSkBitmapRep()->bitmap(); |
| } |
| |
| Image::operator const SkBitmap&() { |
| return *(this->operator const SkBitmap*()); |
| } |
| |
| #if defined(OS_LINUX) |
| Image::operator GdkPixbuf*() { |
| internal::ImageRep* rep = GetRepresentation(Image::kGdkPixbufRep); |
| return rep->AsGdkPixbufRep()->pixbuf(); |
| } |
| #endif |
| |
| #if defined(OS_MACOSX) |
| Image::operator NSImage*() { |
| internal::ImageRep* rep = GetRepresentation(Image::kNSImageRep); |
| return rep->AsNSImageRep()->image(); |
| } |
| #endif |
| |
| bool Image::HasRepresentation(RepresentationType type) { |
| return storage_->representations().count(type) != 0; |
| } |
| |
| size_t Image::RepresentationCount() { |
| return storage_->representations().size(); |
| } |
| |
| void Image::SwapRepresentations(gfx::Image* other) { |
| storage_.swap(other->storage_); |
| } |
| |
| internal::ImageRep* Image::DefaultRepresentation() { |
| RepresentationMap& representations = storage_->representations(); |
| RepresentationMap::iterator it = |
| representations.find(storage_->default_representation_type()); |
| DCHECK(it != representations.end()); |
| return it->second; |
| } |
| |
| internal::ImageRep* Image::GetRepresentation(RepresentationType rep_type) { |
| // If the requested rep is the default, return it. |
| internal::ImageRep* default_rep = DefaultRepresentation(); |
| if (rep_type == storage_->default_representation_type()) |
| return default_rep; |
| |
| // Check to see if the representation already exists. |
| RepresentationMap::iterator it = storage_->representations().find(rep_type); |
| if (it != storage_->representations().end()) |
| return it->second; |
| |
| // At this point, the requested rep does not exist, so it must be converted |
| // from the default rep. |
| |
| // Handle native-to-Skia conversion. |
| if (rep_type == Image::kSkBitmapRep) { |
| internal::SkBitmapRep* rep = NULL; |
| #if defined(OS_LINUX) |
| if (storage_->default_representation_type() == Image::kGdkPixbufRep) { |
| internal::GdkPixbufRep* pixbuf_rep = default_rep->AsGdkPixbufRep(); |
| rep = new internal::SkBitmapRep( |
| internal::GdkPixbufToSkBitmap(pixbuf_rep->pixbuf())); |
| } |
| #elif defined(OS_MACOSX) |
| if (storage_->default_representation_type() == Image::kNSImageRep) { |
| internal::NSImageRep* nsimage_rep = default_rep->AsNSImageRep(); |
| rep = new internal::SkBitmapRep( |
| internal::NSImageToSkBitmap(nsimage_rep->image())); |
| } |
| #endif |
| CHECK(rep); |
| AddRepresentation(rep); |
| return rep; |
| } |
| |
| // Handle Skia-to-native conversions. |
| if (default_rep->type() == Image::kSkBitmapRep) { |
| internal::SkBitmapRep* skia_rep = default_rep->AsSkBitmapRep(); |
| internal::ImageRep* native_rep = NULL; |
| #if defined(OS_LINUX) |
| if (rep_type == Image::kGdkPixbufRep) { |
| GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(skia_rep->bitmap()); |
| native_rep = new internal::GdkPixbufRep(pixbuf); |
| } |
| #elif defined(OS_MACOSX) |
| if (rep_type == Image::kNSImageRep) { |
| NSImage* image = gfx::SkBitmapToNSImage(*(skia_rep->bitmap())); |
| base::mac::NSObjectRetain(image); |
| native_rep = new internal::NSImageRep(image); |
| } |
| #endif |
| CHECK(native_rep); |
| AddRepresentation(native_rep); |
| return native_rep; |
| } |
| |
| // Something went seriously wrong... |
| return NULL; |
| } |
| |
| void Image::AddRepresentation(internal::ImageRep* rep) { |
| storage_->representations().insert(std::make_pair(rep->type(), rep)); |
| } |
| |
| } // namespace gfx |