[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 1 | // Copyright (c) 2011 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.h" |
| 6 | |
[email protected] | 66171ab | 2011-03-03 15:50:07 | [diff] [blame] | 7 | #include <algorithm> |
| 8 | |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 9 | #include "base/logging.h" |
[email protected] | 7b9cf72 | 2011-04-27 20:50:52 | [diff] [blame] | 10 | #include "base/stl_util-inl.h" |
[email protected] | 6f01e95 | 2011-02-22 17:59:47 | [diff] [blame] | 11 | #include "third_party/skia/include/core/SkBitmap.h" |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 12 | |
| 13 | #if defined(OS_LINUX) |
| 14 | #include <gdk-pixbuf/gdk-pixbuf.h> |
| 15 | #include <glib-object.h> |
| 16 | #include "ui/gfx/canvas_skia.h" |
| 17 | #include "ui/gfx/gtk_util.h" |
| 18 | #elif defined(OS_MACOSX) |
| 19 | #include "base/mac/mac_util.h" |
| 20 | #include "skia/ext/skia_utils_mac.h" |
| 21 | #endif |
| 22 | |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 23 | namespace gfx { |
| 24 | |
| 25 | namespace internal { |
| 26 | |
| 27 | #if defined(OS_MACOSX) |
| 28 | // This is a wrapper around gfx::NSImageToSkBitmap() because this cross-platform |
| 29 | // file cannot include the [square brackets] of ObjC. |
[email protected] | 7b9cf72 | 2011-04-27 20:50:52 | [diff] [blame] | 30 | bool NSImageToSkBitmaps(NSImage* image, std::vector<const SkBitmap*>* bitmaps); |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 31 | #endif |
| 32 | |
| 33 | #if defined(OS_LINUX) |
| 34 | const SkBitmap* GdkPixbufToSkBitmap(GdkPixbuf* pixbuf) { |
[email protected] | 06ec9a2 | 2011-02-17 17:24:07 | [diff] [blame] | 35 | gfx::CanvasSkia canvas(gdk_pixbuf_get_width(pixbuf), |
| 36 | gdk_pixbuf_get_height(pixbuf), |
[email protected] | 2a4f026f | 2011-03-30 14:44:31 | [diff] [blame] | 37 | /*is_opaque=*/false); |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 38 | canvas.DrawGdkPixbuf(pixbuf, 0, 0); |
| 39 | return new SkBitmap(canvas.ExtractBitmap()); |
| 40 | } |
| 41 | #endif |
| 42 | |
| 43 | class SkBitmapRep; |
| 44 | class GdkPixbufRep; |
| 45 | class NSImageRep; |
| 46 | |
| 47 | // An ImageRep is the object that holds the backing memory for an Image. Each |
| 48 | // RepresentationType has an ImageRep subclass that is responsible for freeing |
| 49 | // the memory that the ImageRep holds. When an ImageRep is created, it expects |
| 50 | // to take ownership of the image, without having to retain it or increase its |
| 51 | // reference count. |
| 52 | class ImageRep { |
| 53 | public: |
| 54 | explicit ImageRep(Image::RepresentationType rep) : type_(rep) {} |
| 55 | |
| 56 | // Deletes the associated pixels of an ImageRep. |
| 57 | virtual ~ImageRep() {} |
| 58 | |
| 59 | // Cast helpers ("fake RTTI"). |
| 60 | SkBitmapRep* AsSkBitmapRep() { |
| 61 | CHECK_EQ(type_, Image::kSkBitmapRep); |
| 62 | return reinterpret_cast<SkBitmapRep*>(this); |
| 63 | } |
| 64 | |
| 65 | #if defined(OS_LINUX) |
| 66 | GdkPixbufRep* AsGdkPixbufRep() { |
| 67 | CHECK_EQ(type_, Image::kGdkPixbufRep); |
| 68 | return reinterpret_cast<GdkPixbufRep*>(this); |
| 69 | } |
| 70 | #endif |
| 71 | |
| 72 | #if defined(OS_MACOSX) |
| 73 | NSImageRep* AsNSImageRep() { |
| 74 | CHECK_EQ(type_, Image::kNSImageRep); |
| 75 | return reinterpret_cast<NSImageRep*>(this); |
| 76 | } |
| 77 | #endif |
| 78 | |
| 79 | Image::RepresentationType type() const { return type_; } |
| 80 | |
| 81 | private: |
| 82 | Image::RepresentationType type_; |
| 83 | }; |
| 84 | |
| 85 | class SkBitmapRep : public ImageRep { |
| 86 | public: |
| 87 | explicit SkBitmapRep(const SkBitmap* bitmap) |
[email protected] | 7b9cf72 | 2011-04-27 20:50:52 | [diff] [blame] | 88 | : ImageRep(Image::kSkBitmapRep) { |
[email protected] | 954ec2e6 | 2011-04-25 13:10:33 | [diff] [blame] | 89 | CHECK(bitmap); |
[email protected] | 7b9cf72 | 2011-04-27 20:50:52 | [diff] [blame] | 90 | bitmaps_.push_back(bitmap); |
| 91 | } |
| 92 | |
| 93 | explicit SkBitmapRep(const std::vector<const SkBitmap*>& bitmaps) |
| 94 | : ImageRep(Image::kSkBitmapRep), |
| 95 | bitmaps_(bitmaps) { |
| 96 | CHECK(!bitmaps_.empty()); |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 97 | } |
| 98 | |
| 99 | virtual ~SkBitmapRep() { |
[email protected] | 7b9cf72 | 2011-04-27 20:50:52 | [diff] [blame] | 100 | STLDeleteElements(&bitmaps_); |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 101 | } |
| 102 | |
[email protected] | 7b9cf72 | 2011-04-27 20:50:52 | [diff] [blame] | 103 | const SkBitmap* bitmap() const { return bitmaps_[0]; } |
| 104 | |
| 105 | const std::vector<const SkBitmap*>& bitmaps() const { return bitmaps_; } |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 106 | |
| 107 | private: |
[email protected] | 7b9cf72 | 2011-04-27 20:50:52 | [diff] [blame] | 108 | std::vector<const SkBitmap*> bitmaps_; |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 109 | |
| 110 | DISALLOW_COPY_AND_ASSIGN(SkBitmapRep); |
| 111 | }; |
| 112 | |
| 113 | #if defined(OS_LINUX) |
| 114 | class GdkPixbufRep : public ImageRep { |
| 115 | public: |
| 116 | explicit GdkPixbufRep(GdkPixbuf* pixbuf) |
| 117 | : ImageRep(Image::kGdkPixbufRep), |
| 118 | pixbuf_(pixbuf) { |
| 119 | CHECK(pixbuf); |
| 120 | } |
| 121 | |
| 122 | virtual ~GdkPixbufRep() { |
| 123 | if (pixbuf_) { |
| 124 | g_object_unref(pixbuf_); |
| 125 | pixbuf_ = NULL; |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | GdkPixbuf* pixbuf() const { return pixbuf_; } |
| 130 | |
| 131 | private: |
| 132 | GdkPixbuf* pixbuf_; |
| 133 | |
| 134 | DISALLOW_COPY_AND_ASSIGN(GdkPixbufRep); |
| 135 | }; |
| 136 | #endif |
| 137 | |
| 138 | #if defined(OS_MACOSX) |
| 139 | class NSImageRep : public ImageRep { |
| 140 | public: |
| 141 | explicit NSImageRep(NSImage* image) |
| 142 | : ImageRep(Image::kNSImageRep), |
| 143 | image_(image) { |
| 144 | CHECK(image); |
| 145 | } |
| 146 | |
| 147 | virtual ~NSImageRep() { |
| 148 | base::mac::NSObjectRelease(image_); |
| 149 | image_ = nil; |
| 150 | } |
| 151 | |
| 152 | NSImage* image() const { return image_; } |
| 153 | |
| 154 | private: |
| 155 | NSImage* image_; |
| 156 | |
| 157 | DISALLOW_COPY_AND_ASSIGN(NSImageRep); |
| 158 | }; |
| 159 | #endif |
| 160 | |
[email protected] | 8779d8b | 2011-04-22 01:08:51 | [diff] [blame] | 161 | // The Storage class acts similarly to the pixels in a SkBitmap: the Image |
| 162 | // class holds a refptr instance of Storage, which in turn holds all the |
| 163 | // ImageReps. This way, the Image can be cheaply copied. |
| 164 | class ImageStorage : public base::RefCounted<ImageStorage> { |
| 165 | public: |
| 166 | ImageStorage(gfx::Image::RepresentationType default_type) |
| 167 | : default_representation_type_(default_type) { |
| 168 | } |
| 169 | |
| 170 | gfx::Image::RepresentationType default_representation_type() { |
| 171 | return default_representation_type_; |
| 172 | } |
| 173 | gfx::Image::RepresentationMap& representations() { return representations_; } |
| 174 | |
| 175 | private: |
| 176 | ~ImageStorage() { |
| 177 | for (gfx::Image::RepresentationMap::iterator it = representations_.begin(); |
| 178 | it != representations_.end(); |
| 179 | ++it) { |
| 180 | delete it->second; |
| 181 | } |
| 182 | representations_.clear(); |
| 183 | } |
| 184 | |
| 185 | // The type of image that was passed to the constructor. This key will always |
| 186 | // exist in the |representations_| map. |
| 187 | gfx::Image::RepresentationType default_representation_type_; |
| 188 | |
| 189 | // All the representations of an Image. Size will always be at least one, with |
| 190 | // more for any converted representations. |
| 191 | gfx::Image::RepresentationMap representations_; |
| 192 | |
| 193 | friend class base::RefCounted<ImageStorage>; |
| 194 | }; |
| 195 | |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 196 | } // namespace internal |
| 197 | |
| 198 | Image::Image(const SkBitmap* bitmap) |
[email protected] | 8779d8b | 2011-04-22 01:08:51 | [diff] [blame] | 199 | : storage_(new internal::ImageStorage(Image::kSkBitmapRep)) { |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 200 | internal::SkBitmapRep* rep = new internal::SkBitmapRep(bitmap); |
| 201 | AddRepresentation(rep); |
| 202 | } |
| 203 | |
[email protected] | 7b9cf72 | 2011-04-27 20:50:52 | [diff] [blame] | 204 | Image::Image(const std::vector<const SkBitmap*>& bitmaps) |
| 205 | : storage_(new internal::ImageStorage(Image::kSkBitmapRep)) { |
| 206 | internal::SkBitmapRep* rep = new internal::SkBitmapRep(bitmaps); |
| 207 | AddRepresentation(rep); |
| 208 | } |
| 209 | |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 210 | #if defined(OS_LINUX) |
| 211 | Image::Image(GdkPixbuf* pixbuf) |
[email protected] | 8779d8b | 2011-04-22 01:08:51 | [diff] [blame] | 212 | : storage_(new internal::ImageStorage(Image::kGdkPixbufRep)) { |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 213 | internal::GdkPixbufRep* rep = new internal::GdkPixbufRep(pixbuf); |
| 214 | AddRepresentation(rep); |
| 215 | } |
| 216 | #endif |
| 217 | |
| 218 | #if defined(OS_MACOSX) |
[email protected] | 8779d8b | 2011-04-22 01:08:51 | [diff] [blame] | 219 | Image::Image(NSImage* image) |
| 220 | : storage_(new internal::ImageStorage(Image::kNSImageRep)) { |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 221 | internal::NSImageRep* rep = new internal::NSImageRep(image); |
| 222 | AddRepresentation(rep); |
| 223 | } |
| 224 | #endif |
| 225 | |
[email protected] | 8779d8b | 2011-04-22 01:08:51 | [diff] [blame] | 226 | Image::Image(const Image& other) : storage_(other.storage_) { |
| 227 | } |
| 228 | |
| 229 | Image& Image::operator=(const Image& other) { |
| 230 | storage_ = other.storage_; |
| 231 | return *this; |
| 232 | } |
| 233 | |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 234 | Image::~Image() { |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 235 | } |
| 236 | |
[email protected] | 15a2084 | 2011-05-04 01:29:28 | [diff] [blame] | 237 | Image::operator const SkBitmap*() const { |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 238 | internal::ImageRep* rep = GetRepresentation(Image::kSkBitmapRep); |
| 239 | return rep->AsSkBitmapRep()->bitmap(); |
| 240 | } |
| 241 | |
[email protected] | 15a2084 | 2011-05-04 01:29:28 | [diff] [blame] | 242 | Image::operator const SkBitmap&() const { |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 243 | return *(this->operator const SkBitmap*()); |
| 244 | } |
| 245 | |
| 246 | #if defined(OS_LINUX) |
[email protected] | 15a2084 | 2011-05-04 01:29:28 | [diff] [blame] | 247 | Image::operator GdkPixbuf*() const { |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 248 | internal::ImageRep* rep = GetRepresentation(Image::kGdkPixbufRep); |
| 249 | return rep->AsGdkPixbufRep()->pixbuf(); |
| 250 | } |
| 251 | #endif |
| 252 | |
| 253 | #if defined(OS_MACOSX) |
[email protected] | 15a2084 | 2011-05-04 01:29:28 | [diff] [blame] | 254 | Image::operator NSImage*() const { |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 255 | internal::ImageRep* rep = GetRepresentation(Image::kNSImageRep); |
| 256 | return rep->AsNSImageRep()->image(); |
| 257 | } |
| 258 | #endif |
| 259 | |
[email protected] | 15a2084 | 2011-05-04 01:29:28 | [diff] [blame] | 260 | bool Image::HasRepresentation(RepresentationType type) const { |
[email protected] | 8779d8b | 2011-04-22 01:08:51 | [diff] [blame] | 261 | return storage_->representations().count(type) != 0; |
| 262 | } |
| 263 | |
[email protected] | 15a2084 | 2011-05-04 01:29:28 | [diff] [blame] | 264 | size_t Image::RepresentationCount() const { |
[email protected] | 8779d8b | 2011-04-22 01:08:51 | [diff] [blame] | 265 | return storage_->representations().size(); |
[email protected] | c41b06f | 2011-02-24 15:53:26 | [diff] [blame] | 266 | } |
| 267 | |
[email protected] | 66171ab | 2011-03-03 15:50:07 | [diff] [blame] | 268 | void Image::SwapRepresentations(gfx::Image* other) { |
[email protected] | 8779d8b | 2011-04-22 01:08:51 | [diff] [blame] | 269 | storage_.swap(other->storage_); |
[email protected] | 66171ab | 2011-03-03 15:50:07 | [diff] [blame] | 270 | } |
| 271 | |
[email protected] | 15a2084 | 2011-05-04 01:29:28 | [diff] [blame] | 272 | internal::ImageRep* Image::DefaultRepresentation() const { |
[email protected] | 8779d8b | 2011-04-22 01:08:51 | [diff] [blame] | 273 | RepresentationMap& representations = storage_->representations(); |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 274 | RepresentationMap::iterator it = |
[email protected] | 8779d8b | 2011-04-22 01:08:51 | [diff] [blame] | 275 | representations.find(storage_->default_representation_type()); |
| 276 | DCHECK(it != representations.end()); |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 277 | return it->second; |
| 278 | } |
| 279 | |
[email protected] | 15a2084 | 2011-05-04 01:29:28 | [diff] [blame] | 280 | internal::ImageRep* Image::GetRepresentation( |
| 281 | RepresentationType rep_type) const { |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 282 | // If the requested rep is the default, return it. |
| 283 | internal::ImageRep* default_rep = DefaultRepresentation(); |
[email protected] | 8779d8b | 2011-04-22 01:08:51 | [diff] [blame] | 284 | if (rep_type == storage_->default_representation_type()) |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 285 | return default_rep; |
| 286 | |
| 287 | // Check to see if the representation already exists. |
[email protected] | 8779d8b | 2011-04-22 01:08:51 | [diff] [blame] | 288 | RepresentationMap::iterator it = storage_->representations().find(rep_type); |
| 289 | if (it != storage_->representations().end()) |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 290 | return it->second; |
| 291 | |
| 292 | // At this point, the requested rep does not exist, so it must be converted |
| 293 | // from the default rep. |
| 294 | |
| 295 | // Handle native-to-Skia conversion. |
| 296 | if (rep_type == Image::kSkBitmapRep) { |
| 297 | internal::SkBitmapRep* rep = NULL; |
| 298 | #if defined(OS_LINUX) |
[email protected] | 8779d8b | 2011-04-22 01:08:51 | [diff] [blame] | 299 | if (storage_->default_representation_type() == Image::kGdkPixbufRep) { |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 300 | internal::GdkPixbufRep* pixbuf_rep = default_rep->AsGdkPixbufRep(); |
| 301 | rep = new internal::SkBitmapRep( |
| 302 | internal::GdkPixbufToSkBitmap(pixbuf_rep->pixbuf())); |
| 303 | } |
| 304 | #elif defined(OS_MACOSX) |
[email protected] | 8779d8b | 2011-04-22 01:08:51 | [diff] [blame] | 305 | if (storage_->default_representation_type() == Image::kNSImageRep) { |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 306 | internal::NSImageRep* nsimage_rep = default_rep->AsNSImageRep(); |
[email protected] | 7b9cf72 | 2011-04-27 20:50:52 | [diff] [blame] | 307 | std::vector<const SkBitmap*> bitmaps; |
| 308 | CHECK(internal::NSImageToSkBitmaps(nsimage_rep->image(), &bitmaps)); |
| 309 | rep = new internal::SkBitmapRep(bitmaps); |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 310 | } |
| 311 | #endif |
[email protected] | 2a4f026f | 2011-03-30 14:44:31 | [diff] [blame] | 312 | CHECK(rep); |
| 313 | AddRepresentation(rep); |
| 314 | return rep; |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 315 | } |
| 316 | |
| 317 | // Handle Skia-to-native conversions. |
| 318 | if (default_rep->type() == Image::kSkBitmapRep) { |
| 319 | internal::SkBitmapRep* skia_rep = default_rep->AsSkBitmapRep(); |
| 320 | internal::ImageRep* native_rep = NULL; |
| 321 | #if defined(OS_LINUX) |
| 322 | if (rep_type == Image::kGdkPixbufRep) { |
[email protected] | 06ec9a2 | 2011-02-17 17:24:07 | [diff] [blame] | 323 | GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(skia_rep->bitmap()); |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 324 | native_rep = new internal::GdkPixbufRep(pixbuf); |
| 325 | } |
| 326 | #elif defined(OS_MACOSX) |
| 327 | if (rep_type == Image::kNSImageRep) { |
[email protected] | 7b9cf72 | 2011-04-27 20:50:52 | [diff] [blame] | 328 | NSImage* image = gfx::SkBitmapsToNSImage(skia_rep->bitmaps()); |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 329 | base::mac::NSObjectRetain(image); |
| 330 | native_rep = new internal::NSImageRep(image); |
| 331 | } |
| 332 | #endif |
[email protected] | 2a4f026f | 2011-03-30 14:44:31 | [diff] [blame] | 333 | CHECK(native_rep); |
| 334 | AddRepresentation(native_rep); |
| 335 | return native_rep; |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 336 | } |
| 337 | |
| 338 | // Something went seriously wrong... |
| 339 | return NULL; |
| 340 | } |
| 341 | |
[email protected] | 15a2084 | 2011-05-04 01:29:28 | [diff] [blame] | 342 | void Image::AddRepresentation(internal::ImageRep* rep) const { |
[email protected] | 8779d8b | 2011-04-22 01:08:51 | [diff] [blame] | 343 | storage_->representations().insert(std::make_pair(rep->type(), rep)); |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 344 | } |
| 345 | |
[email protected] | 15a2084 | 2011-05-04 01:29:28 | [diff] [blame] | 346 | size_t Image::GetNumberOfSkBitmaps() const { |
[email protected] | 7b9cf72 | 2011-04-27 20:50:52 | [diff] [blame] | 347 | return GetRepresentation(Image::kSkBitmapRep)->AsSkBitmapRep()-> |
| 348 | bitmaps().size(); |
| 349 | } |
| 350 | |
[email protected] | 15a2084 | 2011-05-04 01:29:28 | [diff] [blame] | 351 | const SkBitmap* Image::GetSkBitmapAtIndex(size_t index) const { |
[email protected] | 7b9cf72 | 2011-04-27 20:50:52 | [diff] [blame] | 352 | return GetRepresentation(Image::kSkBitmapRep)->AsSkBitmapRep()-> |
| 353 | bitmaps()[index]; |
| 354 | } |
| 355 | |
[email protected] | 40a080e | 2011-02-11 17:42:28 | [diff] [blame] | 356 | } // namespace gfx |